Welcome to MSDN Blogs Sign in | Join | Help

Clearing some confusion about Dynamic Data and VS SP1 Beta

Today, we released Visual Studio 2008 and .NET FX 3.5 SP1 Beta, which include the new ASP.NET Dynamic Data.  The part that gets a little bit confusing is that the Dynamic Data bits that come with the brand new Beta are actually older than Preview bits that we've had out for a while on Code Gallery!  The reason for this is that getting a VS Beta out is a pretty big deal, and we need to start locking down the bits long before the beta goes out.  But while we stopped changing the beta bits a while back, we continued to work full speed on what will eventually become the RTM bits.  The great thing is that we have been able to put frequent updates up on Code Gallery, which has allowed us to get some super valuable user feedback.

I should also mentioned that we put up a refresh of the Dynamic Data bits on code gallery this morning, at the same time that the Beta was released.  And unlike the previews drops that were available earlier on Code Gallery, this new drop requires that you first install the VS SP1 Beta.  In fact, this new Preview drop is extremely similar to what the VS SP1 RTM bits will be, as this project cycle is coming to completion.

I know, this all sounds a bit confusing.  So what does this all mean to you?  Basically, it means that if you want to use Dynamic Data today, the best thing you can do is:

Although the Dynamic Data bits that come with the beta are usable, they are essentially outdated and we would much prefer to have everyone use the very latest.

Posted by davidebb | 2 Comments

New Dynamic Data Preview available

I'm happy to announce that we have made a new public drop of ASP.NET Dynamic Data available.  The last public drop was made in December and was starting to get a little outdated.  More recently we had made some drops available via Microsoft Connect, but it was a non-public preview which required an approval process.

This new drop is openly available at all on MSDN Code Gallery.  One of the nice things about this is that we'll be able to give much more frequent updates, instead of the long delays that often happen between betas.

Another cool thing about this new Preview is that it includes the first public drop of the Dynamic Data Website Wizard, which gives a richer design time experience to Dynamic Data sites.  Though the wizard is still in a pretty early and incomplete state, we're hoping it will give you an idea of where we are going with this.

You can look at my previous post for an overview of the changes we made since the December drop.  Note that even though the drop has the ability to support Entity Framework (in addition to Linq To Sql), you will not quite be able to use it yet because it targets an Entity Framework build that is newer than the latest public build.  But don't worry, it won't be long before the missing pieces that will allow this are made available.

We are eagerly awaiting your feedback on this new drop.  As before, the best place to discuss Dynamic Data and report issues is our ASP.NET forum.

Thanks!

Posted by davidebb | 1 Comments

Dynamic Data at MIX, and upcoming changes

Yesterday, I gave a talk on ASP.NET Dynamic Data at the MIX 2008 conference.  It was a full room, and it was great to see the interest for the technology.  What's nice about MIX is that they are making all talks available online within 24 hours.  So you can watch my talk today!  Here is the direct link.

Note that the build of Dynamic Data that I use in the session is quite a bit newer than the one we made available in December.  You should expect the new build to be available within a few weeks.

While a lot of the core concepts remain the same, there are a number of important changes, and I'll describe some of them here:

 

Entity Framework support

In addition the Linq To Sql, Dynamic Data now supports Entity Framework.  For the most part, everything works the same way.  When you get started, you create an Entity Framework model instead of a Linq To Sql model.  You'll also notice that pages use an EntityDataSource instead of a LinqDataSource.  This is a new data source control from the Entity Framework team, and it will be usable both in Dynamic Data apps and in 'regular' ASP.NET pages.

 

Folder structure changes

In the December CTP, we were using an App_Shared folder, and that name really didn't make much sense.  The new folder structure uses name that better describe the semantic.  Here is what it looks like

  • App root
    • global.asax, web.config, site.master, ...
    • DynamicData
        • PageTemplates
          • list.aspx, details.aspx, edit.aspx, insert.aspx
        • CustomPages
          • SomeTable
            • list.aspx, details.aspx, edit.aspx, insert.aspx
          • SomeOtherTable
            • list.aspx, details.aspx, edit.aspx, insert.aspx
        • FieldTemplates
          • boolean.ascx, integer.ascx, etc...

 

