Proxying anonymous objects across AppDomain boundaries

One of the most requested features I'm trying to smooth out for RazorEngine vNext is support for loading template assemblies into a separate AppDomain so that we can unload assemblies when we need to. A large function request, but it has the effect of detonating the introduction of restrictions on the possible types of models that can be introduced into templates.

One of the nice features of the current version v2.1 is the ability to use anonymous types as models. We do the work of determining that the model type in the template is anonymous, and we set the base template as TemplateBase<dynamic> . Runtime binding takes care of late calls for model members for us. Everything is very nice.

When we introduce support for running templates in a separate AppDomain , we now have a restriction that models can only be of [Serializable] types (which is implied through the inheritance of MarshalByRefObject ). Anonymous types are not serializable, as well as private .

My thinking would be to somehow create a proxy model (declared as dynamic ) in a template database that sends model calls (which will be in the calling domain, and not in the domain in which the template works). Essentially:

Template

 <h1>@Model.Name</h1> 

A call to @Model.Name will do something like:

 Template.Model (ModelProxy) -> GetMember(Name) -> |BOUNDARY| -> Model.Name 

Does anyone know or have experience with the best method of trying to proxy a call to an anonymous (or dynamic object) in another AppDomain ?

It is important to note that I am not trying to skip an anonymous object using the AppDomain border, which is not possible.

+4
source share
1 answer

Ok Assuming you know about reflection and create a new AppDomain . What I know, you know how to do it ... :)

I created two helper classes that allow you to pass anonymous objects. ProxyAnonymousObject and ProxyDynamicObject . You create a ProxyAnonymousObject in the first AppDomain and use ProxyDynamicObject in another AppDomain . (both of these objects exist in the primary AppDomain library)

 [Serializable] public class ProxyAnonymousObject : ISerializable { static Dictionary<string, Type> cached = new Dictionary<string, Type>(); object model; public Dictionary<string, object> ModelProperties = new Dictionary<string, object>(); public ProxyAnonymousObject(object model) { this.model = model; } public ProxyAnonymousObject(SerializationInfo info, StreamingContext ctx) { try { string fieldName = string.Empty; object fieldValue = null; foreach (var field in info) { fieldName = field.Name; fieldValue = field.Value; if (string.IsNullOrWhiteSpace(fieldName)) continue; if (fieldValue == null) continue; ModelProperties.Add(fieldName, fieldValue); } } catch (Exception e) { var x = e; } } public void GetObjectData(SerializationInfo info, StreamingContext context) { foreach (var pi in model.GetType().GetProperties()) { info.AddValue(pi.Name, pi.GetValue(model, null), pi.PropertyType); } } } public class ProxyDynamicObject : DynamicObject{ internal ProxyAnonymousObject Proxy { get; set; } public ProxyDynamicObject(ProxyAnonymousObject model) { this.Proxy = model; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = Proxy.ModelProperties[binder.Name]; return true; } } 

To make this work in your inherited MarshalByRefObject class, you simply set the dynamic object target to new ProxyDynamicObject(model) . In the example I wrote, I make such a call.

 instance = Activator.CreateInstance(type); var setModel = type.GetMethod("SetModel", BindingFlags.Public | BindingFlags.Instance); var render = type.GetMethod("Render", BindingFlags.Public | BindingFlags.Instance); setModel.Invoke(instance, new object[] { new ProxyDynamicObject(model) }); render.Invoke(instance, null); 

I wrote a blog post about this http://buildstarted.com/2011/06/28/getting-anonymous-types-to-cross-the-appdomain-boundary/ to explain this in a bit more detail. (although I am not very good)

There are certain problems with this implementation. It does not support nested anonymous types, and I'm sure it will break at all. But it will definitely help you on the right track.

+4
source

All Articles