Today @ 2:28 am
- from Maui, Hawaii
I love dynamic functionality in a strongly typed language because it offers us the best of both worlds. In C# (or any of the main .NET languages) we now have the dynamic type that provides a host of dynamic features for the static C# language.
One place where I've found dynamic to be incredibly useful is in building extensible types or types that expose traditionally non-object data (like dictionaries) in easier to use and more readable syntax. I wrote about a couple of these for accessing old school ADO.NET DataRows and DataReaders more easily for example. These classes are dynamic wrappers that provide easier syntax and auto-type conversions which greatly simplifies code clutter and increases clarity in existing code.
ExpandoObject in .NET 4.0
Another great use case for dynamic objects is the ability to create extensible objects - objects that start out with a set of static members and then can add additional properties and even methods dynamically. The .NET 4.0 framework actually includes an ExpandoObject class which provides a very dynamic object that allows you to add properties and methods on the fly and then access them again.
For example with ExpandoObject you can do stuff like this:
dynamic expand = new ExpandoObject();
expand.Name = "Rick";
expand.HelloWorld = (Func<string, string>) ((string name) =>
{
return "Hello " + name;
});
Console.WriteLine(expand.Name);
Console.WriteLine(expand.HelloWorld("Dufus"));
Internally ExpandoObject uses a Dictionary like structure and interface to store properties and methods and then allows you to add and access properties and methods easily. As cool as ExpandoObject is it has a few shortcomings too:
- It's a sealed type so you can't use it as a base class
- It only works off 'properties' in the internal Dictionary - you can't expose existing type data
- It doesn't serialize to XML or with DataContractSerializer/DataContractJsonSerializer
Expando - A truly extensible Object
ExpandoObject is nice if you just need a dynamic container for a dictionary like structure. However, if you want to build an extensible object that starts out with a set of strongly typed properties and then allows you to extend it, ExpandoObject does not work because it's a sealed class that can't be inherited.
I started thinking about this very scenario for one of my applications I'm building for a customer. In this system we are connecting to various different user stores. Each user store has the same basic requirements for username, password, name etc. But then each store also has a number of extended properties that is available to each application. In the real world scenario the data is loaded from the database in a data reader and the known properties are assigned from the known fields in the database. All unknown fields are then 'added' to the expando object dynamically.
In the past I've done this very thing with a separate property - Properties - just like I do for this class. But the property and dictionary syntax is not ideal and tedious to work with.
I started thinking about how to represent these extra property structures. One way certainly would be to add a Dictionary, or an ExpandoObject to hold all those extra properties. But wouldn't it be nice if the application could actually extend an existing object that looks something like this as you can with the Expando object:
public class User : Westwind.Utilities.Dynamic.Expando
{
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public DateTime? ExpiresOn { get; set; }
}
and then simply start extending the properties of this object dynamically? Using the Expando object I describe later you can now do the following:
[TestMethod]
public void UserExampleTest()
{
var user = new User();
// Set strongly typed properties
user.Email = "rick@west-wind.com";
user.Password = "nonya123";
user.Name = "Rickochet";
user.Active = true;
// Now add dynamic properties
dynamic duser = user;
duser.Entered = DateTime.Now;
duser.Accesses = 1;
// you can also add dynamic props via indexer
user["NickName"] = "AntiSocialX";
duser["WebSite"] = "http://www.west-wind.com/weblog";
// Access strong type through dynamic ref
Assert.AreEqual(user.Name,duser.Name);
// Access strong type through indexer
Assert.AreEqual(user.Password,user["Password"]);
// access dyanmically added value through indexer
Assert.AreEqual(duser.Entered,user["Entered"]);
// access index added value through dynamic
Assert.AreEqual(user["NickName"],duser.NickName);
// loop through all properties dynamic AND strong type properties (true)
foreach (var prop in user.GetProperties(true))
{
object val = prop.Value;
if (val == null)
val = "null";
Console.WriteLine(prop.Key + ": " + val.ToString());
}
}
As you can see this code somewhat blurs the line between a static and dynamic type. You start with a strongly typed object that has a fixed set of properties. You can then cast the object to dynamic (as I discussed in my last post) and add additional properties to the object. You can also use an indexer to add dynamic properties to the object.
To access the strongly typed properties you can use either the strongly typed instance, the indexer or the dynamic cast of the object. Personally I think it's kinda cool to have an easy way to access strongly typed properties by string which can make some data scenarios much easier.
To access the 'dynamically added' properties you can use either the indexer on the strongly typed object, or property syntax on the dynamic cast.
Using the dynamic type allows all three modes to work on both strongly typed and dynamic properties.
Finally you can iterate over all properties, both dynamic and strongly typed if you chose. Lots of flexibility.
Note also that by default the Expando object works against the (this) instance meaning it extends the current object. You can also pass in a separate instance to the constructor in which case that object will be used to iterate over to find properties rather than this.
Using this approach provides some really interesting functionality when use the dynamic type. To use this we have to add an explicit constructor to the Expando subclass:
public class User : Westwind.Utilities.Dynamic.Expando
{
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public DateTime? ExpiresOn { get; set; }
public User() : base()
{ }
// only required if you want to mix in seperate instance
public User(object instance)
: base(instance)
{
}
}
to allow the instance to be passed. When you do you can now do:
[TestMethod]
public void ExpandoMixinTest()
{
// have Expando work on Addresses
var user = new User( new Address() );
// cast to dynamicAccessToPropertyTest
dynamic duser = user;
// Set strongly typed properties
duser.Email = "rick@west-wind.com";
user.Password = "nonya123";
// Set properties on address object
duser.Address = "32 Kaiea";
//duser.Phone = "808-123-2131";
// set dynamic properties
duser.NonExistantProperty = "This works too";
// shows default value Address.Phone value
Console.WriteLine(duser.Phone);
}
Using the dynamic cast in this case allows you to access *three* different 'objects': The strong type properties, the dynamically added properties in the dictionary and the properties of the instance passed in! Effectively this gives you a way to simulate multiple inheritance (which is scary - so be very careful with this, but you can do it).
How Expando works
Behind the scenes Expando is a DynamicObject subclass as I discussed in my last post. By implementing a few of DynamicObject's methods you can basically create a type that can trap 'property missing' and 'method missing' operations. When you access a non-existant property a known method is fired that our code can intercept and provide a value for. Internally Expando uses a custom dictionary implementation to hold the dynamic properties you might add to your expandable object.
Let's look at code first. The code for the Expando type is straight forward and given what it provides relatively short. Here it is.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using System.Reflection;
namespace Westwind.Utilities.Dynamic
{
/// <summary>
/// Class that provides extensible properties and methods. This
/// dynamic object stores 'extra' properties in a dictionary or
/// checks the actual properties of the instance.
///
/// This means you can subclass this expando and retrieve either
/// native properties or properties from values in the dictionary.
///
/// This type allows you three ways to access its properties:
///
/// Directly: any explicitly declared properties are accessible
/// Dynamic: dynamic cast allows access to dictionary and native properties/methods
/// Dictionary: Any of the extended properties are accessible via IDictionary interface
/// </summary>
[Serializable]
public class Expando : DynamicObject, IDynamicMetaObjectProvider
{
/// <summary>
/// Instance of object passed in
/// </summary>
object Instance;
/// <summary>
/// Cached type of the instance
/// </summary>
Type InstanceType;
PropertyInfo[] InstancePropertyInfo
{
get
{
if (_InstancePropertyInfo == null && Instance != null)
_InstancePropertyInfo = Instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
return _InstancePropertyInfo;
}
}
PropertyInfo[] _InstancePropertyInfo;
/// <summary>
/// String Dictionary that contains the extra dynamic values
/// stored on this object/instance
/// </summary>
/// <remarks>Using PropertyBag to support XML Serialization of the dictionary</remarks>
public PropertyBag Properties = new PropertyBag();
//public Dictionary<string,object> Properties = new Dictionary<string, object>();
/// <summary>
/// This constructor just works off the internal dictionary and any
/// public properties of this object.
///
/// Note you can subclass Expando.
/// </summary>
public Expando()
{
Initialize(this);
}
/// <summary>
/// Allows passing in an existing instance variable to 'extend'.
/// </summary>
/// <remarks>
/// You can pass in null here if you don't want to
/// check native properties and only check the Dictionary!
/// </remarks>
/// <param name="instance"></param>
public Expando(object instance)
{
Initialize(instance);
}
protected virtual void Initialize(object instance)
{
Instance = instance;
if (instance != null)
InstanceType = instance.GetType();
}
/// <summary>
/// Try to retrieve a member by name first from instance properties
/// followed by the collection entries.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
// first check the Properties collection for member
if (Properties.Keys.Contains(binder.Name))
{
result = Properties[binder.Name];
return true;
}
// Next check for Public properties via Reflection
if (Instance != null)
{
try
{
return GetProperty(Instance, binder.Name, out result);
}
catch { }
}
// failed to retrieve a property
result = null;
return false;
}
/// <summary>
/// Property setter implementation tries to retrieve value from instance
/// first then into this object
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// first check to see if there's a native property to set
if (Instance != null)
{
try
{
bool result = SetProperty(Instance, binder.Name, value);
if (result)
return true;
}
catch { }
}
// no match - set or add to dictionary
Properties[binder.Name] = value;
return true;
}
/// <summary>
/// Dynamic invocation method. Currently allows only for Reflection based
/// operation (no ability to add methods dynamically).
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (Instance != null)
{
try
{
// check instance passed in for methods to invoke
if (InvokeMethod(Instance, binder.Name, args, out result))
return true;
}
catch { }
}
result = null;
return false;
}
/// <summary>
/// Reflection Helper method to retrieve a property
/// </summary>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="result"></param>
/// <returns></returns>
protected bool GetProperty(object instance, string name, out object result)
{
if (instance == null)
instance = this;
var miArray = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance);
if (miArray != null && miArray.Length > 0)
{
var mi = miArray[0];
if (mi.MemberType == MemberTypes.Property)
{
result = ((PropertyInfo)mi).GetValue(instance,null);
return true;
}
}
result = null;
return false;
}
/// <summary>
/// Reflection helper method to set a property value
/// </summary>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
protected bool SetProperty(object instance, string name, object value)
{
if (instance == null)
instance = this;
var miArray = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty |
BindingFlags.Instance);
if (miArray != null && miArray.Length > 0)
{
var mi = miArray[0];
if (mi.MemberType == MemberTypes.Property)
{
((PropertyInfo)mi).SetValue(Instance, value, null);
return true;
}
}
return false;
}
/// <summary>
/// Reflection helper method to invoke a method
/// </summary>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
protected bool InvokeMethod(object instance, string name, object[] args, out object result)
{
if (instance == null)
instance = this;
// Look at the instanceType
var miArray = InstanceType.GetMember(name,
BindingFlags.InvokeMethod |
BindingFlags.Public | BindingFlags.Instance);
if (miArray != null && miArray.Length > 0)
{
var mi = miArray[0] as MethodInfo;
result = mi.Invoke(Instance, args);
return true;
}
result = null;
return false;
}
/// <summary>
/// Convenience method that provides a string Indexer
/// to the Properties collection AND the strongly typed
/// properties of the object by name.
///
/// // dynamic
/// exp["Address"] = "112 nowhere lane";
/// // strong
/// var name = exp["StronglyTypedProperty"] as string;
/// </summary>
/// <remarks>
/// The getter checks the Properties dictionary first
/// then looks in PropertyInfo for properties.
/// The setter checks the instance properties before
/// checking the Properties dictionary.
/// </remarks>
/// <param name="key"></param>
///
/// <returns></returns>
public object this[string key]
{
get
{
try
{
// try to get from properties collection first
return Properties[key];
}
catch (KeyNotFoundException ex)
{
// try reflection on instanceType
object result = null;
if (GetProperty(Instance, key, out result))
return result;
// nope doesn't exist
throw;
}
}
set
{
if (Properties.ContainsKey(key))
{
Properties[key] = value;
return;
}
// check instance for existance of type first
var miArray = InstanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
if (miArray != null && miArray.Length > 0)
SetProperty(Instance, key, value);
else
Properties[key] = value;
}
}
/// <summary>
/// Returns and the properties of
/// </summary>
/// <param name="includeProperties"></param>
/// <returns></returns>
public IEnumerable<KeyValuePair<string,object>> GetProperties(bool includeInstanceProperties = false)
{
if (includeInstanceProperties && Instance != null)
{
foreach (var prop in this.InstancePropertyInfo)
yield return new KeyValuePair<string, object>(prop.Name, prop.GetValue(Instance, null));
}
foreach (var key in this.Properties.Keys)
yield return new KeyValuePair<string, object>(key, this.Properties[key]);
}
/// <summary>
/// Checks whether a property exists in the Property collection
/// or as a property on the instance
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Contains(KeyValuePair<string, object> item, bool includeInstanceProperties = false)
{
bool res = Properties.ContainsKey(item.Key);
if (res)
return true;
if (includeInstanceProperties && Instance != null)
{
foreach (var prop in this.InstancePropertyInfo)
{
if (prop.Name == item.Key)
return true;
}
}
return false;
}
}
}
Although the Expando class supports an indexer, it doesn't actually implement IDictionary or even IEnumerable. It only provides the indexer and Contains() and GetProperties() methods, that work against the Properties dictionary AND the internal instance.
The reason for not implementing IDictionary is that a) it doesn't add much value since you can access the Properties dictionary directly and that b) I wanted to keep the interface to class very lean so that it can serve as an entity type if desired. Implementing these IDictionary (or even IEnumerable) causes LINQ extension methods to pop up on the type which obscures the property interface and would only confuse the purpose of the type. IDictionary and IEnumerable are also problematic for XML and JSON Serialization - the XML Serializer doesn't serialize IDictionary<string,object>, nor does the DataContractSerializer. The JavaScriptSerializer does serialize, but it treats the entire object like a dictionary and doesn't serialize the strongly typed properties of the type, only the dictionary values which is also not desirable. Hence the decision to stick with only implementing the indexer to support the user["CustomProperty"] functionality and leaving iteration functions to the publicly exposed Properties dictionary.
Note that the Dictionary used here is a custom PropertyBag class I created to allow for serialization to work. One important aspect for my apps is that whatever custom properties get added they have to be accessible to AJAX clients since the particular app I'm working on is a SIngle Page Web app where most of the Web access is through JSON AJAX calls. PropertyBag can serialize to XML and one way serialize to JSON using the JavaScript serializer (not the DCS serializers though).
The key components that make Expando work in this code are the Properties Dictionary and the TryGetMember() and TrySetMember() methods. The Properties collection is public so if you choose you can explicitly access the collection to get better performance or to manipulate the members in internal code (like loading up dynamic values form a database).
Notice that TryGetMember() and TrySetMember() both work against the dictionary AND the internal instance to retrieve and set properties. This means that user["Name"] works against native properties of the object as does user["Name"] = "RogaDugDog".
What's your Use Case?
This is still an early prototype but I've plugged it into one of my customer's applications and so far it's working very well. The key features for me were the ability to easily extend the type with values coming from a database and exposing those values in a nice and easy to use manner. I'm also finding that using this type of object for ViewModels works very well to add custom properties to view models. I suspect there will be lots of uses for this - I've been using the extra dictionary approach to extensibility for years - using a dynamic type to make the syntax cleaner is just a bonus here.
What can you think of to use this for?
Resources
February 01, 2012 @ 2:47 am
- from Maui, Hawaii
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 IDynamicMetaObjectProvider or subclass from DynamicObject as opposed to arbitrary type casts of standard .NET types. IDynamicMetaObjectProvider 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 IDynamicMetaObjectProvider 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 IDynamicMetaObjectProvider 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 theIDynamicMetaObjectProvider 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 IDynamicMetaObjectProvider 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
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 IDynamicMetaObjectProvider. 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 IDynamicMetaObjectProvider 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.
January 13, 2012 @ 1:25 pm
- from Maui, Hawaii
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:
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.
January 11, 2012 @ 12:13 am
- from Maui, Hawaii
Opening CHM files from a non-local location is no longer supported. Conveniently there's no decent error information displayed for this error so your first thought goes that there's something wrong with your help file. Here's an overview of the issue and some solutions on how to deal with it.
Read more...
January 03, 2012 @ 6:44 pm
- from Maui, Hawaii
Looks like IE9 has a bug that won't render box-shadow CSS when the box-shadow is contained within a table that has border-collapse set. Here's what the problem is and how to work around it.
Read more...
January 02, 2012 @ 2:36 am
- from Maui, Hawaii
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...
December 23, 2011 @ 4:19 am
- from Maui, Hawaii
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...
December 15, 2011 @ 2:19 am
- from Maui, Hawaii
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...
December 10, 2011 @ 9:13 pm
- from Maui, Hawaii
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...
December 10, 2011 @ 8:24 pm
- from Maui, Hawaii
HTML5 input types are new, and as it turns out ASP.NET Webforms input controls can easily create HTML5 input elements.
Read more...
December 08, 2011 @ 3:47 am
- from Maui, Hawaii
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...
December 06, 2011 @ 5:04 am
- from Maui, Hawaii
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...