Обтекание __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
для повторного использования.