Dynamic Types and DynamicObject References in C#



I've been working a bit with C# custom dynamic types for several customers recently and I've seen some confusion in understanding how dynamic types are referenced. This discussion specifically centers around types that implement IDynamicObject or subclass from DynamicObject as opposed to arbitrary type casts of standard .NET types. IDynamicObject types  are treated special when they are cast to the dynamic type.

Assume for a second that I've created my own implementation of a custom dynamic type called DynamicFoo which is about as simple of a dynamic class that I can think of:

public class DynamicFoo : DynamicObject
{
    Dictionary<string, object> properties = new Dictionary<string, object>();

    public string Bar { get; set; }
    public DateTime Entered { get; set; }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;
        if (!properties.ContainsKey(binder.Name))
            return false;

        result = properties[binder.Name];
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        properties[binder.Name] = value;
        return true;
    }
}

This class has an internal dictionary member and I'm exposing this dictionary member through a dynamic by implementing DynamicObject. This implementation exposes the properties dictionary so the dictionary keys can be referenced like properties (foo.NewProperty = "Cool!"). I override TryGetMember() and TrySetMember() which are fired at runtime every time you access a 'property' on a dynamic instance of this DynamicFoo type.

Strong Typing and Dynamic Casting

I now can instantiate and use DynamicFoo in a couple of different ways:

Strong Typing

DynamicFoo fooExplicit = new DynamicFoo();
var fooVar = new DynamicFoo();

These two commands are essentially identical and use strong typing. The compiler generates identical code for both of them. The var statement is merely a compiler directive to infer the type of fooVar at compile time and so the type of fooExplicit is DynamicFoo, just like fooExplicit. This is very static - nothing dynamic about it - and it completely ignores the IDynamicObject implementation of my class above as it's never used.

Using either of these I can access the native properties:

DynamicFoo fooExplicit = new DynamicFoo();


// static typing assignments
fooVar.Bar = "Barred!"; fooExplicit.Entered = DateTime.Now;
// echo back static values
Console.WriteLine(fooVar.Bar);
Console.WriteLine(fooExplicit.Entered);

but I have no access whatsoever to the properties dictionary. Basically this creates a strongly typed instance of the type with access only to the strongly typed interface. You get no dynamic behavior at all. The IDynamicObject features don't kick in until you cast the type to dynamic.

If I try to access a non-existing property on fooExplicit I get a compilation error that tells me that the property doesn't exist. Again, it's clearly and utterly non-dynamic.

Dynamic

dynamic fooDynamic = new DynamicFoo();

fooDynamic on the other hand is created as a dynamic type and it's a completely different beast. I can also create a dynamic by simply casting any type to dynamic like this:

DynamicFoo fooExplicit = new DynamicFoo();
dynamic fooDynamic = fooExplicit;

Note that dynamic typically doesn't require an explicit cast as the compiler automatically performs the cast so there's no need to use as dynamic.

Dynamic functionality works at runtime and allows for the dynamic wrapper to look up and call members dynamically. A dynamic type will look for members to access or call in two places:

  • Using the strongly typed members of the object
  • Using the IDynamicObject Interface methods to access members

So rather than statically linking and calling a method or retrieving a property, the dynamic type looks up - at runtime  - where the value actually comes from. It's essentially late-binding which allows runtime determination what action to take when a member is accessed at runtime *if* the member you are accessing does not exist on the object. Class members are checked first before IDynamicObject interface methods are kick in.

All of the following works with the dynamic type:

dynamic fooDynamic = new DynamicFoo();
// dynamic typing assignments
fooDynamic.NewProperty = "Something new!";
fooDynamic.LastAccess = DateTime.Now;

// dynamic assigning static properties
fooDynamic.Bar = "dynamic barred";
fooDynamic.Entered = DateTime.Now;

// echo back dynamic values
Console.WriteLine(fooDynamic.NewProperty);
Console.WriteLine(fooDynamic.LastAccess);
Console.WriteLine(fooDynamic.Bar);
Console.WriteLine(fooDynamic.Entered);

The dynamic type can access the native class properties (Bar and Entered) and create and read new ones (NewProperty,LastAccess) all using a single type instance which is pretty cool. As you can see it's pretty easy to create an extensible type this way that can dynamically add members at runtime dynamically.

The Alter Ego of IDynamicObject

