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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With