6

目标

我有几个接口和一些为这些接口提供实现的 dll。我想将实现加载到一个新的 AppDomain 中(这样我以后可以卸载 dll)并在新的 AppDomain 中实例化实现,然后使用客户端(这里是默认的 AppDomain)代理来包装实际的实现对象。目标是创建ClientProxy一次这些实例并在不将实现程序集加载到默认 AppDomain 时更改它们的实际实现。

问题

当调用 ClientProxy __TransparentProxy 对象上的方法时,该对象将另一个 ClientProxy 作为参数,我得到以下异常:

System.Runtime.Remoting.RemotingException: 'The argument type 'System.MarshalByRefObject' cannot be converted into parameter type 'IData'.'

有内部异常:

无效转换异常:Object must implement IConvertible.

当传递直接从服务器 AppDomain 获得的 __TransparentProxy 时,ClientProxy 工作。

设置

可克隆自:https ://github.com/mailgerigk/remoting

接口:

public interface IData
{
    int Foo { get; set; }
}

public interface ILogic
{
    void Update(IData data);
}

_impl.dll 中的接口 Impl:

public class DataImpl : MarshalByRefObject, IData
{
    public int Foo { get; set; }
}

public class LogicImpl : MarshalByRefObject, ILogic
{
    public void Update(IData data)
    {
        data.Foo++;
    }
}

服务器端 AssemblyLoader:

class AssemblyLoader : MarshalByRefObject
{
    public Assembly Assembly { get; private set; }

    public AssemblyLoader(string assemblyFile)
    {
        Assembly = Assembly.LoadFrom(assemblyFile);
    }

    public object CreateInstance(string typeName)
    {
        return Activator.CreateInstance(Assembly.GetType(typeName));
    }
}

客户端代理:

class ClientProxy : RealProxy
{
    private RealProxy innerProxy;

    public ClientProxy(Type interfaceType, object proxyObject)
        : base(interfaceType)
    {
        SetInnerProxy(proxyObject);
    }

    public void SetInnerProxy(object proxyObject)
    {
        innerProxy = RemotingServices.GetRealProxy(proxyObject);
    }

    public override IMessage Invoke(IMessage msg)
    {
        return innerProxy.Invoke(msg);
    }
}

主要的:

class Program
{
    static void Main(string[] args)
    {
        var app = AppDomain.CreateDomain("ImplDomain", null,
            AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.RelativeSearchPath,
            true);

        var assmblyLoader = app.CreateInstanceFromAndUnwrap(
            typeof(AssemblyLoader).Assembly.Location, typeof(AssemblyLoader).FullName,
            false, BindingFlags.CreateInstance, null,
            new object[]
            {
                "_impl.dll"
            },
            null, null) as AssemblyLoader;

        var dataImpl = assmblyLoader.CreateInstance("DataImpl") as IData;
        var logicImpl = assmblyLoader.CreateInstance("LogicImpl") as ILogic;

        logicImpl.Update(dataImpl); // Works
        Console.WriteLine(dataImpl.Foo); // prints 1

        var clientDataProxy = new ClientProxy(typeof(IData), dataImpl);
        var clientDataImpl = clientDataProxy.GetTransparentProxy() as IData;

        var clientLogicProxy = new ClientProxy(typeof(ILogic), logicImpl);
        var clientLogicImpl = clientLogicProxy.GetTransparentProxy() as ILogic;

        clientLogicImpl.Update(dataImpl); // Works
        Console.WriteLine(clientDataImpl.Foo); // prints 2

        clientLogicImpl.Update(clientDataImpl); // throws System.Runtime.Remoting.RemotingException
        Console.WriteLine(clientDataImpl.Foo);
    }
}
4

1 回答 1

2

在不知道您的推理的情况下,从提供的示例代码来看,ClientProxy 类似乎没有必要,因为您只需RealProxy直接使用 a 即可获得相同的行为:

var clientDataProxy = RemotingServices.GetRealProxy(dataImpl);

话虽这么说,您当然可以创建自己的混凝土RealProxy并让它按预期工作 - 您只是缺少从以下位置获取 TransparentProxy 的覆盖innerProxy

class ClientProxy : RealProxy
{
    ... everything else ...

    public override object GetTransparentProxy() => innerProxy.GetTransparentProxy();

    public object GetOuterTransparentProxy() => base.GetTransparentProxy();
}

...
var clientDataProxy = new ClientProxy(typeof(IData), dataImpl);
var clientDataImpl = clientDataProxy.GetOuterTransparentProxy() as IData;

var clientLogicProxy = new ClientProxy(typeof(ILogic), logicImpl);
var clientLogicImpl = clientLogicProxy.GetOuterTransparentProxy() as ILogic;
...

就目前而言,您ClientProxy正在将 a 返回TransparentProxy到外部对象。

编辑:根据评论中的解释,我添加了一个GetOuterTransparentProxy返回外部基本实现的辅助方法。这仍将允许使用 OOTBRealProxy.Invoke实现,同时公开TransparentProxy要重用的外部。

于 2018-08-09T18:01:00.593 回答