Description
Summary
This is an issue related to the RpcMarshalableAttribute which is proposed in #774 and merged in #777
According to my understanding, the IDisposable
interface is ambiguous on the receiver side when marshaling with RpcMarshalableAttribute.
Example
Let's think of a scenario that the ITest
interface should be marshaled instead of serialized.
[RpcMarshalable]
public interface ITest : IDisposable
{
/* Omitted */
}
The RPC server (sender):
public interface IServer
{
Task<ITest> GetTest();
}
And the RPC client (receiver) uses it as:
ITest test = await jsonRpc.GetTest();
/* Do something with the ITest */
test.Dispose();
Problem
RpcMarshalableAttribute
required the marshaled interface to derive from IDisposable
interface (to dispose the proxy?).
It seems to me that, the Dispose()
method called on the receiver side would cause two things to happen:
- Disposal of the generated proxy on the receiver side, causing JsonRpc on both sides mark the RPC
handle
as disposed, and the sender side removes the corresponding Object out of context. - Calling
IDisposable.Dispose()
on the original object on the sender side.
So, calling Dispose()
method on the receiver side is rather ambiguous.
Sometimes we only want to dispose the proxy on receiver side without disposing the original object on sender side. However, I don't know how to achieve this.
Probable Solution?
How about defining a dedicated interface for marshaling like this:
public interface IRpcMarshalable
{
/* This method should be overridden by the Proxy generator on receiver side */
void DisposeProxy()
{
throw new InvalidOperationException("Disposal of proxy should only be done on the receiver side.");
}
}
And require any interface to be marshaled derive from this interface:
public interface ITest : IRpcMarshalable
{
/* Omitted */
}
The IRpcMarshalable
interface has a default implementation of DisposeProxy()
, which won't be overridden on the sender side.
The receiver side proxy generate should override DisposeProxy()
method to dispose the proxy
and handle
in JsonRpc context.
With this approach, IDisposable
won't be a required interface to derive from, and the ambiguity disappears.
Remind receiver to dispose proxies
In #774 (comment) concerned about to remind the receiver to dispose the proxies
I think we should start with requiring any additional interfaces to derive from IDisposable, since that makes it more obvious to the receiver that they should dispose of these proxies. If we need to we can always remove that requirement, but adding it later would be a breaking change, so I prefer to start conservatively.
Maybe we can write an Roslyn Code Analyzer to warn that these proxies should be disposed?
If this approach is acceptable, I would be happy to implement it and create a PR.