Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping __TransparentProxy of RemotingProxy in another Proxy throws RemotingException

Goal

I have a couple of interfaces and some dlls that provide implementations for these interfaces. I want to load the implementation into a new AppDomain (so I can unload the dll later) and instatiate the implementation in the new AppDomain and then use a clientside(here the default AppDomain) proxy to wrap the actual implementation object. The goal is to create these ClientProxy instances once and change their actual implementations whenever while not loading the implementations assembly into the default AppDomain.

Problem

When calling a method on the ClientProxy __TransparentProxy object that gets another ClientProxy as argument I get the following Exceptions:

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

With inner Exception:

InvalidCastException: Object must implement IConvertible.

When passing the __TransparentProxy obtained directly from the Server AppDomain the ClientProxy works.

Setup

Cloneable from: https://github.com/mailgerigk/remoting

Interfaces:

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

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

Interface Impl in _impl.dll:

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

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

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

ClientProxy:

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

Main:

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);
    }
}
like image 919
prydain Avatar asked Jul 30 '18 14:07

prydain


1 Answers

Without knowing your reasoning, from the example code provided, the ClientProxy class appears unnecessary since you could get the same behaviour just by using a RealProxy directly:

var clientDataProxy = RemotingServices.GetRealProxy(dataImpl);

That being said, you can of course create your own concrete RealProxy and have it work as expected - you are just missing an override to get the TransparentProxy from the 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;
...

As it stands, your ClientProxy is returning a TransparentProxy to the outer object.

Edit: Based on the explanation in the comments I have added a secondary GetOuterTransparentProxy method that returns the outer base implementation. This would still allow the OOTB RealProxy.Invoke implementation to be used, while exposing the outer TransparentProxy to be reused.

like image 109
Andrew Hanlon Avatar answered Oct 04 '22 23:10

Andrew Hanlon