---
title: "Writing your first IceRPC + Slice client in C#"
description: "Learn how to write your first IceRPC + Slice client."
url: https://docs.icerpc.dev/getting-started/icerpc-slice-tutorial/client-tutorial
docs_index: https://docs.icerpc.dev/llms.txt
---

> For an index of every page, see [llms.txt](https://docs.icerpc.dev/llms.txt).

# Writing your first IceRPC + Slice client in C#

Learn how to write your first IceRPC + Slice client.

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](https://docs.icerpc.dev/getting-started/icerpc-slice-tutorial/server-tutorial.md). This part shows how to create the client.

## Create the client

```shell
dotnet new icerpc-slice-client -o MySliceClient
```

This command creates a new IceRPC client application in directory `MySliceClient`.

![MySliceClient in Visual Studio Code](https://docs.icerpc.dev/images/MySliceClient.png)

Let's examine each file:

### slice/VisitorCenter/Greeter.slice - the contract

This file is (and must be) identical or almost identical to the `Greeter.slice` we used for the server:

```slice
[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:

```csharp
// 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 the `icerpc` protocol to `localhost` on the default port for `icerpc` (4062)
- we don't specify a transport so we use the default multiplexed transport (`quic`)
- Setting `clientAuthenticationOptions` is required with `quic`

> **Note**
>
> Creating the connection instance does not establish the connection.

Then, the client program creates an invocation pipeline that flows into the connection:

```csharp
Pipeline pipeline = new Pipeline()
    .UseLogger(loggerFactory)
    .UseDeadline(defaultTimeout: TimeSpan.FromSeconds(10))
    .Into(connection);
```

When we send a request, the request goes through the [Logger](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.Logger.html) interceptor and the [Deadline](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.Deadline.html) interceptor before being sent over the connection. The `Deadline` interceptor we install here ensures the request times out after 10 seconds without a response.

> **Note**
>
> 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:

```csharp
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](https://docs.icerpc.dev/icerpc/invocation/service-address.md)) 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:

```csharp
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:

```csharp
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](https://www.nuget.org/packages/IceRpc.Slice) - the IceRPC + Slice integration package
- [IceRpc.Slice.Tools](https://www.nuget.org/packages/IceRpc.Slice.Tools) - the package that compiles `Greeter.slice` into `generated/Greeter.cs` and `generated/Greeter.IceRpc.cs`
- [IceRpc.Deadline](https://www.nuget.org/packages/IceRpc.Deadline) and [IceRpc.Logger](https://www.nuget.org/packages/IceRpc.Logger) - the packages with the two interceptors we installed in our invocation pipeline

## Run the full application

### Start the server

```shell
cd MySliceServer
dotnet run
```

The server is now listening for new connections from clients.

### Start the client

```shell
cd MySliceClient
dotnet run
```

The 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 Ok
Hello, jose!
```

### Shutdown the server

Press Ctrl+C on the server console to shut it down.
