Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

March 2009 - Posts

Some ASP.NET compiler black magic

(c) 2005 Bertrand Le Roy In the work we’ve been doing with Rob on the Kona commerce app, our quest for extreme pluggability has led us to look at quite a few interesting features of ASP.NET compilation. Features I didn’t know about before Dmitry and David pointed them out for me. I thought I’d share…

It starts with the <%@ Assembly src= %> and <%@ Reference virtualpath= %> directives which you may have seen show up in IntelliSense when building a page. But what are they doing exactly and what differentiates them?

Works in Medium Trust

They both enable you to reference code that is in a different file in the site. With both of them, you get full IntelliSense on the referenced code, but they don’t reference the same kinds of files. @Reference is meant to reference a specific class whereas @Assembly brings in an arbitrary code file. As a consequence, @Reference needs a file where there is a well-defined default class, such as a Page or a User Control. @Assembly on the other hand will enable you to reference an arbitrary code file with as many classes as you wish and get the compiler to dynamically build a neat assembly out of it. The difference really amounts to whether or not the build provider implements GetGeneratedType, which the aspx and ascx build provider implements, and which generic code file providers typically don’t. In other words, if you built your own Build Provider and implemented that method, the files it compiles could be referenced using Reference. Without it, ASP.NET wouldn’t know which type to pick as the referenced one. But @Assembly will still work.

Building an assembly from a virtual path was really important for us because it enables a file anywhere in the web site to be dynamically compiled and used, which is exactly how we wanted plug-ins to work: we could have put the plug-ins into App_code (which does dynamic compilation automatically) but the name is not exactly intuitive, and it limits your ability to mix languages within the same folder. Please note that there *is* a way to mix languages in App_code:

<system.web>
  <compilation debug="true">
    <codeSubDirectories>
      <add directoryName="cs"/>
      <add directoryName="js"/>
    </codeSubDirectories>
  </compilation>
</system.web>

This config setting instructs the compiler to compile the cs and js folders in App_code into separate assemblies, which is all we need to allow for multiple languages. But we can do better than that.

It so happens that the compiler feature that enables the Assembly directive is also available as a public API (that works in Medium Trust):

BuildManager.GetCompiledAssembly

Seriously, this is now officially my favorite API in the whole .NET framework. This quite remarkable API takes a code file within the site and compiles it into an assembly (if that hasn’t already been done by a previous call to that API or by an @Assembly directive). What you get back from it is an Assembly object, which you can reflect on (using public reflection, which works in Medium Trust) and use any way you want.

This will enable us to dynamically compile the files in a Plugins top-level directory of the app. Doing so, we are getting multi-language support without config settings or special folders, nicer folder name and bonus points for not shutting down the app domain every effing time any file is touched. Bye bye App_code!

Well, of course that’s now one assembly per code file, which could be a problem if you have 800 plug-ins in your app, but then again if that’s the case maybe it’s time you moved all those into a nice pre-compiled assembly. Or do some clean-up. Anyway, this will work just fine for the type of scale we have in mind.

A question you may ask at this point is what exactly happens when one of those files is modified. Well, the BuildManager will generate a new assembly and “forget” about the old one. And when I say forget, I don’t mean it’s getting unloaded, just that it won’t get used anymore (unless you have code that held on to a reference). That means that potentially, there could be some assembly rot after a while if your files change often. To mitigate that, BuildManager has a set of rules that it uses to determine that it needs to restart the app domain after a while. Just like App_code, just a lot less often. Basically, the rules are pretty much the same as for aspx or ascx files.

All right, so this is all quite useful (I know I’m going to use that stuff a lot, at least). How about a useless hack now? (if you don’t like a fun hack, feel free to skip the rest of this post)

So think about all the neat stuff we could do by combining this with Virtual Path Providers... Except that in ASP.NET up to and including 3.5 SP1, Virtual Path Providers don’t work in Medium Trust. Neither do Build Providers, which would also be quite neat to play with. Bummer.

