# client csharp

Source: https://apigenie.pl/techdocs/reference/cli-generate_client_csharp

Generate a C# client of an OpenAPI document, built on `HttpClient` and `System.Text.Json`, for
.NET 8 (or 9 and 10).

From an AsyncAPI document, the command generates a C# Kafka client library instead; see
[apigenie-generator client csharp (AsyncAPI)](/techdocs/reference/cli-generate_client_csharp_asyncapi).

## Usage

```sh
apigenie-generator client csharp [...OPTIONS] INPUT_FILE
```

## Description

The input must be an OpenAPI 3.0, 3.1 or 3.2 document. The output is a class library: a `.csproj`
targeting `net8.0` with nullable reference types, which references only `Microsoft.Extensions.Http`
(for the registration with dependency injection).

```csharp
services.AddPetStoreClient(options =>
{
    options.BaseUrl = new Uri("https://api.example.com/v1");
    options.ApiKey = ApiCredential.Static(configuration["PetStore:ApiKey"]!);
});

// inject PetStoreClient, or the client of a group, e.g. IPetsClient
var pets = await client.Pets.ListPetsAsync(limit: 10, cancellationToken: token);
try
{
    var pet = await client.Pets.GetPetAsync(42);
}
catch (ApiException error) when (error.StatusCode == 404)
{
    Console.WriteLine(error.Problem?.Detail);
}
```

Without dependency injection, `PetStoreClient.CreateHttpClient(options)` makes an `HttpClient` that
sends the credentials: `new PetStoreClient(PetStoreClient.CreateHttpClient(options), options)`.

The generated code has:

- **Clients.** One interface and one client per group of operations (`IPetsClient`, `PetsClient`),
  grouped by their first tag (see `grouping`), and `PetStoreClient` with a property per group. The
  clients take the `HttpClient` in their constructor, so `IHttpClientFactory` manages them;
  `AddPetStoreClient()` registers them as typed clients, with the options and the handlers sending
  the credentials. Operations marked `x-internal` are left out.
- **Methods.** An `async` method per operation, named after its operation ID (`ListPetsAsync`, see
  `action_naming`): path parameters, the other required parameters and the body first, optional
  parameters (`null` leaves them out) and a `CancellationToken` last. Parameters are sent in their
  style (form, spaceDelimited, pipeDelimited, deepObject, simple, label, matrix), objects included;
  parameters described by `content` are sent as JSON. Deprecated operations are `[Obsolete]`.
- **Bodies.** JSON (with the media type of the document, e.g. `application/merge-patch+json`), text,
  binary (`Stream`), JSON Lines uploads (`IAsyncEnumerable<T>`), and forms and multipart bodies as
  `*Form` classes, whose files are `FilePart`s (`format: binary` or `contentMediaType`) sent with the
  content type of their `encoding`.
- **Responses.** The method returns the body of the success response (`Task<Pet>`), nothing
  (`Task`), or, when statuses have different bodies, a `*Result` with the status, the headers and a
  property per status. Responses with a status of 400 or more throw an `ApiException` with the
  status, the headers, the body as text and its RFC 9457 problem details (`Problem`).
  `text/event-stream` responses are `IAsyncEnumerable<ServerSentEvent<T>>`, typed by the
  `contentSchema` of the events' data (OpenAPI 3.2 `itemSchema`, or `x-itemSchema`), JSON Lines
  responses `IAsyncEnumerable<T>`, both read as they arrive; binary responses are `BinaryResponse`s
  to dispose. Each other media type of a body or a response is a method of its own, named after it:
  `UpdatePetJsonPatchAsync`, `GetLabelPdfAsync`, `GetEventsEventStreamAsync`.
- **Servers.** Requests go to `BaseUrl`, else to the base address of the `HttpClient`, else to the
  first server of the operation, of its path or of the API, with `ServerVariables` for their
  variables.
- **Security.** A credential per security scheme in the options (`ApiCredential.Static`,
  `ApiCredential.FromProvider` for tokens that change, `ApiCredential.Basic`). A request carries the
  credentials of the first alternative of its operation's security requirements whose credentials
  are set; the `AuthorizationHandler` (HTTP schemes such as bearer and basic, OAuth 2.0, OpenID
  Connect) and `ApiKeyHandler` (headers, query parameters, cookies) delegating handlers add them.
- **Models.** Records and enums, as `apigenie-generator server aspnet` writes them: `required`/`init`
  properties, DataAnnotations for constraints (exclusive bounds of OpenAPI 3.0 and 3.1 alike),
  nullable types for `nullable`, `null` types and `oneOf` with `null`. `allOf` is merged into one
  record, or is inheritance (`[JsonPolymorphic]`/`[JsonDerivedType]`) when the base has a
  discriminator. `oneOf` and `anyOf` with a discriminator are abstract records whose variants are
  told apart by the discriminator value (explicit mapping, or the `const` of the discriminator
  property, or the schema name), read and written by generated converters wherever the
  discriminator is in the object; values with an unknown discriminator are read as the default
  mapping (`defaultMapping`, or `x-defaultMapping`). Unions whose members have one C# type
  (e.g. strings of different formats) have that type.
