Skip to content

Commit e188b98

Browse files
committed
add initial docs for Stream API
1 parent 1416650 commit e188b98

File tree

5 files changed

+67
-11
lines changed

5 files changed

+67
-11
lines changed

Build.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project Sdk="Microsoft.Build.Traversal/2.0.19">
22
<ItemGroup>
3-
<ProjectReference Include="src\**\*.csproj" Exclude="examples\**\*.csproj" />
3+
<ProjectReference Include="src\**\*.csproj" Exclude="examples\**\*.csproj;docs\**\*.csproj" />
44
</ItemGroup>
55
<ItemGroup Condition="$(Packing) != 'true'">
6-
<ProjectReference Include="**\*.csproj" Exclude="examples\**\*.csproj" />
6+
<ProjectReference Include="**\*.csproj" Exclude="examples\**\*.csproj;docs\**\*.csproj" />
77
</ItemGroup>
88
</Project>

docs/docs.csproj

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<!-- this file exists only to make it easy to work with the folder in devenv -->
4+
<TargetFramework>$(DefaultTFM)</TargetFramework>
5+
</PropertyGroup>
6+
</Project>

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Build Tools ("contract-first" focus)](https://protobuf-net.github.io/protobuf-net/contract_first)
88
- [Create Proto File](createProtoFile)
99
- [Register Client Service in Startup.cs](registerClientService)
10+
- [.NET Streams](streams)
1011

1112
Other Content
1213

docs/streams.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Streams
2+
3+
gRPC has a `stream` concept that allows client-streaming, server-streaming, and full-duplex (independent bidirectional) streaming of messages. Inside
4+
protobuf-net.Grpc, this is typically exposed via the `IAsyncEnumerable<T>` API, which is an asynchronous sequence of messages of type `T`. For example,
5+
6+
```
7+
public async IAsyncEnumerable<SomeResponse> SomeDuplexMethod(IAsyncEnumerable<SomeRequest> requests)
8+
{
9+
// very basic request/response server using streaming
10+
await foreach (var req in requests)
11+
{
12+
yield return ApplySomeTransformation(req);
13+
}
14+
}
15+
```
16+
17+
This is *fine*, but .NET has another "stream", i.e. `System.IO.Stream` - a sequence of *bytes*.
18+
19+
As of 1.2.2, protobuf-net.Grpc has limited (and growing) support for `Stream` as an exchange mechanism. Currently supported scenarios:
20+
21+
- `Task<Stream> SomeMethod(/* optional single request message, optional context/cancellation */);`
22+
- `ValueTask<Stream> SomeMethod(/* optional single request message, optional context/cancellation */);`
23+
24+
For example:
25+
26+
``` c#
27+
public async Task<Stream> GetFileContents(SomeRequest request)
28+
{
29+
var localPath = await CheckAccessAndMapToLocalPath(request.Path);
30+
31+
return File.OpenRead(localPath);
32+
}
33+
```
34+
35+
This hands a `Stream` back to the library, with the library assuming control of how to transmit that, disposing the stream when done (as an implementation detail: it
36+
is sent as a `stream` of [`BytesValue`](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/wrappers.proto) messages,
37+
with bespoke marshalling). As you would expect, the client can access this data trivially:
38+
39+
``` c#
40+
await using var data = proxy.GetFileContents(request);
41+
await using var localFile = File.Create(localCachePath);
42+
await data.CopyToAsync(localFile);
43+
```
44+
45+
---
46+
47+
These are just trivial examples; more complex scenarios are possible, for example using `Pipe` on the server to allow the worker to provide
48+
data after the initial response (this will be more direct when the supported APIs are extended to include pipes directly).

protobuf-net.Grpc.sln

+10-9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ EndProject
2626
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{8D5FDB6F-6111-4604-8EDF-6A59F8B12D7E}"
2727
ProjectSection(SolutionItems) = preProject
2828
appveyor.yml = appveyor.yml
29+
Build.csproj = Build.csproj
2930
Directory.build.props = Directory.build.props
3031
Directory.Build.targets = Directory.Build.targets
3132
Directory.Packages.props = Directory.Packages.props
@@ -71,15 +72,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "managed", "managed", "{3949
7172
EndProject
7273
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "native", "native", "{BE48D1A2-CE10-4AEE-B370-ED8E373B082B}"
7374
EndProject
74-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{6065C30D-7C5D-4C54-97B4-F407B56C7A89}"
75-
ProjectSection(SolutionItems) = preProject
76-
docs\configuration.md = docs\configuration.md
77-
docs\gettingstarted.md = docs\gettingstarted.md
78-
docs\index.md = docs\index.md
79-
docs\projects.md = docs\projects.md
80-
docs\releasenotes.md = docs\releasenotes.md
81-
EndProjectSection
82-
EndProject
8375
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "examples\grpc\Client\Client.csproj", "{301A0DD5-0300-4B6B-BEE7-65DB6604DD2F}"
8476
EndProject
8577
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "examples\grpc\Server\Server.csproj", "{A5D8E4BD-7204-4CF7-A323-F2F5A44659D9}"
@@ -149,6 +141,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderSys.Portfolios.Client
149141
EndProject
150142
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderSys.Portfolios.Shared", "examples\wcf-port\TraderSys\src\TraderSys.Portfolios.Shared\TraderSys.Portfolios.Shared.csproj", "{80479131-FE55-473E-AE68-55601AD96668}"
151143
EndProject
144+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "docs", "docs\docs.csproj", "{9479D65D-A0E8-4520-99EC-93774A217EF6}"
145+
EndProject
152146
Global
153147
GlobalSection(SolutionConfigurationPlatforms) = preSolution
154148
Debug|Any CPU = Debug|Any CPU
@@ -402,6 +396,12 @@ Global
402396
{80479131-FE55-473E-AE68-55601AD96668}.Release|Any CPU.Build.0 = Release|Any CPU
403397
{80479131-FE55-473E-AE68-55601AD96668}.VS|Any CPU.ActiveCfg = Debug|Any CPU
404398
{80479131-FE55-473E-AE68-55601AD96668}.VS|Any CPU.Build.0 = Debug|Any CPU
399+
{9479D65D-A0E8-4520-99EC-93774A217EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
400+
{9479D65D-A0E8-4520-99EC-93774A217EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
401+
{9479D65D-A0E8-4520-99EC-93774A217EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
402+
{9479D65D-A0E8-4520-99EC-93774A217EF6}.Release|Any CPU.Build.0 = Release|Any CPU
403+
{9479D65D-A0E8-4520-99EC-93774A217EF6}.VS|Any CPU.ActiveCfg = Debug|Any CPU
404+
{9479D65D-A0E8-4520-99EC-93774A217EF6}.VS|Any CPU.Build.0 = Debug|Any CPU
405405
EndGlobalSection
406406
GlobalSection(SolutionProperties) = preSolution
407407
HideSolutionNode = FALSE
@@ -462,6 +462,7 @@ Global
462462
{87D27844-3A62-431A-9B53-92AFC09A1C3C} = {2B3F5AED-24B3-4915-900E-EFC58414EA29}
463463
{AC65761B-5CF5-4F3E-AF60-41F977E8070D} = {2B3F5AED-24B3-4915-900E-EFC58414EA29}
464464
{80479131-FE55-473E-AE68-55601AD96668} = {2B3F5AED-24B3-4915-900E-EFC58414EA29}
465+
{9479D65D-A0E8-4520-99EC-93774A217EF6} = {8D5FDB6F-6111-4604-8EDF-6A59F8B12D7E}
465466
EndGlobalSection
466467
GlobalSection(ExtensibilityGlobals) = postSolution
467468
SolutionGuid = {BA14B07C-CA29-430D-A600-F37A050636D3}

0 commit comments

Comments
 (0)