Use of ASP.NET Routing to configure URLs

In the CTP, Dynamic Data was using a fairly complex web.config section to customize its URLs.  Unfortunately, it was a bit hard to use, and not as customizable as it needed to be.  Instead, we are now using the new ASP.NET Routing feature.  See this post from Scott Guthrie for an  introduction to the feature.  Also, check out my MIX Session video for examples of how this works in Dynamic Data (around 42 minutes into it).

In a nutshell, routing allows you to decouple your external URLs from the internal structure of your web files.  That means that you can change the URLs that your users see in the web browser without having to move your files around accordingly.  It's a bit of a mind shift when you're used to URLs mapping directly to a server file, but it's a pretty powerful concept once you get used to it.

 

Here is an example of what a custom route may look like in a Dynamic Data app.  This code typically lives in your Application_Start (in global.asax).

RouteTable.Routes.Add(new DynamicDataRoute("Hello/{action}/DynamicData/{table}") {
    Constraints = new RouteValueDictionary(new { action = "Edit|Insert" }),
    Model = model
});

 

Direct support for GridView, DetailsView and other data controls

In the December CTP, you had to use new versions of those controls, like DynamicGridView.  To avoid the proliferation of new controls, we now support directly using the existing data controls in Dynamic Data web sites.

 

Changes to the way metadata attributes are specified on the model

We have changed the way you specify metadata on your model properties.  Previously, everything was done at the class level.  Now, we use a 'buddy' class that can hold the metadata at the property level.  Here is an example:

[MetadataType(typeof(Product_MD))]
public partial class Product {
    partial void OnProductNameChanging(string value) {
        if (value[0] == 'a')
            throw new Exception("Must not start with a");
    }
}

public class Product_MD {
    [DisplayName("My units")]
    [Description("This number shows how many unites are left")]
    [Range(0, 150, ErrorMessage = "Incorrect range for the units in stock")]
    [UIHint("IntegerSlider")]
    public object UnitsInStock { get; set; }

    [UIHint("BooleanAjax")]
    public object Discontinued { get; set; }
}

If you're wondering why we need an extra class, the reason is that there is no way in C# (and VB) to add attributes to properties defined on the other side of the partial class (which is the generated model file).  Still, this is an improvement over having all the attributes for all the properties specified at class level.  We're hoping that future language improvement will allow us to remove the need for the extra class.

Note that the metadata model is extensible via standard TypeDescriptionProviders.  The 'buddy' class mechanism shown above is the default provider that comes with Dynamic Data, but you can write new providers that get their data from arbitrary other places: XML file, database, ...

 

Model registration and support for multiple models

In the CTP, the model was registered in web.config, and only a single model was supported.  Now, the registration happens in your Application_Start (in global.asax), next to where you have your routes.  Here is what a typical registration looks like:

MetaModel model = new MetaModel();
model.RegisterContext(typeof(NWDataContext), new ContextConfiguration() { ScaffoldAllTables = true });

You could register multiple models, and would pass the relevant one to each route (see route example above).  Also note that 'ScaffoldAllTables' attribute.  By default it's false.  Making it true turns on scaffolding for that model, mean that the tables are made available via the page templates.  When it's false, only custom pages (living under DynamicData\CustomPages) are supported.

 

New extensibility points for Page Templates and Field Templates

You can  now customize many aspects of how field and page templates work.  Things you can customize include:

  • The location of the folders containing the templates
  • How templates are selected
  • You can completely override the field template creation process, which for example lets use use precompiled controls instead of user controls (ascx files).

 

Many bug fixes and API renames

We fixed a lot of issues, and cleaned up a lot of API names to be more straightforward and discoverable.

Posted by davidebb | 11 Comments

