Skip to content

Example: SOAP client consumption

consumption.md tells a consumer how to reference, construct, call, and dispose a SOAP client proxy, and what resilience the package does and does not provide. Doc gen produces one per SOAP client package at docs/clients/<client-slug>/consumption.md: the reference style, the construction code, the endpoint configuration, the six resilience concerns, disposal, the exceptions a caller must handle, and usage drawn from real consumer source.

What reviewers should check

  • The reference style names the real mechanism (Service Reference, Web Reference, or ChannelFactory<T>), the file that declares it, the generated file, and the regeneration command. A hand edit inside generated code is called out, because the next refresh discards it.
  • The construction code is copied from source, not rewritten, and no credential value appears; only the credential type and where it comes from.
  • Endpoint configuration says where the address comes from and whether an operator can change it without a rebuild. A library project's app.config is not read at run time, and the page says the consumer must copy the sections.
  • All six resilience rows are present: timeout, retry, circuit breaking, thread safety, cancellation, and message size limits. "None configured" is a finding, not a blank, and each one is expanded in the prose below the table with what a consumer must do instead. A source column cites a file and line, or reads -.
  • Disposal records what the repository does, not what it should do. Here Dispose() calls Close() with no Abort() on a faulted channel, and the page says that a faulted channel throws from disposal and masks the original failure instead of showing the correct pattern.
  • Every exception type in Error handling for consumers carries the dagger marker, because it describes behavior inherited from the framework, and the note box follows Related documentation. A fault type local to the service, ClaimValidationFault, is named only inside the generic FaultException.
  • The empty-output row names the actual value a caller gets, here null, and says what it can mean. It must agree with the service documentation, where the service also returns null when its own lookup fails.
  • Usage examples come from real source, here the integration tests, and each cites file and line in the Notes column. An invented usage pattern is a defect.
  • Lifecycle answers agree with the rest of the page: not safe to share, so a gateway per unit of work, and disposable.
  • No HTTP vocabulary: no verb, route, or status code. A SOAP fault is a fault, and a timeout is a TimeoutException.

Example

The document below is a complete, correct client-consumption.md for a fictional SOAP client package.

View rendered example — the same document as a real page, so you can compare rendering as well as source.

---
title: Consumption
summary: How a consuming application references, constructs, calls, and disposes the proxy.
status: draft
owner: Claims Systems Team
last_reviewed: 2026-09-10
---

# Consumption

A consumer references `SAIF.Claims.Intake.Client`, copies the `ClaimIntakeBasic` client endpoint into its own configuration file, constructs a `ClaimIntakeGateway` with that endpoint name, calls `Submit` or `GetStatus`, and disposes the gateway. The gateway wraps a svcutil-generated `ClaimIntakeClient`. It applies a 30-second send timeout, performs no retry, and is not safe for concurrent use.

## Reference style

| Field | Value |
| --- | --- |
| Mechanism | Service Reference (svcutil) |
| Declared in | `src/SAIF.Claims.Intake.Client/Service References/ClaimIntakeReference/Reference.svcmap` |
| Generated artifacts | `src/SAIF.Claims.Intake.Client/Service References/ClaimIntakeReference/Reference.cs` |
| Target framework | .NET Framework 4.8 |
| Regeneration command | Visual Studio "Update Service Reference" |

`Reference.cs` has no hand edits, so a refresh does not discard any repository change.

## Construction

```csharp
public ClaimIntakeGateway(string endpointConfigurationName)
{
    _client = new ClaimIntakeClient(endpointConfigurationName);
    _client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Identification;
}
```

| Step | Requirement |
| --- | --- |
| Endpoint address | Read from the `<client><endpoint name="ClaimIntakeBasic">` element of the consuming application's configuration file, selected by the `endpointConfigurationName` constructor argument |
| Binding | Named binding configuration `ClaimIntakeBasic` |
| Credentials | Windows transport credential of the calling process, with an impersonation level of `Identification` |
| Initialization | Single-constructor |

## Endpoint configuration

| Setting | Value | Source | Changeable without a rebuild |
| --- | --- | --- | --- |
| Endpoint address | `http://claims-intake-dev.saif.example/ClaimIntake.svc` | `src/SAIF.Claims.Intake.Client/app.config` | Yes |
| Binding configuration | `ClaimIntakeBasic` | `src/SAIF.Claims.Intake.Client/app.config` | Yes |
| Security mode | `TransportCredentialOnly` (`clientCredentialType="Windows"`) | `src/SAIF.Claims.Intake.Client/app.config:14` | Yes |

