# Stream Send (/sdks/csharp/how-to/events-store/stream-send)



## Overview [#overview]

**Stream send** covers publishing a batch of persistent events over one long-lived connection instead of opening a new request for each message. The single-shot `SendEventStoreAsync` call is fine for one-off writes, but if you're bulk-loading history, replicating a firehose of records, or backfilling an Events Store channel, paying gRPC connection overhead once instead of per-message turns network latency into your throughput ceiling instead of an app-level bottleneck.

`CreateEventStoreStreamAsync` opens a bidirectional gRPC stream that's reused for every send. Each `stream.SendAsync(msg, clientId)` still awaits the broker's acknowledgment, returning an `EventStoreResult` with the assigned `Id` and a `Sent` boolean confirming persistence, before `CloseAsync()` half-closes the write side and drains any pending confirmations. &#x2A;*Gotchas:** because each send awaits its own confirmation, this pattern is latency-bound per call — true concurrent throughput needs multiple in-flight sends, not just a shared connection; calling `CloseAsync()` before outstanding sends complete can cut off their confirmations; and for occasional publishing, opening and tearing down a stream is pure overhead — use `SendEventStoreAsync` directly instead.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* C# SDK installed (`dotnet add package KubeMQ.SDK.CSharp`)

## Code [#code]

```csharp title="Program.cs"
// KubeMQ .NET SDK — Events Store: Stream Publish
//
// This example demonstrates high-throughput persistent event publishing via stream.
// Each send awaits server confirmation of persistence.
//
// Prerequisites:
//   - KubeMQ server running on localhost:50000
//   - dotnet run

using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.EventsStore;
using System.Text;

var options = new KubeMQClientOptions
{
    Address = "localhost:50000",
    ClientId = "csharp-eventsstore-stream-send-client",
};

await using var client = new KubeMQClient(options);
await client.ConnectAsync();

await using var stream = await client.CreateEventStoreStreamAsync();

for (int i = 0; i < 10; i++)
{
    var msg = new EventStoreMessage
    {
        Channel = "csharp-eventsstore.stream-send",
        Body = Encoding.UTF8.GetBytes($"Persistent event #{i}"),
    };
    var result = await stream.SendAsync(msg, options.ClientId!);
    Console.WriteLine($"Event {result.Id}: Sent={result.Sent}");
}

await stream.CloseAsync();

```

## How It Works [#how-it-works]

* `CreateEventStoreStreamAsync` opens a bidirectional gRPC stream specifically for the Events Store. Unlike `SendEventStoreAsync`, this reuses a single connection for all sends, reducing per-message overhead.
* Each `stream.SendAsync(msg, clientId)` awaits server acknowledgment of persistence before returning `EventStoreResult` — you get the assigned `Id` and a `Sent` boolean confirming storage.
* `CloseAsync()` half-closes the write side; pending confirmations drain before the stream is torn down.
* For high-throughput pipelines, this stream approach can achieve significantly higher message rates than individual `SendEventStoreAsync` calls.

## Related [#related]

* [Pattern overview](/learn/events-store/getting-started)
* [C# SDK Reference](/sdks/csharp/reference)
* [Persistent Pub/Sub](/sdks/csharp/tutorials/persistent-pubsub)
* [Cancel Subscription](/sdks/csharp/how-to/events-store/cancel-subscription)