Workaround for update issue in the Dynamic Data preview

We found that  the Dynamic Data preview has a pretty nasty caching bug that prevents updates from showing up in some foreign key scenarios.  Marcin has posted a fix on his blog, so if you run into this, please check it out.

Posted by davidebb | 1 Comments

Answers to frequently asked ASP.NET Dynamic Data questions

Now that ScottGu blogged about it, we have received a number of great feedback and questions.  I'll try to answer some them here.

 

Will this work with MVC?

In this initial Preview, Dynamic Data is mostly targeting 'standard' ASP.NET pages, but we are absolutely planning to support Dynamic Data for MVC.  And the good news is that you can actually try this today!  To do this:

This was completely unadvertised, so most folks probably didn't see it, but it's in there for you to play with!  Start by just running it, and then look through the source code, which is all part of the solution.  Of course, it's all very preliminary, and far from feature complete, but it should give you an idea of where we're heading.  If there is enough interest, I'll try to make a screen cast that shows it.

 

Will it support databases other than SQL Server?

Yes, this is a key requirement for us.  In the Preview build, Dynamic Data runs on top of Linq To Sql, which only supports SQL Server.  But in the near future, it will also support Linq To Entities, which supports other databases like Oracle.

 

Will it support Many to Many relationships?

Yes, this will be fully supported with Linq To Entities.  Linq To Sql does not directly support Many to Many relationships, though it can be achieved with some extra steps (see for example this post).  But with Entities, it will work with much less effort.

 

Is this feature for prototyping,  or for real applications?

The answer is that it really is for both.  By using the initial scaffolding, you instantly get a working app.  For some admin scenarios, it may actually be good enough as is, but for most other scenarios that need to be more polished, you can deeply customize it, while preserving many of the benefits.

The customization of a Dynamic Data app happens at many level, which I briefly described my my previous post.  Again, I'll try to go deeper into the various scenarios in future posts.

 

Can I use it with an existing app?

If you have an  existing ASP.NET application that already uses Linq To Sql, then you should be able to add some Dynamic Data goodness in there, simply by using some of the new controls in your pages.  e.g. A DynamicControl contains the logic that drives the rendering of a field via the Field Template mechanism.

There are some aspects that are a little trickier, like getting your existing pages to automatically link to each other (e.g. for traveling foreign  key relationships).  That's not as easy as it could be today, but we are working on improving this support.

 

Can I use multiple Data Contexts in my application?

With the Preview bits, you can only use one, but future builds will remove this restriction.  You will be able to register multiple contexts with the Dynamic Data engine, and it will work with all of them.

 

Why is Dynamic Data using a sort of routing section in web.config, while the new MVC does routing in global.asax?

Well, we basically didn't have time to make those two  things come together in the Preview, but future builds will  make Dynamic Data use the exact same routing mechanism as MVC.  To be clear that means both the MVC and the non-MVC (aka WebForms) flavors of Dynamic Data will rely on this routing mechanism.  As a result, most of what we have in web.config today will disappear.

 

I have seen some mentions of 'Oryx'.  How does it relate to Dynamic Data?

'Oryx' is the internal code name for Dynamic Data, although the name has been used a few times publicly.  In any case they refer to exactly the same thing.  As for the name 'Dynamic Data', we may or may not stick with it as the official name when this is released.  If you have a great naming suggestion, send it my way! :-)

Posted by davidebb | 10 Comments

Dynamic Data screencast is now available!

I made a screencast which walks through a basic scenario of using ASP.NET Dynamic Data in a simple site using Northwind.  It's about 17 minutes long.  Enjoy and feel free to give any feedback here or in the forum.

Posted by davidebb | 55 Comments

Attachment(s): AjaxFieldTemplates.zip

Introducing the new Dynamic Data

Today, we released the ASP.NET 3.5 Extensions Preview, which includes the exciting Dynamic Data feature.

A lot has happened since we released an early version of Dynamic Data Controls in the May ASP.NET Futures release.  While it was already an exciting feature, it was not very customizable and was limited to a small set of scenarios.

