跨AppDomain边界代理匿名对象

时间:2011-06-23 09:35:13

标签: c# proxy razor appdomain anonymous-types

我正在努力为RazorEngine vNext提供的一个最需要的功能是支持在单独的AppDomain中加载模板程序集,以便我们可以在需要时卸载程序集。很棒的功能请求,但它具有对可以注入模板的可能模型类型引入约束的连锁效应。

当前v2.1版本的一个很好的功能是能够使用匿名类型作为模型。我们确定模板中的模型类型是匿名类型,并将基本模板设置为TemplateBase<dynamic>。运行时绑定程序为我们处理模型成员的后期绑定调用。一切都很好。

当我们在单独的AppDomain中引入对运行模板的支持时,我们现在有一个约束,即模型只能是[Serializable]的类型(通过继承MarshalByRefObject隐含) 。匿名类型不可序列化,也是private

我的想法是以某种方式在模板库中创建一个代理模型(声明为dynamic),该模型基于调用模型的调用(它将在调用域中,而不是模板运行的域) 。实质上:

模板

<h1>@Model.Name</h1>

@Model.Name的调用会执行以下操作:

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

是否有人知道或有过尝试代理对另一个dynamic中的匿名(或AppDomain对象)的调用的最佳方法的经验?

重要的是,我不是试图通过AppDomain边界推送匿名对象,这是无法做到的。

1 个答案:

答案 0 :(得分:4)

确定。假设您了解反射并创建新的AppDomain。我知道你知道怎么做...... :)

我创建了两个帮助程序类,允许您传递匿名对象。 ProxyAnonymousObjectProxyDynamicObject。您可以在第一个ProxyAnonymousObject中创建AppDomain,并在另一个ProxyDynamicObject中使用AppDomain。 (这两个对象都存在于主AppDomain库中)

[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;
    }
}

要让您在MarshalByRefObject继承的课程中发挥作用,您只需将目标dynamic object设置为new ProxyDynamicObject(model)即可。在我写的例子中,我打这样的电话。

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);

我写了一篇关于它的博客文章http://buildstarted.com/2011/06/28/getting-anonymous-types-to-cross-the-appdomain-boundary/,以便更详细地解释一下。 (虽然这是我不太擅长的东西)

这种实现肯定存在问题。它不支持嵌套的匿名类型,我相当肯定它会破坏。但这绝对能让你走上正轨。