The key point here is that all three statements - explicit, var and dynamic - declare a new DynamicFoo(), but the dynamic declaration results in completely different behavior than the first two simply because the type has been cast to dynamic.

Dynamic binding means that the type loses its typical strong typing, compile time features. You can see this easily in the Visual Studio code editor. As soon as you assign a value to a dynamic you lose Intellisense and you see

DynamicInDebugger

which means there's no Intellisense and no compiler type checking on any members you apply to this instance.

If you're new to the dynamic type it might seem really confusing that a single type can behave differently depending on how it is cast, but that's exactly what happens when you use a type that implements IDynamicObject. Declare the type as its strong type name and you only get to access the native instance members of the type. Declare or cast it to dynamic and you get dynamic behavior which accesses native members plus it uses IDynamicObject implementation to handle any missing member definitions by running custom code.

You can easily cast objects back and forth between dynamic and the original type:

dynamic fooDynamic = new DynamicFoo();
fooDynamic.NewProperty = "New Property Value";             
DynamicFoo foo = fooDynamic;
foo.Bar = "Barred";

Here the code starts out with a dynamic cast and a dynamic assignment. The code then casts back the value to the DynamicFoo. Notice that when casting from dynamic to DynamicFoo and back we typically do not have to specify the cast explicitly - the compiler can induce the type so I don't need to specify as dynamic or as DynamicFoo.

Moral of the Story

This easy interchange between dynamic and the underlying type is actually super useful, because it allows you to create extensible objects that can expose non-member data stores and expose them as an object interface. You can create an object that hosts a number of strongly typed properties and then cast the object to dynamic and add additional dynamic properties to the same type at runtime. You can easily switch back and forth between the strongly typed instance to access the well-known strongly typed properties and to dynamic for the dynamic properties added at runtime.

Keep in mind that dynamic object access has quite a bit of overhead and is definitely slower than strongly typed binding, so if you're accessing the strongly typed parts of your objects you definitely want to use a strongly typed reference. Reserve dynamic for the dynamic members to optimize your code.

The real beauty of dynamic is that with very little effort you can build expandable objects or objects that expose different data stores to an object interface. I'll have more on this in my next post when I create a customized and extensible Expando object based on DynamicObject.



Unable to cast transparent proxy to type <type>



This is not the first time I've run into this wonderful error while creating new AppDomains in .NET and then trying to load types and access them across App Domains.

In almost all cases the problem I've run into with this error the problem comes from the two AppDomains involved loading different copies of the same type. Unless the types match exactly and come exactly from the same assembly the typecast will fail. The most common scenario is that the types are loaded from different assemblies - as unlikely as that sounds.

An Example of Failure

To give some context, I'm working on some old code in Html Help Builder that creates a new AppDomain in order to parse assembly information for documentation purposes. I create a new AppDomain in order to load up an assembly process it and then immediately unload it along with the AppDomain. The AppDomain allows for unloading that otherwise wouldn't be possible as well as isolating my code from the assembly that's being loaded.

The process to accomplish this is fairly established and I use it for lots of applications that use add-in like functionality - basically anywhere where code needs to be isolated and have the ability to be unloaded. My pattern for this is:

  • Create a new AppDomain
  • Load a Factory Class into the AppDomain
  • Use the Factory Class to load additional types from the remote domain

Here's the relevant code from my TypeParserFactory that creates a domain and then loads a specific type - TypeParser - that is accessed cross-AppDomain in the parent domain:

public class TypeParserFactory : System.MarshalByRefObject,IDisposable    
{
/// <summary>
/// TypeParser Factory method that loads the TypeParser
/// object into a new AppDomain so it can be unloaded.
/// Creates AppDomain and creates type.
/// </summary>
/// <returns></returns>
public TypeParser CreateTypeParser() 
{
    if (!CreateAppDomain(null))
        return null;

    /// Create the instance inside of the new AppDomain
    /// Note: remote domain uses local EXE's AppBasePath!!!
    TypeParser parser = null;

    try 
    {
       Assembly assembly = Assembly.GetExecutingAssembly();               
       string assemblyPath = Assembly.GetExecutingAssembly().Location;
       parser = (TypeParser) this.LocalAppDomain.CreateInstanceFrom(assemblyPath,
                                              typeof(TypeParser).FullName).Unwrap();                              
    }
    catch (Exception ex)
    {
        this.ErrorMessage = ex.GetBaseException().Message;
        return null;
    }

    return parser;
}

private bool CreateAppDomain(string lcAppDomain) 
{
    if (lcAppDomain == null)
        lcAppDomain = "wwReflection" + Guid.NewGuid().ToString().GetHashCode().ToString("x");

    AppDomainSetup setup = new AppDomainSetup();

    // *** Point at current directory
    setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
    //setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");

    this.LocalAppDomain = AppDomain.CreateDomain(lcAppDomain,null,setup);

    // Need a custom resolver so we can load assembly from non current path
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    
    return true;
}
   …
}