While this new version preserves the essence of the original, it is also completely different.  Not only it addresses the limited customization, but it also brings a lot of new things.

Since most of you are likely new to Dynamic Data, let me start with a quick introduction of what it is about.  The core purpose is to make it easy to write powerful and extensible data driven web application.  Here are some of the steps involved (not all of them are necessary):

  1. Define your model: this step is not specific to Dynamic Data, but this is where it all starts.  You have a database, and you create a model for it.  Currently, this is done using Linq to SQL.  In the future Linq to Entities will be supported as well.
  2. Test a scaffolded version of your app: with no additional effort, Dynamic Data lets you run a scaffolded web application on top of your schema.  Although this scaffold has a 'standard' UI, it lets you try all CRUD operations on your tables.  Furthermore, it has full support for relationships.  For instance (assuming Northwind), the Products page would let you navigate to a product's category info, and editing a Product's category would show you a rich drop down with category names (instead of IDs).
  3. Customize the shared page views: you then get to make changes to the page templates that are used to display the scaffolded views.  That, you can make changes that will affect the view of all the tables.
  4. Customize the specific pages: the next step is to define some custom UI for specific pages.  e.g. you could write one of Products and another for Categories.  The nice thing is that while you do this, you still get the standard scaffolded view for all the other tables.  This lets you create specfic pages at your own pace, while keeping a functional application at every point.
  5. Annotate your model: you can add attributes to your model to add extra knowledge about certain fields.  e.g. you could set a Range on the product's UnitsInStock field so it only allows values between 0 and 50.  The UI would then automatically pick this up and validate inputs accordingly.  The great thing is that you are keeping this information on your model, and don't need to 'pollute' your pages with it.
  6. Create custom field templates: all fields in Dynamic Data are rendered via field templates, which are basically user controls.  You can also create custom templates, which can be very powerful.  e.g. in the above case, you could create a field template that renders a range as a slider control instead of a textbox.  Again, your page would pick that up without you having to add this custom UI directly in the aspx file.

Well, this should be enough for a quick introduction.  I'll try to have a screencast available tomorrow, which will run through some of the basic scanerios.  I'll then follow up with more specific posts and screencasts that go deeper into specific areas.

To ask questions or report issues about this feature, please use this ASP.NET forum.

Posted by davidebb | 10 Comments
Filed under: ,

Dynamic Data Controls - the Present and the Future

As part of the May 2007 ASP.NET Futures, we released an alpha version of the Dynamic Data Controls.  You can watch my screencast to get a quick introduction to it.  The general goal is to make it really easy to create data driven web sites in ASP.NET.

 

While the current alpha drop is fun to play with and gives an idea of the ‘flavor’ of the feature, it is not by any mean complete.  Several of you have posted questions on the forums, mostly asking for ways to customize the behavior in various ways.  In a lot of cases, the level of customization that you are asking for just isn’t there yet.

 

However, there will be more releases, and they will solve a lot of those issues.  One other major change that will happen is that Dynamic Data Controls will become based in LINQ, while currently they are not (they use the ASP.NET SqlDataSource control).  With LINQ, you will be able to customize the query used by the dynamic framework using powerful LINQ syntax.

 

In the meantime, I apologize that the feature set is still quite limited.  We do very much appreciate all feedback that you have for us on this exciting new feature.

 

Thanks!

David

Posted by davidebb | 6 Comments
Filed under:

Announcing IronPython for ASP.NET

We recently added support for using the IronPython language in ASP.NET.  Please check it out here.  We also set up a new ASP.NET forum to discuss this, so if you have any comments/suggestions/questions, we'd love to hear it!

David

Posted by davidebb | 0 Comments
Filed under: ,

Overriding ASP.NET combine behavior using a VirtualPathProvider

This article applies to ASP.NET 2.0.

Background

