You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1. Extend `IDisposable` (unless interface is call-scoped).
11
11
1. Not include any properties.
12
12
1. Not include any events.
13
13
14
14
The object that implements a marshalable interface may include properties and events as well as other additional members but only the methods defined by the marshalable interface will be available on the proxy, and the data will not be serialized.
15
15
16
+
The `RpcMarshalableAttribute` must be applied directly to the interface used as the return type, parameter type, or member type within a return type or parameter type's object graph.
17
+
The attribute is not inherited.
18
+
In fact different interfaces in a type hierarchy can have this attribute applied with distinct settings, and only the settings on the attribute applied directly to the interface used will apply.
19
+
20
+
## Call-scoped vs. explicitly scoped
21
+
22
+
### Explicit lifetime
23
+
24
+
An RPC marshalable interface has an explicit lifetime by default.
25
+
This means that the receiver of a marshaled object owns its lifetime, which may extend beyond an individual RPC call.
26
+
Memory for the marshaled object and its proxy are not released until the receiver either disposes of its proxy or the JSON-RPC connection is closed.
27
+
28
+
### Call-scoped lifetime
29
+
30
+
A call-scoped interface produces a proxy that is valid only during the RPC call that delivered it.
31
+
It may only be used as part of a method request as or within an argument.
32
+
Using it as or within a return value or exception will result in an error.
33
+
34
+
This is the preferred model when an interface is expected to only be used within request arguments because it mitigates the risk of a memory leak due to the receiver failing to dispose of the proxy.
35
+
This model also allows the sender to retain control over the lifetime of the marshaled object.
36
+
37
+
Special allowance is made for `IAsyncEnumerable<T>`-returning RPC methods so that the lifetime of the marshaled object is extended to the lifetime of the enumeration.
38
+
An `IAsyncEnumerable<T>` in an exception thrown from the method will *not* have access to the call-scoped marshaled object because exceptions thrown by the server always cause termination of objects marshaled by the request.
39
+
40
+
Opt into call-scoped lifetimes by setting the `CallScopedLifetime` property to `true` on the attribute applied to the interface:
41
+
42
+
```css
43
+
[RpcMarshalable(CallScopedLifetime=true)]
44
+
```
45
+
46
+
It is not possible to customize the lifetime of an RPC marshaled object except on its own interface.
47
+
For example, applying this attribute to the parameter that uses the interface is not allowed.
48
+
16
49
## Use cases
17
50
18
51
In all cases, the special handling of a marshalable object only occurs if the container of that value is typed as the corresponding marshalable interface.
@@ -104,6 +137,8 @@ class RpcServer : IRpcServer
104
137
}
105
138
```
106
139
140
+
Call-scoped marshalable interfaces may not be used as a return type or member of its object graph.
141
+
107
142
### Method argument
108
143
109
144
In this use case the RPC *client* provides the marshalable object to the server:
@@ -119,6 +154,8 @@ var counter = new Counter();
119
154
awaitclient.ProvideCounterAsync(counter);
120
155
```
121
156
157
+
Call-scoped marshalable interfaces may only appear as a method parameter or a part of its object graph.
158
+
122
159
### Value within a single argument's object graph
123
160
124
161
In this use case the RPC client again provides the marshalable object to the server,
⚠️ While this use case is supported, be very wary of this pattern because it becomes less obvious to the receiver that an `IDisposable` value is tucked into the object tree of an argument somewhere that *must* be disposed to avoid a resource leak.
184
+
This risk can be mitigated by using call-scoped marshalable interfaces.
147
185
148
186
### As an argument without a proxy for an RPC interface
Verify.Operation(this.Resultis not null,"This instance hasn't been initialized with a result yet.");
885
+
Verify.Operation(!this.resultDeserialized,"Result is no longer available or has already been deserialized.");
886
+
887
+
varresult=(JToken)this.Result;
888
+
if(result.Type==JTokenType.Null)
889
+
{
890
+
Verify.Operation(!resultType.GetTypeInfo().IsValueType||Nullable.GetUnderlyingType(resultType)is not null,"null result is not assignable to a value type.");
0 commit comments