Note that the classes must be either [Serializable] (by value) or inherit from MarshalByRefObject in order to be accessible remotely. Here I need to call methods on the remote object so all classes are MarshalByRefObject.

The specific problem code is the loading up a new type which points at an assembly that visible both in the current domain and the remote domain and then instantiates a type from it. This is the code in question:

Assembly assembly = Assembly.GetExecutingAssembly();               
string assemblyPath = Assembly.GetExecutingAssembly().Location;
parser = (TypeParser) this.LocalAppDomain.CreateInstanceFrom(assemblyPath,
                                       typeof(TypeParser).FullName).Unwrap();  

The last line of code is what blows up with the Unable to cast transparent proxy to type <type> error. Without the cast the code actually returns a TransparentProxy instance, but the cast is what blows up. In other words I AM in fact getting a TypeParser instance back but it can't be cast to the TypeParser type that is loaded in the current AppDomain.

Finding the Problem

To see what's going on I tried using the .NET 4.0 dynamic type on the result and lo and behold it worked with dynamic - the value returned is actually a TypeParser instance:

Assembly assembly = Assembly.GetExecutingAssembly();               
string assemblyPath = Assembly.GetExecutingAssembly().Location;
object objparser = this.LocalAppDomain.CreateInstanceFrom(assemblyPath,
                                      typeof(TypeParser).FullName).Unwrap();


// dynamic works
dynamic dynParser = objparser;
string info = dynParser.GetVersionInfo(); // method call works

// casting fails
parser = (TypeParser)objparser; 

So clearly a TypeParser type is coming back, but nevertheless it's not the right one. Hmmm… mysterious.
Another couple of tries reveal the problem however:

// works
dynamic dynParser = objparser;
string info = dynParser.GetVersionInfo(); // method call works

// c:\wwapps\wwhelp\wwReflection20.dll   (Current Execution Folder)
string info3 = typeof(TypeParser).Assembly.CodeBase;

// c:\program files\vfp9\wwReflection20.dll   (my COM client EXE's folder)
string info4 = dynParser.GetType().Assembly.CodeBase;

// fails
parser = (TypeParser)objparser; 

As you can see the second value is coming from a totally different assembly. Note that this is even though I EXPLICITLY SPECIFIED an assembly path to load the assembly from! Instead .NET decided to load the assembly from the original ApplicationBase folder. Ouch!

How I actually tracked this down was a little more tedious: I added a method like this to both the factory and the instance types and then compared notes:

public string GetVersionInfo()
{
    return ".NET Version: " + Environment.Version.ToString() + "\r\n" +
    "wwReflection Assembly: " + typeof(TypeParserFactory).Assembly.CodeBase.Replace("file:///", "").Replace("/", "\\") + "\r\n" +
    "Assembly Cur Dir: " + Directory.GetCurrentDirectory() + "\r\n" +
    "ApplicationBase: " + AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "\r\n" +
    "App Domain: " + AppDomain.CurrentDomain.FriendlyName + "\r\n";
}

For the factory I got:

.NET Version: 4.0.30319.239
wwReflection Assembly: c:\wwapps\wwhelp\bin\wwreflection20.dll
Assembly Cur Dir: c:\wwapps\wwhelp
ApplicationBase: C:\Programs\vfp9\
App Domain: wwReflection534cfa1f

For the instance type I got:

.NET Version: 4.0.30319.239
wwReflection Assembly: C:\\Programs\\vfp9\wwreflection20.dll
Assembly Cur Dir: c:\\wwapps\\wwhelp
ApplicationBase: C:\\Programs\\vfp9\
App Domain: wwDotNetBridge_56006605

which clearly shows the problem. You can see that both are loading from different appDomains but the each is loading the assembly from a different location.

Probably a better solution yet (for ANY kind of assembly loading problem) is to use the .NET Fusion Log Viewer to trace assembly loads.The Fusion viewer will show a load trace for each assembly loaded and where it's looking to find it. Here's what the viewer looks like:

FusionLogViewer

The last trace above that I found for the second wwReflection20 load (the one that is wonky) looks like this:

*** Assembly Binder Log Entry  (1/13/2012 @ 3:06:49 AM) ***

The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\V4.0.30319\clr.dll
Running under executable  c:\programs\vfp9\vfp9.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = Ras\ricks
LOG: DisplayName = wwReflection20, Version=4.61.0.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/Programs/vfp9/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = vfp9.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Programs\vfp9\vfp9.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\V4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Programs/vfp9/wwReflection20.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\Programs\vfp9\wwReflection20.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: wwReflection20, Version=4.61.0.0, Culture=neutral, PublicKeyToken=null
LOG: Binding succeeds. Returns assembly from C:\Programs\vfp9\wwReflection20.dll.
LOG: Assembly is loaded in default load context.
WRN: The same assembly was loaded into multiple contexts of an application domain:
WRN: Context: Default | Domain ID: 2 | Assembly Name: wwReflection20, Version=4.61.0.0, Culture=neutral, PublicKeyToken=null
WRN: Context: LoadFrom | Domain ID: 2 | Assembly Name: wwReflection20, Version=4.61.0.0, Culture=neutral, PublicKeyToken=null
WRN: This might lead to runtime failures.
WRN: It is recommended to inspect your application on whether this is intentional or not.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.

Notice that the fusion log clearly shows that the .NET loader makes no attempt to even load the assembly from the path I explicitly specified.

Remember your Assembly Locations

As mentioned earlier all failures I've seen like this ultimately resulted from different versions of the same type being available in the two AppDomains. At first sight that seems ridiculous - how could the types be different and why would you have multiple assemblies - but there are actually a number of scenarios where it's quite possible to have multiple copies of the same assembly floating around in multiple places.

If you're hosting different environments (like hosting the Razor Engine, or ASP.NET Runtime for example) it's common to create a private BIN folder and it's important to make sure that there's no overlap of assemblies.

In my case of Html Help Builder the problem started because I'm using COM interop to access the .NET assembly and the above code. COM Interop has very specific requirements on where assemblies can be found and because I was mucking around with the loader code today, I ended up moving assemblies around to a new location for explicit loading. The explicit load works in the main AppDomain, but failed in the remote domain as I showed. The solution here was simple enough: Delete the extraneous assembly which was left around by accident.

Not a common problem, but one that when it bites is pretty nasty to figure out because it seems so unlikely that types wouldn't match. I know I've run into this a few times and writing this down hopefully will make me remember in the future rather than poking around again for an hour trying to debug the issue as I did today. Hopefully it'll save some of you some time as well in the future.



IE9 not rendering box-shadow Elements inside of Table Cells



Ran into an annoying problem today with IE 9. Slowly updating some older sites with CSS 3 tags and for the most part IE9 does a reasonably decent job of working with the new CSS 3 features. Not all by a long shot but at least some of the more useful ones like border-radius and box-shadow are supported.

Until today I was happy to see that IE supported box-shadow just fine, but I ran into a problem with some old markup that uses tables for its main layout sections. I found that inside of a table cell IE fails to render a box-shadow.

Below are images from Chrome (left) and IE 9 (right) of the same content:

ChromeAndIe

The download and purchase images are rendered with:

<a href="download.asp" style="display:block;margin: 10px;"><img src="../images/download.gif" class="boxshadow roundbox" /></a>

where the .boxshadow and .roundbox styles look like this:

.boxshadow 
{
  -moz-box-shadow: 3px 3px 5px #535353;
  -webkit-box-shadow: 3px 3px 5px #535353;       
  box-shadow: 3px 3px 5px #535353;
}
.roundbox
{  
  -moz-border-radius: 6px 6px 6px 6px;
  -webkit-border-radius: 6px;  
  border-radius: 6px 6px 6px 6px;
}

And the Problem is… collapsed Table Borders

Now normally these two styles work just fine in IE 9 when applied to elements. But the box-shadow doesn't work inside of this markup - because the parent container is a table cell.

<td class="sidebar" style="border-collapse: collapse">
   <a href="download.asp" style="display:block;margin: 10px;"><img src="../images/download.gif" class="boxshadow roundbox" /></a>

</td>

This HTML causes the image to not show a shadow. In actuality I'm not styling inline, but as part of my browser Reset I have the following in my master .css file:

table 
{
    border-collapse: collapse;
    border-spacing: 0;
}

which has the same effect as the inline style. border-collapse by default inherits from the parent and so the TD inherits from table and tr - so TD tags are effectively collapsed.

You can check out a test document that demonstrates this behavior here in this CodePaste.net snippet or run it here.

How to work around this Issue

To get IE9 to render the shadows inside of the TD tag correctly, I can just change the style explicitly NOT to use border-collapse:

<td class="sidebar" style="border-collapse: separate; border-width: 0;">

Or better yet (thanks to David's comment below), you can add the border-collapse: separate to the .boxshadow style like this:

.boxshadow 
{
  -moz-box-shadow: 3px 3px 5px #535353;
  -webkit-box-shadow: 3px 3px 5px #535353;       
  box-shadow: 3px 3px 5px #535353;
  border-collapse: separate;
}

With either of these approaches IE renders the shadows correctly.

Do you really need border-collapse?

Should you bother with border-collapse? I think so! Collapsed borders render flat as a single fat line if a border-width and border-color are assigned, while separated borders render a thin line with a bunch of weird white space around it or worse render a old skool 3D raised border which is terribly ugly as well. So as a matter of course in any app my browser Reset includes the above code to make sure all tables with borders render the same flat borders.

As you probably know, IE has all sorts of rendering issues in tables and on backgrounds (opacity backgrounds or image backgrounds) most of which is caused by the way that IE internally uses ActiveX filters to apply these effects. Apparently collapsed borders are yet one more item that causes problems with rendering.

There you have it. Another crappy failure in IE we have to check for now, just one more reason to hate Internet Explorer. Luckily this one has a reasonably easy workaround. I hope this helps out somebody and saves them the hour I spent trying to figure out what caused this problem in the first place.

Resources



XmlWriter and lower ASCII characters



If you've ever tried to generate an XML document from content that contains lower ASCII characters you might have found out that this will throw exceptions. Here's why this happens and how you can work around the issue in a pinch.

Read more...



Changing the default HTML Templates to HTML5 in Visual Studio



The default WebForms templates in Visual Studio still use the XHTML doctype headers by default. HTML5 doctype headers are easier to use and read and with HTML5 support now becoming mainstream and backward compatible with older browsers its time to switch those doctype headers. This post demonstrates how to change the default VS templates or create new templates altogether. With HTML becoming more prominent and the new headers being easier to read and smaller in size, it's

Read more...



Debugging Application_Start and Module Initialization with IIS and Visual Studio



If you're running the full version of IIS and you try to debug your Web application's startup code in Application_Start you might have found that you can't debug this code as the debugger doesn't break there. Here's why and some easy ways you can work around this limitation.

Read more...



HTML 5 Input Types - How useful is this really going to be?



The HTML 5 input controls enhancements seem like a nice feature - until you look a little closer and realize that that validation and styling these control enhancement use are likely going to interfere with your existing application logic and styling. Here are are some thoughts on the subject.

Read more...



HTML 5 Input Types on WebForms Controls



HTML5 input types are new, and as it turns out ASP.NET Webforms input controls can easily create HTML5 input elements.

Read more...



A Key Code Checker for DOM Keyboard Events



Handling keyboard input events in JavaScript can be tricky when you need to deal with key codes. There are browser difference and different behaviors for various key events. Here's a refresher on how keyboard events work and a utility that lets you test key strokes and their resulting key codes in the various events available.

Read more...



Creating a Dynamic DataReader for easier Property Access



Custom dynamic types in .NET are great to wrap other data structures into easier to use and cleaner object.property interfaces. In this post I demonstrate how you can create a dynamic DataReader that allows access to a DataReader's fields using plain object.property syntax.

Read more...



jQuery Time Entry with Time Navigation Keys



How do you display editable time values in Web applications? While date display has a pretty clear UI choice with date pickers, visual time picking isn't very efficient. In this post I show a keyboard based alternative to navigating and entering time (and date values) values using hotkeys hooked up through a jQuery plugin.

Read more...



Creating a Dynamic DataRow for easier DataRow Syntax



The Dynamic Language Runtime features in .NET 4.0 make it very easy to create custom dynamic types that use alternate 'data sources' to expose a new member interface. In this post I describe a simple example that exposes a classic DataRow as a dynamic type to allow cleaner syntax and no need for type casting when accessing DataRow objects.

Read more...




West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2012