But wait, people in the team thought about that and wondered what harm exactly you could do with VPPs and BPs that you couldn’t already do by simply writing code and well, the answer is pretty much nothing. So the good news is that VPPs and BPs will work in Medium Trust in ASP.NET 4.0. Hurray!

So just for the sake of it, here’s the real black magic part of this post. Please note that there is more than one better way to do what I’m about to do and pretty much all of them would be simpler. I’m just hacking here and it’s going to be relatively convoluted.

And as Adam Savage says, “don’t try any of what you’re about to see at home. Ever!”. Seriously, this is dangerous code that has too many holes to count and that I wouldn’t run on anything but Visual Studio’s built-in web server (which is limited to requests from the same machine). Again, just hacking for fun here.

The thing I’ve done is build a VPP that takes an operation embedded in the file name and builds a function that executes that operation. So for example, if you ask it for “A+B.cs”, it will get you a cs file that contains a class that has a method that takes two arguments and returns their sum:

namespace Dynamic {
    public static class Calculator {
        public static double Operation(double A, double B) {
            return A + B;
        }
    }
}

Mix that with the amazing BuildManager.GetCompiledAssembly and we have dynamic compilation of a user-specified function. Which is the scariest part of course, but still fun, eh?

The usual disclaimer to use this at your own risk applies… I also didn’t spend too much time encoding the file name in the VPP thingy so some operations won’t go through too well. Multiplications for example. But eh, even the brightest minds don’t get everything right the first time.

http://weblogs.asp.net/blogs/bleroy/Samples/VppTest.zip

Posted: Mar 30 2009, 04:48 PM by Bertrand Le Roy | with 6 comment(s) |
Filed under: , ,
asp:menu fix for IE8 problem available

(c) 2003 Bertrand Le Roy Internet Explorer 8 is a unique release in the history of Internet Explorer in more than one way, but the decision to make standards mode the default means that authors of existing sites are impacted by it, if only to set the compatibility mode to IE7.

But what if your site is built using components that render out markup and script over which you have little control, such as ASP.NET WebControls? Well, if one of the controls fails in IE8 standards mode, you need to either switch to compatibility mode (ouch!) or you need the component developer to ship an updated version.

During the whole IE8 development cycle, we monitored the behavior of existing controls. Most ASP.NET built-in controls have been doing just fine in IE8, or the faulty behavior was actually due to an IE bug that we reported and that got fixed.

All except asp:menu. It so happens that the menu control is making a bad assumption on what the default value for z-index should be. We debated this at length with the IE team, but it became clear as we did so that they were right and that we were wrong. We had to fix that.

So here it is, the patch for menu is out and you can apply it to build IE8-compatible ASP.NET WebForms sites…

Windows 2000, XP, Server 2003:
http://code.msdn.microsoft.com/KB962351/Release/ProjectReleases.aspx?ReleaseId=2294

Windows Vista, Server 2008:
http://code.msdn.microsoft.com/KB967535/Release/ProjectReleases.aspx?ReleaseId=2328

UPDATE: the KB article can now be found here:
http://support.microsoft.com/kb/962351

Some MIX talks

(c) 2005 Bertrand Le Roy Stephen Walther just published links to the video, slides and sample code for his Ajax talk at MIX09:

http://stephenwalther.com/blog/archive/2009/03/22/mix-slides-code-and-session-recording.aspx

It’s pretty cool to see all the work we put into Ajax this past year or so presented at MIX. This is a really nice presentation, like Stephen’s always are.

Another presentation I had lots of fun watching (not just because the speaker is making an incredible impression of me but also because I’ve been spending a good part of my time lately contributing to the application he’s showing) is Rob Conery’s. Rob is showing an interesting way to develop ASP.NET applications, aimed at ease of use and customization rather than architectural purity. Check it out, let me know what you think…

http://videos.visitmix.com/MIX09/T62F