This article originated from a customer question on the ASP.NET site.  What they are trying to achieve is running multiple sites under a single actual ASP.NET application.  This can be useful to avoid the overhead of having a different appdomain per site.  So the general idea is to have a single application, and to use sub-directories to represent the site.  Let's call them 'pseudo-sites' as they are really just directories from the point of view of ASP.NET.

For example, the app could have this structure:

MyApp
    PseudoSites
        Site1
            page.aspx
            uc.ascx
        Site2
            Site2's files...
 

Such pseudo-sites will necessarily have a number of limitations: e.g. they won't be able to each have their own bin, App_Code, and other top level directories, since these can only exist at the top level of a real ASP.NET application.  In spite of these limitations, the structure can be useful for apps that don't needs to have those directories.

 

The issue we're trying to solve: how to make path resolution work

The main issue that this article deals with is the fact that path resolution will by default not work correctly when using such a structure.  e.g. suppose /MyApp/PseudoSites/Site1/page.aspx has:

<%@ Register Src="~/uc.ascx" TagName="uc" TagPrefix="uc1" %>

Recall that '~' means "the root of the app".  Clearly "~/uc.ascx" means to refer to uc.ascx in the same pseudo-site as page.aspx.  But ASP.NET will not see it that way, as the real root of the app is just "/MyApp".  Instead, this will resolve to "/MyApp/uc.ascx", which is not where the file is.

One obvious solution is to use relative paths instead of app relative paths.  e.g. here you could write src="uc.ascx" mce_src="uc.ascx" and it would work fine.  This is a fine thing to do in some cases, but in many other cases, you are much better off using app relative paths, as you are then free to move files around without having to worry about the relative locations always staying the same.

So the question is: how can we make app relative paths (as well as absolute path, e.g. "/Site1/page.aspx") work correctly in the pseudo-site environment?

 

VirtualPathProvider to the rescue

ASP.NET 2.0 introduces the ability to hook deep into the way it deals with files via something called a VirtualPathProvider.  Implementing a full VirtualPathProvider is somewhat involved, and is usually done to serve files out of an alternate store, like a database.  Doing this is beyond the scope of this article (though I'd like to write more about it if there is interest!), and we will look at only one VirtualPathProvider method: CombineVirtualPaths.  This method is called whenever the parser needs to resolve paths, which is exactly what we need to solve our problem!

The code below shows a sample implementation of CombineVirtualPaths.  You will need to adapt it to your situation but it demonstrates the principle.  To try this code, simply put it somewhere in the App_Code directory (of your real app, not pseudo app!).

Note: AppInitialize is a special method that gets called automatically at startup when it is found somewhere is App_Code.  You could alternatively register the VirtualPathProvider from global.asax (in Application_OnStart) or an HttpModule.

 

using System;
using
System.Web;
using
System.Web.Util;
using
System.Web.Hosting;

public class
SimpleVPP : VirtualPathProvider {
   
public static void AppInitialize() {
        HostingEnvironment.RegisterVirtualPathProvider(
new SimpleVPP());
   
}

   
public override string CombineVirtualPaths(string basePath, string relativePath) {

       
// If the path is relative, let normal processing happen
       
if (!VirtualPathUtility.IsAbsolute(relativePath))
           
return base.CombineVirtualPaths(basePath, relativePath);
        
       
// Determine the pseudo site from the request.  To demonstrate, we just get it from the
        // query string, but it could come from other places, like the http host header
       
string site = HttpContext.Current.Request.QueryString["site"];

       
// If we couldn't, default to normal processing
       
if (site == null)
           
return base.CombineVirtualPaths(basePath, relativePath);

       
// Make it app relative (i.e. ~/...)
       
relativePath = VirtualPathUtility.ToAppRelative(relativePath);

       
// Remap the virtual path to be inside the correct pseudo site
       
return "~/PseudoSites/" + site + relativePath.Substring(1);
   
}
}

That's basically it!  With this code, the situation described above will be able to run, since your code is driving the path resolution.  Basically, you get to give whatever meaning you want to '~'.

A couple more notes about this:

  • The name of the parameter 'relativePath' in CombineVirtualPaths is misleading, since this is actually called for all paths, not just relative.  And of course, if that were not the case, our solution wouldn't work!
  • To test the example above, you would need to request something like http://localhost/MyApp/PseudoSites/Site1/Foo.aspx?site=Site1, because that's what the sample CombineVirtualPaths expects.  In a real world app, you would probably not use that.

Note on using a VirtualPathProvider with a precompiled site

As some of you found, if your site if precompiled, your VPP is not used.  I wish we had made this scenario work, but I guess it fell through due to scheduling.  We basically ended up artificially disabling the scenario because we didn't have time to test it properly.  Someone posted a workaround using private reflection.  It is definitely a hack (and may break in later versions, though that's not likely), and I can't guarantee that it works well in all scenario, but if it can unblock you, give it a try.