A class library's own `app.config` is not read at run time. The consuming application must copy the `<client>` and `<bindings>` sections into its own configuration file, which is also where each environment sets its address.

## Resilience

| Concern | Finding | Configurable by consumer | Source | Tech debt |
| --- | --- | --- | --- | --- |
| Timeout | 30 seconds | Yes (`sendTimeout`) | `src/SAIF.Claims.Intake.Client/app.config:12` | No |
| Retry | None configured | No | - | Yes |
| Circuit breaking | None configured | No | - | Yes |
| Thread safety | Not safe - one channel is shared by every call on a gateway instance | - | `src/SAIF.Claims.Intake.Client/ClaimIntakeGateway.cs:21` | Yes |
| Cancellation | Not supported - no cancellation on any call site | - | - | No |
| Message size limits | None set - binding defaults apply | Yes (`maxReceivedMessageSize`) | - | No |

A consumer that needs retry must wrap each call in its own policy and create a new gateway for each attempt, because a faulted channel cannot be reused. A consumer that needs circuit breaking must provide it outside the package. A consumer that calls from several threads must construct one gateway per thread. The retry, circuit breaking, and thread safety findings are recorded in [Tech debt](tech-debt.md).

## Disposal

| Aspect | Finding |
| --- | --- |
| Disposal performed | `Close()` |
| Faulted channel handled | No - a faulted channel throws from disposal and masks the original failure |
| Wrapper responsibility | `ClaimIntakeGateway` disposes the proxy |
| Source | `src/SAIF.Claims.Intake.Client/ClaimIntakeGateway.cs:78` |

```csharp
public void Dispose()
{
    _client.Close();
}
```

## Error handling for consumers

| Failure | Surfaces as | Consumer action |
| --- | --- | --- |
| Service unreachable | `EndpointNotFoundException`† | Treat as transient, check the configured address, and retry with a new gateway |
| SOAP fault returned | `FaultException<ClaimValidationFault>`† for a failed validation on `SubmitClaim`, `FaultException`† for any other failure | Read `Detail.Code` and `Detail.FieldErrors` from the typed fault; treat the generic fault as a service error and do not retry blindly |
| Timeout exceeded | `TimeoutException`† | Retry with a new gateway, because the channel is faulted after a timeout |
| Response larger than the message size limit | `CommunicationException`† | Raise `maxReceivedMessageSize` in the consuming application's binding configuration |
| Empty or absent output message | `null` from `GetStatus` | Treat as "claim not found or status unavailable"; the service also returns null when its own lookup fails |

## Usage examples

```csharp
// Submit a claim, then read its status
using (var gateway = new ClaimIntakeGateway("ClaimIntakeBasic"))
{
    ClaimReceipt receipt = gateway.Submit(submission);
    ClaimStatusResult status = gateway.GetStatus(receipt.ClaimNumber);
}
```

```csharp
// Handle a validation failure
try
{
    gateway.Submit(submission);
}
catch (FaultException<ClaimValidationFault> ex)
{
    Assert.Equal("INJURY_DATE_FUTURE", ex.Detail.Code);
}
```

| Scenario | Operations used | Notes |
| --- | --- | --- |
| Submit a claim and read its status | `SubmitClaim`, `GetClaimStatus` | Taken from `tests/SAIF.Claims.Intake.Client.IntegrationTests/GatewayTests.cs:18` |
| Handle a validation failure | `SubmitClaim` | Taken from `tests/SAIF.Claims.Intake.Client.IntegrationTests/GatewayTests.cs:41` |

## Lifecycle guidance

| Question | Answer |
| --- | --- |
| Construct once or per call | Per unit of work - every integration test constructs a gateway inside a `using` block and disposes it after one or two calls |
| Disposable | Yes (`IDisposable`) |
| Safe to share across threads | No - see the Thread safety row in Resilience |

## Related documentation
- [Client index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Contract](contract.md)
- [Operation map](operation-map.md)
- [Client reference](reference.md)
- [Dependencies](dependencies.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with † were retrieved from dependency library documentation
> via the Legends CLI RAG service. They describe inherited behavior from the indicated
> origin library and should be verified if the dependency version changes.

## Sources
- `src/SAIF.Claims.Intake.Client/ClaimIntakeGateway.cs`
- `src/SAIF.Claims.Intake.Client/Service References/ClaimIntakeReference/Reference.svcmap`
- `src/SAIF.Claims.Intake.Client/Service References/ClaimIntakeReference/Reference.cs`
- `src/SAIF.Claims.Intake.Client/app.config`
- `tests/SAIF.Claims.Intake.Client.IntegrationTests/GatewayTests.cs`