# .NET 10 C# Client Detailed Design ## Purpose Provide an idiomatic .NET 10 C# client library for MXAccess Gateway, plus a test CLI and unit tests. This client is for modern .NET callers and must not load MXAccess COM. Follow the [C# Style Guide](./style-guides/CSharpStyleGuide.md) for handwritten code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Projects Recommended layout: ```text clients/dotnet/ MxGateway.Client/ MxGateway.Client.csproj GatewayClient.cs MxGatewaySession.cs MxGatewayClientOptions.cs Authentication/ Conversion/ Errors/ Generated/ MxGateway.Client.Cli/ MxGateway.Client.Cli.csproj Program.cs Commands/ MxGateway.Client.Tests/ MxGateway.Client.Tests.csproj MxGateway.Client.IntegrationTests/ MxGateway.Client.IntegrationTests.csproj ``` Target framework: ```xml net10.0 ``` Expected packages: - `Grpc.Net.Client` - `Google.Protobuf` - `Grpc.Tools` for generation - `Microsoft.Extensions.Logging.Abstractions` - `System.CommandLine` or similar for CLI - test framework: xUnit or NUnit ## Library API Suggested public types: ```csharp public sealed class MxGatewayClient : IAsyncDisposable { public static MxGatewayClient Create(MxGatewayClientOptions options); public Task OpenSessionAsync( OpenSessionOptions? options = null, CancellationToken cancellationToken = default); public Task InvokeAsync( MxCommandRequest request, CancellationToken cancellationToken = default); } public sealed class MxGatewaySession : IAsyncDisposable { public string SessionId { get; } public Task RegisterAsync(string clientName, CancellationToken ct = default); public Task UnregisterAsync(int serverHandle, CancellationToken ct = default); public Task AddItemAsync(int serverHandle, string item, CancellationToken ct = default); public Task AddItem2Async(int serverHandle, string item, string context, CancellationToken ct = default); public Task AdviseAsync(int serverHandle, int itemHandle, CancellationToken ct = default); public Task UnAdviseAsync(int serverHandle, int itemHandle, CancellationToken ct = default); public Task WriteAsync(int serverHandle, int itemHandle, MxValue value, int userId, CancellationToken ct = default); public IAsyncEnumerable StreamEventsAsync(CancellationToken ct = default); public Task CloseAsync(CancellationToken ct = default); } ``` Generated protobuf types should remain available under a generated namespace. Handwritten wrappers should not hide raw replies. ## Options ```csharp public sealed class MxGatewayClientOptions { public required Uri Endpoint { get; init; } public required string ApiKey { get; init; } public bool UseTls { get; init; } public string? CaCertificatePath { get; init; } public string? ServerNameOverride { get; init; } public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(10); public TimeSpan DefaultCallTimeout { get; init; } = TimeSpan.FromSeconds(30); public ILoggerFactory? LoggerFactory { get; init; } } ``` API key may be loaded from `MXGATEWAY_API_KEY` by the CLI, not implicitly by the library constructor unless a helper explicitly says it does that. ## Auth Interceptor Use a gRPC call credentials/interceptor layer to attach: ```text authorization: Bearer ``` The interceptor must redact the key in logs and exceptions. ## Streaming Expose `StreamEventsAsync` as `IAsyncEnumerable`. On cancellation, cancel the gRPC stream and surface `OperationCanceledException` only when the caller initiated cancellation. Do not reorder events. ## Error Handling Recommended exceptions: ```csharp MxGatewayException MxGatewayAuthenticationException MxGatewayAuthorizationException MxGatewaySessionException MxGatewayWorkerException MxGatewayCommandException MxAccessException ``` For command replies that include MXAccess HRESULT/status, prefer returning the reply and exposing helper methods: ```csharp reply.EnsureProtocolSuccess(); reply.EnsureMxAccessSuccess(); ``` ## Test CLI Project: `MxGateway.Client.Cli`. Command examples: ```powershell mxgw-dotnet version mxgw-dotnet smoke --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --item TestChildObject.TestInt mxgw-dotnet stream-events --session-id --json mxgw-dotnet write --session-id --server-handle 1 --item-handle 1 --type int32 --value 123 ``` The CLI should use `System.CommandLine` or a similarly testable parser. JSON output should be deterministic and redact API keys. ## Unit Tests Use an in-process fake gRPC service with `Grpc.AspNetCore.Server` test host or mock the generated client behind an internal interface. Required tests: - auth metadata is attached, - API key is redacted, - options build plaintext and TLS channels correctly, - `RegisterAsync` builds the right command payload, - `AddItem2Async` includes context, - `WriteAsync` converts scalar and array values, - command reply status helpers preserve MXAccess HRESULT, - `StreamEventsAsync` yields ordered events, - stream cancellation disposes the call, - CLI parsing and JSON output. ## Integration Tests Use xUnit traits or categories. Skip unless: ```text MXGATEWAY_INTEGRATION=1 MXGATEWAY_ENDPOINT= MXGATEWAY_API_KEY= MXGATEWAY_TEST_ITEM= ``` Integration smoke should open, register, add, advise, stream for bounded time, and close.