0.6.0
What's Changed
0.6.0 is a major release that updates IceRPC's transport defaults and overhauls the Slice toolchain. The three biggest changes are:
QUIC is now the default multiplexed transport
QUIC is now the default transport for the icerpc protocol. You no longer need to construct and configure a transport to get a QUIC connection:
// Uses QUIC, the default transport.
await using var connection = new ClientConnection(new Uri("icerpc://localhost"));QUIC always uses TLS, so a QUIC server needs a server certificate, and a QUIC client needs the matching trust/authentication options unless the server presents a publicly trusted certificate:
using X509Certificate2 serverCertificate = X509CertificateLoader.LoadPkcs12FromFile(
"server.p12", password: null, keyStorageFlags: X509KeyStorageFlags.Exportable);
// SslServerAuthenticationOptions is a standard .NET type (System.Net.Security).
var serverAuthenticationOptions = new SslServerAuthenticationOptions
{
ServerCertificateContext = SslStreamCertificateContext.Create(
serverCertificate,
additionalCertificates: null)
};
// Uses QUIC, the default transport.
await using var server = new Server(new Chatbot(), serverAuthenticationOptions);
server.Listen();See the Greeter example for the complete client and server setup. QUIC is available wherever .NET's QUIC support is available.
You can easily use the previous default transport, Slic over TCP, by setting transport=tcp in the server address—there's no need to build a SlicClientTransport/SlicServerTransport over a TCP transport yourself:
// Uses Slic over TCP instead of the default, QUIC.
await using var connection = new ClientConnection(new Uri("icerpc://localhost?transport=tcp"));See the TcpFallback example if you want to accept both QUIC and TCP connections.
Direct support for Ice's Slice files
You can now compile Ice's Slice files (.ice files) directly and generate IceRPC integration code from them—there's no need to rewrite your Ice definitions in the new Slice syntax. Add your .ice files to your project with the SliceCompile item type, reference the new ZeroC.Ice.Slice.Tools and IceRpc.Ice packages, and you're done:
<ItemGroup>
<SliceCompile Include="Greeter.ice" IceRpc="true" />
<PackageReference Include="ZeroC.Ice.Slice.Tools"
Version="[3.8.2,3.9.0)"
PrivateAssets="All" />
<PackageReference Include="IceRpc.Ice" />
</ItemGroup>The new IceRpc.Ice package provides the integration between the IceRPC core APIs and the code generated from your .ice files, and makes it easy to communicate with Ice applications over the ice protocol. See the Ice Greeter and IceGrid examples.
The IceRpc.Templates package now includes Ice project templates, alongside the existing Slice and Protobuf templates, to help you get started quickly: icerpc-ice-client, icerpc-ice-server, icerpc-ice-di-client, and icerpc-ice-di-server (the -di- variants use Microsoft's DI container). For example:
dotnet new install IceRpc.Templates
dotnet new icerpc-ice-server -o MyServerNew Slice compiler toolchain
The Slice compiler toolchain has been re-architected. The previous all-in-one slicec-cs compiler has been replaced by the slicec compiler driving separate code generator plug-ins:
slicecparses and validates your Slice files and feeds the result to code generators.ZeroC.Slice.Generatorgenerates C# code for Slice structs and enums.IceRpc.Slice.Generatorgenerates C# code for Slice interfaces.
The code generators are now regular .NET assemblies written in C# rather than part of a monolithic Rust compiler, and the plug-in interface means a code generator can be written in any language with Slice support. See the IceRpc.Slice.Tools README for details.
Upgrade notes and breaking changes
- Default multiplexed transport: existing code should switch to QUIC or add
transport=tcpto keep using TCP (see above). - QUIC transport package removed:
IceRpc.Transports.Quicno longer exists as a separate package—QUIC support is now built into the coreIceRpcpackage. Remove any<PackageReference Include="IceRpc.Transports.Quic" />from your project. - New service attribute: the
[ProtobufService]and[SliceService]attributes were replaced by a single attribute,[Service]. Update your code accordingly. TheServiceAttributeclass is in theIceRpcnamespace. - New default access for generated code: the C# code generated from Slice definitions is now
internalby default. You can use the[cs::public]attribute to switch back topublicaccess. - Slice attribute: the
cs::namespaceattribute has been replaced bycs::identifier. - Slice modes removed: the Slice1 mode was replaced by the Ice Slice support. Remove
mode = Slice1from your Slice files and revert to the original Slice syntax in.icefiles. - Slice typed stream mapping: when you receive a typed stream—a stream return value through a proxy, or a stream parameter in a service implementation—it is now an
IceRpc.IAsyncStream<T>instead of anIAsyncEnumerable<T>.IAsyncStream<T>derives fromIAsyncEnumerable<T>(soawait foreachis unchanged) and also implementsIDisposable: it owns the underlying transport stream, so dispose it—for example with ausingdeclaration—when you don't iterate it to completion. The sending side is unchanged (IAsyncEnumerable<T>), and byte streams (stream uint8) still map toPipeReader.
The Stream example shows this change on the receiving (client) side:
// 0.5.x: the proxy returns a plain IAsyncEnumerable<int>.
IAsyncEnumerable<int> numbers = await generatorProxy.GenerateNumbersAsync();
// 0.6.0: the proxy returns an IAsyncStream<int> that owns the transport
// stream; dispose it with `using` when you don't read it to the end.
using IAsyncStream<int> numbers = await generatorProxy.GenerateNumbersAsync();- Protobuf streaming RPC mapping: the same change applies to Protobuf streaming RPCs. When you receive a stream of messages—the response of a server-streaming RPC through the generated client, or the request parameter of a client-streaming RPC in your service implementation—it is now an
IceRpc.IAsyncStream<TMessage>instead of anIAsyncEnumerable<TMessage>. As with Slice,IAsyncStream<T>derives fromIAsyncEnumerable<T>(soawait foreachis unchanged) and addsIDisposable: it owns the underlying transport stream, so dispose it—for example with ausingdeclaration—when you don't iterate it to completion. The sending side—the messages you return from a server-streaming RPC, or pass to a client-streaming RPC—still usesIAsyncEnumerable<TMessage>.
The Protobuf Stream example shows this change on the receiving (client) side:
// 0.5.x: a server-streaming RPC returns a plain IAsyncEnumerable<T>.
IAsyncEnumerable<GenerateResponse> messages = await generator.GenerateNumbersAsync(new Empty());
// 0.6.0: it returns an IAsyncStream<T> that owns the transport stream;
// dispose it with `using` when you don't read it to the end.
using IAsyncStream<GenerateResponse> messages = await generator.GenerateNumbersAsync(new Empty());Other notable changes
- Added a more convenient
Mapextension method forRouter, which does not require an explicit type. - You can now set
OutgoingRequest.IsOnewayin an interceptor; this allows you to send a one-way request independently of the IDL you use. For example, you can send a one-way request with Protobuf. - [Protobuf] Added support for Protobuf editions.