- **JSON.** A `System.Text.Json` source-generated `JsonSerializerContext` (`PetStoreJsonContext`)
  with every model and body type, so the clients read and write JSON without reflection. Use it to
  read error bodies: `JsonSerializer.Deserialize(error.Content!, PetStoreJsonContext.Default.Error)`.

The project also has `README.md`, `Infrastructure/` (the runtime of the clients) and
`Serialization/` (the JSON context and converters). The code builds with `-warnaserror` and the
.NET analyzers.

Not supported: callbacks and webhooks, OAuth 2.0 flows (the caller supplies access tokens),
mutual TLS (configure the client certificate on the HTTP handler), XML bodies (sent and read as
text), `prefixItems`, `patternProperties`, `if`/`then`/`else`, `dependentRequired`,
`dependentSchemas`, `unevaluatedProperties`, `multipleOf`, retries (add a
resilience handler with `configureHttpClient`) and pagination.

## Extensions

| Extension | Effect |
| --------- | ------ |
| `x-apigenie.codegen.controller-name` | on an operation or its path item: the group (client) of the operation, whatever `grouping` says |
| `x-apigenie.codegen.action-name` | name of the method of an operation, when `action_naming` lists `extension` |
| `x-apigenie.codegen.disabled` | `true` on an operation or its path item leaves the operation out, unless `skip_disabled` is `false` |
| `x-internal` | `true` on an operation leaves it out |
| `x-enum-varnames`, `x-enum-names` | names of the members of an enum, in PascalCase |
| `x-enum-descriptions`, `x-enumDescriptions` | documentation comments of the members of an enum |
| `x-defaultMapping` | `defaultMapping` of a discriminator in OpenAPI 3.0 and 3.1 documents: the schema of the values whose discriminator value isn't mapped |
| `x-itemSchema` | `itemSchema` of a media type in OpenAPI 3.0 and 3.1 documents: the type of the items of a streamed response |

The extensions can live in an overlay for this target instead of in the document, e.g.
`csharp.overlay.yaml`:

```yaml
overlay: 1.0.0
info:
  title: C# names
  version: 1.0.0
actions:
  - target: $.components.schemas.PetStatus
    update:
      x-enum-varnames: [Available, OnHold, Sold]
  - target: $.paths['/pets']
    update:
      x-apigenie:
        codegen:
          controller-name: Animals
```

```sh
apigenie-generator client csharp openapi.yaml --overlay csharp.overlay.yaml -o out
```

## Options

### -c, --config

Path to the configuration file to use. See the "Configuration" section for more information.

### --overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the "Extensions" section. Can be repeated: the overlays apply in order, after those
listed under `commands.generate.client_csharp.overlay` in the configuration file (with paths
relative to it). Warnings and errors about a value an overlay set point to the overlay.

### --strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (`OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.`).

### -o, --output

Directory where the project is generated.

### --package-name

Root namespace of the generated code, e.g. `Acme.PetStore.Client`; also the name of the project.
The API-wide types are named after its last segment (or the one before when it is `Client`,
`Clients` or `Sdk`): `PetStoreClient`, `PetStoreClientOptions`. Defaults to the title of the API
followed by `.Client`.

### --package-version

Version of the project. Defaults to the version of the API when it's a valid .NET version, or
`1.0.0`.

### -p, --property NAME=VALUE

Sets an option (see below), e.g. `-p layout=sources`; can be repeated. Options given on the
command line take precedence over the configuration file. OpenAPI documents only: using it with an
AsyncAPI document is an error.

## Configuration

The options are read from `commands.generate.client_csharp`. Its `asyncapi` section configures
the generation from AsyncAPI documents (see
[apigenie-generator client csharp (AsyncAPI)](/techdocs/reference/cli-generate_client_csharp_asyncapi#configuration)).

```yaml
version: '1.0'
commands:
  generate:
    client_csharp:
      namespace: Acme.PetStore.Client
      target_framework: net9.0
      grouping: tag
      dependency_injection: true
```

| Option | Values | Description |
| ------ | ------ | ----------- |
| `layout` | `project`, `sources` | `project` (default): a class library (.csproj, README.md and the sources); `sources`: only the generated sources, for an existing project |
| `namespace` | namespace | Root namespace of the generated code (also `--package-name`); derived from the API title by default, e.g. `PetStore.Client` |
| `target_framework` | `net8.0`, `net9.0`, `net10.0` | Target framework of the project (default: `net8.0`) |
| `grouping` | `tag`, `url` | Grouping of operations into clients: by their first tag (default), or by the first segment of their paths |
| `action_naming` | `id`, `extension`, `url` | Sources of method names, in order of preference (default: `id`, `url`) |
| `dependency_injection` | `true`, `false` | Generate the registration of the clients with `IServiceCollection` and `IHttpClientFactory` (default: `true`; it references Microsoft.Extensions.Http) |
| `skip_deprecated` | `true`, `false` | Don't generate deprecated operations (default: `false`) |
| `skip_disabled` | `true`, `false` | Don't generate operations disabled with the `x-apigenie` extension (default: `true`) |
| `model_suffix` | text | Suffix of model names, e.g. `Dto` |
| `version` | version | Version of the project (also `--package-version`); defaults to the version of the API |

Unknown options are errors.
