Skip to content

Commit dd89676

Browse files
AArnottCopilot
andauthored
Document multiple client proxy patterns (#1470)
Add comprehensive documentation and working examples for the three patterns to construct multiple client proxies over a single JSON-RPC connection: 1. Create one proxy implementing multiple interfaces at once 2. Create JsonRpc first, then call instance Attach<T> multiple times 3. Create the first proxy with static Attach<T>, extract the JsonRpc instance through IJsonRpcClientProxy, then call instance Attach<T> This addresses the common mistake of calling static Attach<T> multiple times on the same stream (which creates multiple distinct JsonRpc instances that cannot safely share one transport). Includes code samples in samples/Proxies.cs with dedicated regions for each pattern and reference documentation in docfx/docs/proxies.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2 parents 43bc390 + 6e1db4c commit dd89676

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

docfx/docs/proxies.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,28 @@ This changes the above example to something like this:
1414

1515
[!code-csharp[](../../samples/Proxies.cs#WithProxies)]
1616

17+
## Multiple proxies on one connection
18+
19+
Do not call static `JsonRpc.Attach<T>` multiple times on the same stream. Each static call creates a distinct <xref:StreamJsonRpc.JsonRpc> instance, and multiple instances cannot safely share one transport.
20+
21+
The following examples use this second RPC interface in addition to `ICalculator`.
22+
23+
[!code-csharp[](../../samples/Proxies.cs#MultipleProxiesInterfaces)]
24+
25+
When you need multiple client proxies over one connection, use one of these patterns:
26+
27+
1. Create one proxy that implements multiple interfaces at once.
28+
29+
[!code-csharp[](../../samples/Proxies.cs#MultipleProxiesPattern1)]
30+
31+
1. Create the <xref:StreamJsonRpc.JsonRpc> connection first, then call instance `Attach<T>` for each proxy.
32+
33+
[!code-csharp[](../../samples/Proxies.cs#MultipleProxiesPattern2)]
34+
35+
1. Create the first proxy with the static `Attach<T>` method, then get its <xref:StreamJsonRpc.JsonRpc> instance through <xref:StreamJsonRpc.IJsonRpcClientProxy> and create additional proxies with instance `Attach<T>` calls.
36+
37+
[!code-csharp[](../../samples/Proxies.cs#MultipleProxiesPattern3)]
38+
1739
## Proxy traits
1840

1941
Generated proxies have the following traits:

samples/Proxies.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,59 @@ internal partial interface ICalculator
2727
}
2828
#endregion
2929

30+
#region MultipleProxiesInterfaces
31+
[JsonRpcContract, GenerateShape(IncludeMethods = MethodShapeFlags.PublicInstance)]
32+
internal partial interface IAdvancedCalculator
33+
{
34+
Task<int> MultiplyAsync(int a, int b);
35+
}
36+
#endregion
37+
38+
static async Task MultipleProxiesOption1(Stream rpcStream)
39+
{
40+
#region MultipleProxiesPattern3
41+
ICalculator calculator = JsonRpc.Attach<ICalculator>(rpcStream);
42+
JsonRpc rpc = ((IJsonRpcClientProxy)calculator).JsonRpc;
43+
IAdvancedCalculator advancedCalculator = rpc.Attach<IAdvancedCalculator>();
44+
using (calculator as IDisposable)
45+
using (advancedCalculator as IDisposable)
46+
{
47+
int sum = await calculator.AddAsync(2, 5);
48+
int product = await advancedCalculator.MultiplyAsync(2, 5);
49+
}
50+
#endregion
51+
}
52+
53+
static async Task MultipleProxiesOption2(Stream rpcStream)
54+
{
55+
#region MultipleProxiesPattern2
56+
using JsonRpc rpc = JsonRpc.Attach(rpcStream);
57+
ICalculator calculator = rpc.Attach<ICalculator>();
58+
IAdvancedCalculator advancedCalculator = rpc.Attach<IAdvancedCalculator>();
59+
using (calculator as IDisposable)
60+
using (advancedCalculator as IDisposable)
61+
{
62+
int sum = await calculator.AddAsync(2, 5);
63+
int product = await advancedCalculator.MultiplyAsync(2, 5);
64+
}
65+
#endregion
66+
}
67+
68+
static async Task MultipleProxiesOption3(Stream rpcStream)
69+
{
70+
#region MultipleProxiesPattern1
71+
using JsonRpc rpc = JsonRpc.Attach(rpcStream);
72+
object combinedProxy = rpc.Attach([typeof(ICalculator), typeof(IAdvancedCalculator)], options: null);
73+
ICalculator calculator = (ICalculator)combinedProxy;
74+
IAdvancedCalculator advancedCalculator = (IAdvancedCalculator)combinedProxy;
75+
using (combinedProxy as IDisposable)
76+
{
77+
int sum = await calculator.AddAsync(2, 5);
78+
int product = await advancedCalculator.MultiplyAsync(2, 5);
79+
}
80+
#endregion
81+
}
82+
3083
partial class ServerWithoutInterface
3184
{
3285
#region ServerWithoutInterface

0 commit comments

Comments
 (0)