Posted by davidebb | 30 Comments

Turning an ascx user control into a redistributable custom control

This article applies to ASP.NET 2.0 and Visual Studio 2005.

Background

Since its early days, ASP.NET has always supported two different ways of authoring server controls:

  1. Custom Controls: these are controls written from scratch exclusively using code.  Essentially, you write a class which extends Control (directly or indirectly), and you take care of everything.  You need to write logic to create child controls that you want to use, and you override the Render method to perform rendering.  Typically, you build this control into a redistributable assembly (i.e. a DLL), which can then be used by any ASP.NET applications, simply by placing the assembly in the 'bin' directory of the app (or in the Global Assembly Cache).
  2. User Controls: these control are written using an ascx file, which looks much like an aspx page.  i.e. you can simply drag and drop the UI elements that you want to use into the design surface.  You don't need to worry about control creation, nor about overriding the Render method.

Looking at those two methods, they are very different, and each has a number of advantages and disadvantages.  I won't discuss them all, but will focus on those that are relevant to this article:

  • Custom Controls require a lot of development expertise to be written, while User Controls are authored using an advanced designer, and are much more approachable.  For this reason, User Controls typically take a lot less time to write.
  • Custom Controls can easily be redistributed without having to give away their sources.  On the other hand, User Controls are always used based on an ascx text file, making it less ideal for reuse across applications.

The goal of this article is to show how you can have the best of both world by turning an ascx user control into a redistributable custom control, by making use of ASP.NET 2.0's new precompilation features.

 

Brief outline of the steps

The basic steps to make this happen are as follows:

  1. Write your User Control as you normally would, typically using the Visual Studio designer.
  2. Test it using a simple page before trying to deploy it.
  3. Deploy the app to precompile it.
  4. Grab the user control's assembly produced by the deployment step, and you're essentially done: you have your Custom Control.
  5. Finally, use your Custom Control in other apps the same way as you always use Custom Control's.
We will look at those steps in more detail in the rest of the article.
 

Step 1: Authoring the User Control

To author the User Control, it is best to start with an empty app that contains nothing other than the ascx.  While the authoring of the User Control uses 'standard' techniques, there are some restrictions that you need to be aware of in order for it to be successfully turned into a standalone Custom Control.

The main restriction is that the User Control needs to be self contained.  That is, it cannot be dependent on app global things like App_Code or global.asax.  The reason for this is that since the goal is to turn the UserControl into a standalone DLL, it would break in other apps if it relied on code that is not part of that DLL.

One exception to this rule is that the UserControl can be dependent on assemblies that live in the bin directory (or in the GAC).  You just have to make sure that the other assemblies are always available when you use your Custom Control in other apps.

Another tricky thing is the use of static resources, like images.  After you turn it into a Custom Control in a standalone assembly, it becomes hard for it to keep such references, and avoiding them simplifies deployment.  If you really must have such references, one option is to use absolute URL's if you can guarantee that the resource will always be available on a certain site.  Or you can look into machine wide resources (e.g. src="/MyImages/welcome.jpg"), though you will then need to make sure the resources are installed on the server machine (e.g. as part of some setup).

