Обтекание __TransparentProxy RemotingProxy в другой Proxy создает исключение RemotingException

Цель

У меня есть пара интерфейсов и несколько библиотек, которые предоставляют реализации для этих интерфейсов. Я хочу загрузить реализацию в новый AppDomain (чтобы я мог выгрузить dll позже) и установить реализацию в новом AppDomain, а затем использовать прокси на стороне клиента (здесь AppDomain по умолчанию), чтобы обернуть фактический объект реализации. Цель состоит в том, чтобы создать эти ClientProxy экземпляры один раз и изменяют их фактические реализации всякий раз, когда не загружается сборка реализаций в домен приложений по умолчанию.

проблема

При вызове метода для объекта ClientProxy __TransparentProxy, который получает другой ClientProxy в качестве аргумента, я получаю следующие исключения:

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

С внутренним исключением:

InvalidCastException: Object must implement IConvertible.

При передаче __TransparentProxy, полученного непосредственно от серверного домена приложения, работает ClientProxy.

Настроить

Клонируется с: https://github.com/mailgerigk/remoting

Интерфейсы:

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

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

Интерфейс Impl в _impl.dll:

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

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

Главный:

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

1 ответ

Решение

Не зная ваших рассуждений, из приведенного примера кода класс ClientProxy кажется ненужным, поскольку вы можете получить то же поведение, просто используя RealProxy непосредственно:

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 возвращает TransparentProxy к внешнему объекту.

Изменить: на основе объяснения в комментариях я добавил вторичный GetOuterTransparentProxy метод, который возвращает внешнюю базовую реализацию. Это все равно позволит OOTB RealProxy.Invoke реализация, которая будет использоваться, одновременно подвергая TransparentProxy для повторного использования.

Другие вопросы по тегам