diff --git a/src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md b/src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md index 00a77abd..014bf72a 100644 --- a/src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md +++ b/src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md @@ -70,24 +70,29 @@ Note, this means the idempotency is weaker as only the last message ID is checke ### Custom blob naming -By default, blob names are generated using `GetBlobName(string id)` which creates names in the format `{id}/{T}.json`, where `id` defaults to the stream ID from `context.Stream.GetId()`. +By default, blob names are generated using `GetBlobName(string id)` which creates names in the format `{id}/{StateType}.json`, where `id` defaults to the stream ID from `context.Stream.GetId()`. You can customize blob naming in two ways: **1. Override the virtual methods globally for all events:** ```csharp -protected override string GetBlobName(string id, IMessageConsumeContext context) { - // Use stream name and type in the path - var streamName = context.Stream.ToString(); - return $"projections/{streamName}/{id}.json"; -} +public class BookingProjection : BlobStorageProjector { + // ... -protected override string GetBlobName(string id) { - return $"{id}/{typeof(T).Name}.json"; + protected override string GetBlobName(string id) => $"bookings/{id}.json"; } ``` +When the blob name depends on the event, override the overload that takes the consume context instead: + +```csharp +protected override string GetBlobName(string id, IMessageConsumeContext context) + => $"projections/{context.Stream}/{id}.json"; +``` + +The default implementation of the two-argument overload calls the one-argument overload, so overriding the two-argument version replaces the naming completely — a one-argument override is then never called. + **2. Override blob ID per event handler using `getBlobId`:** ```csharp diff --git a/src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/BlobStorageProjectorTests.cs b/src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/BlobStorageProjectorTests.cs index 0dad5738..54269184 100644 --- a/src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/BlobStorageProjectorTests.cs +++ b/src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/BlobStorageProjectorTests.cs @@ -37,7 +37,8 @@ BlobContainerClient GetContainer(string containerName) => async Task SetupExistingBlob(string containerName, string blobName, TState initialState) { var blobClient = GetContainer(containerName).GetBlobClient(blobName); var json = JsonSerializer.SerializeToUtf8Bytes(initialState); - await blobClient.UploadAsync(new MemoryStream(json), overwrite: true); + using var stream = new MemoryStream(json); + await blobClient.UploadAsync(stream, overwrite: true); } /// @@ -71,7 +72,8 @@ Func OverwriteBlob(string containerName, string blobName) => async () => { var modifiedState = new ConcurrentState { Value = 999 }; var modifiedJson = JsonSerializer.SerializeToUtf8Bytes(modifiedState); var blobClient = GetContainer(containerName).GetBlobClient(blobName); - await blobClient.UploadAsync(new MemoryStream(modifiedJson), overwrite: true); + using var stream = new MemoryStream(modifiedJson); + await blobClient.UploadAsync(stream, overwrite: true); }; // ========== SYNC STATE HANDLER TESTS ========== diff --git a/src/Experimental/src/Eventuous.ElasticSearch/Store/ElasticSerializer.cs b/src/Experimental/src/Eventuous.ElasticSearch/Store/ElasticSerializer.cs index 10a96d47..9bbbde03 100644 --- a/src/Experimental/src/Eventuous.ElasticSearch/Store/ElasticSerializer.cs +++ b/src/Experimental/src/Eventuous.ElasticSearch/Store/ElasticSerializer.cs @@ -11,8 +11,9 @@ public class ElasticSerializer(IElasticsearchSerializer builtIn, JsonSerializerO readonly ITypeMapper _typeMapper = typeMapper ?? TypeMap.Instance; public object Deserialize(Type type, Stream stream) { - var reader = new BinaryReader(stream); - var obj = JsonSerializer.Deserialize(reader.ReadBytes((int)stream.Length), type, _options); + // Read the stream directly: a BinaryReader here would either close the caller's stream when + // disposed, or leak its buffers when not, and it only added a full copy of the payload + var obj = JsonSerializer.Deserialize(stream, type, _options); if (type != typeof(PersistedEvent)) return obj!; @@ -31,7 +32,8 @@ public void Serialize(T data, Stream stream, SerializationFormatting formatti return; } - var writer = new Utf8JsonWriter(stream); + // Disposing the writer returns its pooled buffers and flushes; it doesn't close the caller's stream + using var writer = new Utf8JsonWriter(stream); JsonSerializer.Serialize(writer, data, _options); }