Ok, so let's start and actually author the User Control.  Visual Studio 2005 gives you the choice to place the code in a separate file, or use inline code.  This is a matter of personal preference, and either one will work to create a Custom Control.  For this article, we will use inline code.  When you create the User Control (say MyTestUC.ascx), VS creates it with the a @control directive that looks like this:

<%@ Control Language="C#" ClassName="MyTestUC" %>

This is fine, except for one thing: we want the class to live within a namespace of our choice (instead of 'ASP', which is what's used by default).  To do this, we simply modify the ClassName attribute to include the namespace (this is a new feature in 2.0).  e.g.

<%@ Control Language="C#" ClassName="Acme.MyTestUC" %>

That's really the only 'special' thing you need to do.  Now you can go ahead and implement your User Control as you always would: add some server control, some client HTML, server script, client script, etc...

 

Step 2: Testing your User Control

Before trying to turn the User Control into a Custom Control, it is a good idea to test it in the source app using a simple page.  To do this, simply create a new Page in Visual Studio, go to design View, and drag and drop your User Control into it.

The two notable pieces of your page are the Register directive:

<%@ Register Src="MyTestUC.ascx" TagName="MyTestUC" TagPrefix="uc1" %>

and the User Control declaration:

        <uc1:MyTestUC ID="MyTestUC1" runat="server" />

Note that at this point, the Register directive uses the User Control syntax (Src/TagName/TagPrefix) and not the Custom Control syntax (TagPrefix/Namespace/Assembly).  This will change after we turn the User Control into a Custom Control.

Run your page (Ctrl-F5) and make sure the User Control works the way you want before moving to the next step.


Step 3: Use the Publish command to precompile the site

The next step is to use the new Publish command to precompile your site and turn your User Control into a potential Custom Control.  You'll find the command under Build / Publish Web Site.  In the Publish dialog, do the following:

  • Pick a Target Location.  This is the location on your hard drive that your site will be precompiled to.
  • Uncheck 'Allow this precompiled site to be updatable'.  In updatable mode, only the code behing file (if any) would get compiled, and the ascx would be left unprocessed.  This is useful in some scenarios, but is not what you want here since you want the resulting DLL to be self contained.
  • Check 'Use fixed naming and single page assemblies'.  This will guarantee that your User Control will be compiled into a single assembly, which will have a name based on the ascx file.  If you don't check this, your User Control could be compiled together with other pages and User Controls (if you had some), and the assembly would get a random name that would be more difficult to work with.

Though it is entirely optional, note that the Publish Web Site dialog lets you strong name the generated assemblies.  This allows you to sign the assembly so that it cannot be tempered with.  Additionally, it allows you to place the assemblies in the Global Assembly Cache (GAC), which makes it easier to use machine wide.  More on this in Step 5.

Go ahead and complete the dialog, which will perform the precompilation.

Note: This same step can also be accomplished without using Visual Studio by using the new aspnet_compiler.exe command line tool.  The options it supports are basically the same as what you see in the Publish dialog.  So if you are more command line inclined, you might prefer that route.  e.g. you would invoke it using the command: 'aspnet_compiler -p c:\SourceApp -v myapp -fixednames c:\PrecompiledApp'.

 

Step 4: finding the resulting Custom Control

Now, using the Windows Explorer or a command line window, let's go to the directory you specified as the target so we can see what was generated.  You will see a number of files in there, but let's focus on the one that is relevant to our goal of turning the User Control into a Custom Control.

In the 'bin' directory, you will find a file named something like App_Web_MyTestUC.ascx.cdcab7d2.dll.  You are basically done, as this file is your User Control transformed into a Custom Control!  The only thing that's left to do is to actually use it.

Note: In case you're curious, the hex number within the file name (here 'cdcab7d2') is a hash code that represents the directory that the original file was in.  So all files at the root of your source app will use 'cdcab7d2', while files in other folders will use different numbers.  This is used to avoid naming conflicts in case you have files with the same name in different directories (which is very common for default.aspx!).

 

Step 5: Using your new Custom Control

Now that we have created our Custom Control, let's go ahead and use it in an app!  To do this, let's create a new Web application in Visual Studio.  We then need to make our Custom Control available to this application:

  • In the solution explorer, right click on your application, and choose Add Reference.
  • In the Add Reference dialog, choose the Browse tab.
  • Navigate to the location of your Custom Control (App_Web_MyTestUC.ascx.cdcab7d2.dll) and select it.  It will be copied to the bin directory of your new app.

Note: as an alternative, you can choose to place the assembly in the GAC instead of the 'bin' directory.  In order to do this, you need to choose the Strong Name option in Step 3.  You then need to add your assembly in the <assemblies> section of web.config in the Web application that wants to use it (or in machine.config to make to globally usable).

Now let's create a test page that uses the Custom Control.  This is similar to Step 2, except that you are now dealing with a Custom Control instead of a User Control.

First, add a Register directive to your page.  It should look something like this:

<%@ Register TagPrefix="Acme" Namespace="Acme" Assembly="App_Web_mytestuc.ascx.cdcab7d2" %>

Note how we are using a different set of attributes compared to Step 2.  Recall that in Step 1, we made sure that the ClassName attribute included a namespace.  This is where it becomes useful, as Custom Control registration is namespace based.  Also, you need to specify the assembly name, which is why having a name that is easily recognizable is useful, as discussed in Step 3.

Now, let's declare a tag for the Custom Control.  e.g.

    <Acme:MyTestUC id="MyUC" runat="server" />

That's it, you can now run your app (Ctrl-F5), and you are using your new Custom Control!

This shows how to use your Custom Control declaratively, but note that it can also be used dynamically, just like any other control.  To do this, just create the control using 'new'.  Here is what the previous sample would look like using dynamic instantiation:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    
protected void Page_Load(object sender, EventArgs e) {
        Label1.Controls.Add(
new Acme.MyTestUC());
    
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
    
<form id="form1" runat="server">
    
<div>
        
<asp:Label ID="Label1" runat="server"></asp:Label></div>
    
</form>
</body>
</html>

 

Note: instantiating your Custom Control dynamically as above is basically the equivalent of instantiating your original User Control using the LoadControl API.  Note that you can no longer use the standard LoadControl API after converting it to a Custom Control, since Custom Controls don't have a virtual path.  However, ASP.NET 2.0 has a new LoadControl override which takes a Type, and that you could use in this case.  The one reason I can think of that you might choose to call LoadControl instead of just using 'new' is to take advantage of fragment caching (aka partial caching).  If you use 'new', any OutputCache directive in your ascx will be ignored.

 

Conclusion

This article outlines how to turn a User Control into a Custom Control, in order to take advantage of the strength of each model.  While there are some limitations with this hybrid model (see Restrictions in Step 1), there are many scenarios for which it can be useful.

In the future, the ASP.NET team is looking at simplifying this scenario and make it more powerful with some new tools that are in the work.  For example, you would be able to take an entire application containing an App_Code directory and multiple User Control, and turn it all into a single assembly that can easily be dropped into the 'bin' directory of other apps.  Though it will allow some more powerful scenarios, note that the basic idea behind it is essentially the same as what is shown in this article.

Posted by davidebb | 47 Comments
Filed under:

Introduction

I've been meaning to start this blog for a while, and I'm finally doing it now.  Better late than never!

Quick introduction: I have been a part of the ASP.NET team since its early days (late 1999).  Though I have worked on most parts of the product through the years, my main area of expertise is the ASP.NET compilation system.  This includes the parsing, code generation, compilation, and caching of pages.  Lately, I have also been working on the Atlas project, in particular on the web services logic.

I will use this blog to write about various interesting aspects of ASP.NET that are not well covered elsewhere.

Posted by davidebb | 1 Comments
 
Page view tracker