Microsoft Ajax 4.0 Preview 4 now available

The Microsoft Ajax team made the fourth preview of the 4.0 version available on CodePlex. This is an important release because it enables the full client data story, complete with the ability to get changes back to the server automatically.

Here’s a quick recap of some of the available features:

  • Getting a client representation of data from an ADO.NET and REST data service.
  • Rendering data on the client using templates.
  • Declarative instantiation of client components.
  • Live bindings, enabling changes in the UI and in the data to be automatically propagated.
  • Command bubbling for codeless wiring of events in template-driven controls.
  • Data identity and association management for efficient and consistent client-server data exchanges.
  • Sending changes back to ADO.NET and REST data services.

In a nutshell, it is probably the easiest way to build a data-driven client application. Check this out:

<div id="peopleView" sys:attach="dataview" class="sys-template"
     dataview:dataprovider="{{ $create(Sys.Data.AdoNetDataContext,
                               {serviceUri: 'PeopleIKnow.svc'})}}"
     dataview:fetchoperation="PeopleIKnow"
     dataview:autofetch="true">
    <fieldset>
        <legend>
            <span>{binding FirstName}</span>
            <span>{binding LastName}</span>
        </legend>
        <img code:if="{{ Photo }}"
             sys:src="{{ 'Images/' + Photo }}"
             alt="{{ FirstName + ' ' + LastName }}" />
        <br />
        <input type="text" id="{{ $id('firstName') }}"
               class="editInPlace name"
               value="{binding FirstName}"
               sys:attach="inplace" inplace:cssclass="editing"/>
        <input type="text" id="{{ $id('lastName') }}"
               class="editInPlace name"
               value="{binding LastName}"
               sys:attach="inplace" inplace:cssclass="editing"/>
    </fieldset>
</div>
<br />
<input id="saveButton" type="button" value="Save" />

This creates a DataView that queries the PeopleIKnow.svc ADO.NET data service, and repeats the markup in the div over the data. The legend contains two spans that will respond to live changes to the data ({binding FirstName and {binding LastName}). The image will only be rendered if there is a photo to show (code:if). It builds the image path using a simple JavaScript expression: {{ ‘Images/’ + Photo }}.

The two input tags are augmented by a custom edit in place behavior (sys:attach=”inplace”) and are bound to the FirstName and LastName data columns so that any change to the value of the field will be propagated to everything that depends on the same data: the data itself of course but also the legend of the fieldset (see video below).

The save button is hooked to the following handler:

$addHandler($get("saveButton"), "click", function() {
    $find("peopleView").get_dataProvider().saveChanges();
}, true);

This handler is super-simple as it only has to call saveChanges on the data provider of the DataView. This is enough because any changes made in the input fields have been propagated to the client data model already, which tracked all changes and can build a simple JSON object to send back to the data service. Here is an example of the kind of JSON object that travels back to the server after I’ve changed Simon’s name through the UI:

{
"__metadata":{
"uri":"http://127.0.0.1:26402/Asp.Net_Ajax_Preview_4/
PeopleIKnow.svc/PeopleIKnow(1)",
"type":"PeopleIKnowModel.PeopleIKnow"
},
"ID":1,
"FirstName":"Simon",
"LastName":"Calvert",
"Photo":"simoncal.jpg" }

Here’s the application running:

<br/><a href="http://video.msn.com/video.aspx?vid=140e8d2a-8d01-49ab-b96f-77e219f93e40" target="_new" title="ASP.NET Ajax 4.0 Preview 4 ">Video: ASP.NET Ajax 4.0 Preview 4 </a>

The source code can be downloaded from here (contains code licensed under MS-PL):
http://weblogs.asp.net/blogs/bleroy/Samples/Asp.Net_Ajax_Preview_4.zip

But wait, there’s more in stock for the next preview… I’ll post more details about some of those features in the following weeks… Stay tuned…

http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24645

More Posts