6 min read
Writing your first IceRPC + Slice client in C#
This tutorial is the second part of a two part series that shows how to create a complete application with IceRPC for C#. We start from scratch—you just need to have the .NET 10 SDK installed on your computer.
The first part of this tutorial showed how to create the server. This part shows how to create the client.
Create the client
dotnet new icerpc-slice-client -o MySliceClientThis command creates a new IceRPC client application in directory MySliceClient.

Let's examine each file:
slice/Greeter.slice - the contract
This file is (and must be) identical or almost identical to the Greeter.slice we used for the server:
[cs::identifier("MySliceClient")]module VisitorCenter
/// Represents a simple greeter.interface Greeter { /// Creates a personalized greeting. /// @param name: The name of the person to greet. /// @returns: The greeting. greet(name: string) -> string}The only difference with our server's Greeter.slice is the cs::identifier attribute. That's fine: attributes don't change the contract. Here, the C# code generator generates the C# code in namespace MySliceClient and contract-wise, it doesn't matter that the server uses a different namespace.
Program.cs - the client
The main program starts by creating a connection to the server:
// Create a simple console logger factory and configure the log level for category IceRpc.using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder .AddSimpleConsole() .AddFilter("IceRpc", LogLevel.Information));
// Path to the root CA certificate.using X509Certificate2 rootCA = X509CertificateLoader.LoadCertificateFromFile("certs/cacert.der");
// Create a client connection that logs messages to a logger with category// IceRpc.ClientConnection.await using var connection = new ClientConnection( new Uri("icerpc://localhost"), clientAuthenticationOptions: CreateClientAuthenticationOptions(rootCA), logger: loggerFactory.CreateLogger<ClientConnection>());This connection naturally matches our server configuration:
- the server address is
icerpc://localhost, meaning we connect with theicerpcprotocol tolocalhoston the default port foricerpc(4062) - we don't specify a transport so we use the default multiplexed transport (
quic) - Setting
clientAuthenticationOptionsis required withquic
Creating the connection instance does not establish the connection.
Then, the client program creates an invocation pipeline that flows into the connection:
Pipeline pipeline = new Pipeline() .UseLogger(loggerFactory) .UseDeadline(defaultTimeout: TimeSpan.FromSeconds(10)) .Into(connection);When we send a request, the request goes through the Logger interceptor and the Deadline interceptor before being sent over the connection. The Deadline interceptor we install here ensures the request times out after 10 seconds without a response.
The Deadline interceptor communicates with the Deadline middleware via a request field sent alongside each request; this allows the middleware to enforce the deadline created by the interceptor. On the other hand, the Logger interceptor and middleware are totally independent.
Next, the client program creates a Greeter proxy with this invocation pipeline:
var greeter = new GreeterProxy(pipeline);GreeterProxy is a record struct that the C# code generator generated from Slice interface Greeter. This record struct allows us to send requests to a remote service that implements Greeter.
With this code, the address of the target service (or service address) is the default for Greeter, namely icerpc:/VisitorCenter.Greeter. It matches the route we created in the server. We could also create the same proxy with an explicit service address:
var greeter = new GreeterProxy(pipeline, new Uri("icerpc:/VisitorCenter.Greeter"));Finally, the client sends a greet request, awaits the response (the greeting), prints the greeting and shuts down the connection gracefully:
string greeting = await greeter.GreetAsync(Environment.UserName);
Console.WriteLine(greeting);await connection.ShutdownAsync();When we call greeter.GreetAsync, the connection to the server is not yet established: it's the GreetAsync call that triggers the connection establishment.
Program.Authentication.cs - AuthenticationOptions helper
This file contains the CreateClientAuthenticationOptions helper method. It creates an SslClientAuthenticationOptions from the root CA.
MySliceClient.csproj - the project file
The project file is identical to the server's project file, with references to 4 separate IceRpc NuGet packages:
- IceRpc.Slice - the IceRPC + Slice integration package
- IceRpc.Slice.Tools - the package that compiles
Greeter.sliceintogenerated/Greeter.IceRpc.cs - IceRpc.Deadline and IceRpc.Logger - the packages with the two interceptors we installed in our invocation pipeline
Run the full application
Start the server
cd MySliceServerdotnet runThe server is now listening for new connections from clients.
Start the client
cd MySliceClientdotnet runThe client sends a single greet request to the service hosted by our server:
info: IceRpc.Logger.LoggerInterceptor[0] Sent request greet to icerpc:/VisitorCenter.Greeter over [::1]:59522<->[::1]:4062 and received a response with status code OkHello, jose!Shutdown the server
Press Ctrl+C on the server console to shut it down.