From 82ce32030d02220b10476d8b2dfaf1a6f5b27d92 Mon Sep 17 00:00:00 2001
From: Oskar Dudycz <oskar.dudycz@gmail.com>
Date: Thu, 1 Dec 2022 19:06:16 +0100
Subject: [PATCH] Configured static analysis and applied ConfigureAwait(false)
 to core classes Fixes #130

---
 .editorconfig                                 | 38 +++++++++++++++++++
 Core.Build.props                              | 12 ++++++
 Core.ElasticSearch/Core.ElasticSearch.csproj  | 13 ++++---
 .../Projections/ElasticSearchProjection.cs    |  4 +-
 .../Repository/ElasticSearchRepository.cs     |  2 +-
 Core.EventStoreDB/Core.EventStoreDB.csproj    | 15 ++++----
 .../Events/AggregateStreamExtensions.cs       |  2 +-
 .../Repository/EventStoreDBRepository.cs      |  6 +--
 .../Repository/RepositoryExtensions.cs        |  6 +--
 ...StoreDBSubscriptionCheckpointRepository.cs | 10 ++---
 .../EventStoreDBSubscriptionToAll.cs          | 13 ++++---
 Core.Kafka/Consumers/KafkaConsumer.cs         |  6 +--
 Core.Kafka/Core.Kafka.csproj                  | 11 +++---
 Core.Kafka/Producers/KafkaProducer.cs         |  2 +-
 Core.Marten/Core.Marten.csproj                | 15 ++++----
 .../MartenExternalProjection.cs               |  4 +-
 Core.Marten/Repository/MartenRepository.cs    |  8 ++--
 .../Repository/RepositoryExtensions.cs        |  6 +--
 .../Subscriptions/MartenEventPublisher.cs     |  5 ++-
 .../Subscriptions/MartenSubscription.cs       |  2 +-
 Core.Serialization/Core.Serialization.csproj  |  3 +-
 Core.Testing/Core.Testing.csproj              |  2 +
 Core.Testing/EventListener.cs                 |  2 +-
 Core.Testing/TestWebApplicationFactory.cs     |  4 +-
 Core.WebApi/Core.WebApi.csproj                |  2 +
 .../ExceptionHandlingMiddleware.cs            |  4 +-
 .../OptimisticConcurrencyMiddleware.cs        |  2 +-
 Core/BackgroundWorkers/BackgroundWorker.cs    |  2 +-
 Core/Core.csproj                              | 28 +++++++-------
 Core/Events/AppendScope.cs                    |  2 +-
 Core/Events/EventBus.cs                       |  4 +-
 Core/Events/External/IExternaEventProducer.cs |  4 +-
 Core/OpenTelemetry/ActivityScope.cs           |  4 +-
 Dockerfile                                    |  2 +
 EventSourcing.NetCore.sln                     |  1 +
 35 files changed, 157 insertions(+), 89 deletions(-)
 create mode 100644 Core.Build.props

diff --git a/.editorconfig b/.editorconfig
index c15480623..f3d99c004 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -156,3 +156,41 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
 # Wrapping preferences
 csharp_preserve_single_line_statements = false
 csharp_preserve_single_line_blocks = true
+
+
+###############################
+# Reliability Inspections     #
+###############################
+
+# CA2012: Use ValueTasks correctly
+dotnet_diagnostic.CA2012.severity = error
+
+# VSTHRD002 Avoid problematic synchronous waits
+dotnet_diagnostic.VSTHRD002.severity = warning
+
+# VSTHRD011 Use AsyncLazy<T>
+dotnet_diagnostic.VSTHRD011.severity = warning
+
+# VSTHRD100 Avoid async void methods
+dotnet_diagnostic.VSTHRD100.severity = error
+
+# VSTHRD101 Avoid unsupported async delegates
+dotnet_diagnostic.VSTHRD101.severity = error
+
+# VSTHRD102 Implement internal logic asynchronously
+dotnet_diagnostic.VSTHRD102.severity = error
+
+# VSTHRD103 Call async methods when in an async method
+dotnet_diagnostic.VSTHRD103.severity = error
+
+# VSTHRD110 Observe result of async calls
+dotnet_diagnostic.VSTHRD110.severity = warning
+
+# VSTHRD111 Use .ConfigureAwait(bool)
+dotnet_diagnostic.VSTHRD111.severity = error
+
+# VSTHRD112 Implement System.IAsyncDisposable
+dotnet_diagnostic.VSTHRD112.severity = error
+
+# VSTHRD200 Use Async suffix for async methods
+dotnet_diagnostic.VSTHRD200.severity = none
diff --git a/Core.Build.props b/Core.Build.props
new file mode 100644
index 000000000..918e2af55
--- /dev/null
+++ b/Core.Build.props
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project>
+    <PropertyGroup>
+        <AnalysisModeReliability>true</AnalysisModeReliability>
+        <EnableNETAnalyzers>true</EnableNETAnalyzers>
+        <AnalysisLevel>latest</AnalysisLevel>
+    </PropertyGroup>
+    <ItemGroup>
+        <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.4.27" PrivateAssets="All"/>
+        <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0" PrivateAssets="All" Condition=" '$(TargetFrawework)' == 'netstandard2.0' "/>
+    </ItemGroup>
+</Project>
diff --git a/Core.ElasticSearch/Core.ElasticSearch.csproj b/Core.ElasticSearch/Core.ElasticSearch.csproj
index d2aa97cee..e38257362 100644
--- a/Core.ElasticSearch/Core.ElasticSearch.csproj
+++ b/Core.ElasticSearch/Core.ElasticSearch.csproj
@@ -6,15 +6,16 @@
 
 
     <ItemGroup>
-        <PackageReference Include="NEST" Version="7.17.5" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="NEST" Version="7.17.5"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Core\Core.csproj" />
+        <ProjectReference Include="..\Core\Core.csproj"/>
     </ItemGroup>
 
+    <Import Project="../Core.Build.props"/>
 </Project>
diff --git a/Core.ElasticSearch/Projections/ElasticSearchProjection.cs b/Core.ElasticSearch/Projections/ElasticSearchProjection.cs
index 1594e8c35..70e81c9c0 100644
--- a/Core.ElasticSearch/Projections/ElasticSearchProjection.cs
+++ b/Core.ElasticSearch/Projections/ElasticSearchProjection.cs
@@ -28,7 +28,7 @@ public async Task Handle(EventEnvelope<TEvent> eventEnvelope, CancellationToken
         var id = getId(eventEnvelope.Data);
         var indexName = IndexNameMapper.ToIndexName<TView>();
 
-        var entity = (await elasticClient.GetAsync<TView>(id, i => i.Index(indexName), ct))?.Source ??
+        var entity = (await elasticClient.GetAsync<TView>(id, i => i.Index(indexName), ct).ConfigureAwait(false))?.Source ??
                      (TView) Activator.CreateInstance(typeof(TView), true)!;
 
         entity.When(eventEnvelope);
@@ -37,7 +37,7 @@ await elasticClient.IndexAsync(
             entity,
             i => i.Index(indexName).Id(id).VersionType(VersionType.External).Version((long)eventEnvelope.Metadata.StreamPosition),
             ct
-        );
+        ).ConfigureAwait(false);
     }
 }
 
diff --git a/Core.ElasticSearch/Repository/ElasticSearchRepository.cs b/Core.ElasticSearch/Repository/ElasticSearchRepository.cs
index d5c275815..d5d09251a 100644
--- a/Core.ElasticSearch/Repository/ElasticSearchRepository.cs
+++ b/Core.ElasticSearch/Repository/ElasticSearchRepository.cs
@@ -26,7 +26,7 @@ IElasticClient elasticClient
 
     public async Task<T?> Find(Guid id, CancellationToken cancellationToken)
     {
-        var response = await elasticClient.GetAsync<T>(id, ct: cancellationToken);
+        var response = await elasticClient.GetAsync<T>(id, ct: cancellationToken).ConfigureAwait(false);
         return response?.Source;
     }
 
diff --git a/Core.EventStoreDB/Core.EventStoreDB.csproj b/Core.EventStoreDB/Core.EventStoreDB.csproj
index 23c12dbbf..66f4b6520 100644
--- a/Core.EventStoreDB/Core.EventStoreDB.csproj
+++ b/Core.EventStoreDB/Core.EventStoreDB.csproj
@@ -6,16 +6,17 @@
 
 
     <ItemGroup>
-        <PackageReference Include="EventStore.Client.Grpc.Streams" Version="22.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
-        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="EventStore.Client.Grpc.Streams" Version="22.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/>
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Core\Core.csproj" />
+        <ProjectReference Include="..\Core\Core.csproj"/>
     </ItemGroup>
 
+    <Import Project="../Core.Build.props"/>
 </Project>
diff --git a/Core.EventStoreDB/Events/AggregateStreamExtensions.cs b/Core.EventStoreDB/Events/AggregateStreamExtensions.cs
index 4a8e148aa..9c4987677 100644
--- a/Core.EventStoreDB/Events/AggregateStreamExtensions.cs
+++ b/Core.EventStoreDB/Events/AggregateStreamExtensions.cs
@@ -22,7 +22,7 @@ public static class AggregateStreamExtensions
             cancellationToken: cancellationToken
         );
 
-        if (await readResult.ReadState == ReadState.StreamNotFound)
+        if (await readResult.ReadState.ConfigureAwait(false) == ReadState.StreamNotFound)
             return null;
 
         var aggregate = (T)Activator.CreateInstance(typeof(T), true)!;
diff --git a/Core.EventStoreDB/Repository/EventStoreDBRepository.cs b/Core.EventStoreDB/Repository/EventStoreDBRepository.cs
index ab48561fa..ee813ec9f 100644
--- a/Core.EventStoreDB/Repository/EventStoreDBRepository.cs
+++ b/Core.EventStoreDB/Repository/EventStoreDBRepository.cs
@@ -45,7 +45,7 @@ public Task<ulong> Add(T aggregate, CancellationToken token = default) =>
                     StreamState.NoStream,
                     GetEventsToStore(aggregate, TelemetryPropagator.GetPropagationContext(activity)),
                     cancellationToken: ct
-                );
+                ).ConfigureAwait(false);
                 return result.NextExpectedStreamRevision.ToUInt64();
             },
             token
@@ -63,7 +63,7 @@ public Task<ulong> Update(T aggregate, ulong? expectedRevision = null, Cancellat
                     nextVersion,
                     eventsToAppend,
                     cancellationToken: ct
-                );
+                ).ConfigureAwait(false);
                 return result.NextExpectedStreamRevision.ToUInt64();
             },
             token
@@ -81,7 +81,7 @@ public Task<ulong> Delete(T aggregate, ulong? expectedRevision = null, Cancellat
                     nextVersion,
                     eventsToAppend,
                     cancellationToken: ct
-                );
+                ).ConfigureAwait(false);
                 return result.NextExpectedStreamRevision.ToUInt64();
             },
             token
diff --git a/Core.EventStoreDB/Repository/RepositoryExtensions.cs b/Core.EventStoreDB/Repository/RepositoryExtensions.cs
index 2027f45d5..bcd413aef 100644
--- a/Core.EventStoreDB/Repository/RepositoryExtensions.cs
+++ b/Core.EventStoreDB/Repository/RepositoryExtensions.cs
@@ -11,7 +11,7 @@ public static async Task<T> Get<T>(
         CancellationToken ct
     ) where T : class, IAggregate
     {
-        var entity = await repository.Find(id, ct);
+        var entity = await repository.Find(id, ct).ConfigureAwait(false);
 
         return entity ?? throw AggregateNotFoundException.For<T>(id);
     }
@@ -24,10 +24,10 @@ public static async Task<ulong> GetAndUpdate<T>(
         CancellationToken ct = default
     ) where T : class, IAggregate
     {
-        var entity = await repository.Get(id, ct);
+        var entity = await repository.Get(id, ct).ConfigureAwait(false);
 
         action(entity);
 
-        return await repository.Update(entity, expectedVersion, ct);
+        return await repository.Update(entity, expectedVersion, ct).ConfigureAwait(false);
     }
 }
diff --git a/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionCheckpointRepository.cs b/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionCheckpointRepository.cs
index 8a3b49181..dcc516470 100644
--- a/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionCheckpointRepository.cs
+++ b/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionCheckpointRepository.cs
@@ -22,12 +22,12 @@ public EventStoreDBSubscriptionCheckpointRepository(
         var result = eventStoreClient.ReadStreamAsync(Direction.Backwards, streamName, StreamPosition.End, 1,
             cancellationToken: ct);
 
-        if (await result.ReadState == ReadState.StreamNotFound)
+        if (await result.ReadState.ConfigureAwait(false) == ReadState.StreamNotFound)
         {
             return null;
         }
 
-        ResolvedEvent? @event = await result.FirstOrDefaultAsync(ct);
+        ResolvedEvent? @event = await result.FirstOrDefaultAsync(ct).ConfigureAwait(false);
 
         return @event?.Deserialize<CheckpointStored>()?.Position;
     }
@@ -46,7 +46,7 @@ await eventStoreClient.AppendToStreamAsync(
                 StreamState.StreamExists,
                 eventToAppend,
                 cancellationToken: ct
-            );
+            ).ConfigureAwait(false);
         }
         catch (WrongExpectedVersionException)
         {
@@ -58,7 +58,7 @@ await eventStoreClient.SetStreamMetadataAsync(
                 StreamState.NoStream,
                 new StreamMetadata(1),
                 cancellationToken: ct
-            );
+            ).ConfigureAwait(false);
 
             // append event again expecting stream to not exist
             await eventStoreClient.AppendToStreamAsync(
@@ -66,7 +66,7 @@ await eventStoreClient.AppendToStreamAsync(
                 StreamState.NoStream,
                 eventToAppend,
                 cancellationToken: ct
-            );
+            ).ConfigureAwait(false);
         }
     }
 
diff --git a/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionToAll.cs b/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionToAll.cs
index 50401cbd8..f133f0ce0 100644
--- a/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionToAll.cs
+++ b/Core.EventStoreDB/Subscriptions/EventStoreDBSubscriptionToAll.cs
@@ -60,7 +60,7 @@ public async Task SubscribeToAll(EventStoreDBSubscriptionToAllOptions subscripti
 
         logger.LogInformation("Subscription to all '{SubscriptionId}'", subscriptionOptions.SubscriptionId);
 
-        var checkpoint = await checkpointRepository.Load(SubscriptionId, ct);
+        var checkpoint = await checkpointRepository.Load(SubscriptionId, ct).ConfigureAwait(false);
 
         await eventStoreClient.SubscribeToAllAsync(
             checkpoint == null ? FromAll.Start : FromAll.After(new Position(checkpoint.Value, checkpoint.Value)),
@@ -70,7 +70,7 @@ await eventStoreClient.SubscribeToAllAsync(
             subscriptionOptions.FilterOptions,
             subscriptionOptions.Credentials,
             ct
-        );
+        ).ConfigureAwait(false);
 
         logger.LogInformation("Subscription to all '{SubscriptionId}' started", SubscriptionId);
     }
@@ -108,9 +108,10 @@ await activityScope.Run($"{nameof(EventStoreDBSubscriptionToAll)}/{nameof(Handle
                 async (_, ct) =>
                 {
                     // publish event to internal event bus
-                    await eventBus.Publish(eventEnvelope, ct);
+                    await eventBus.Publish(eventEnvelope, ct).ConfigureAwait(false);
 
-                    await checkpointRepository.Store(SubscriptionId, resolvedEvent.Event.Position.CommitPosition, ct);
+                    await checkpointRepository.Store(SubscriptionId, resolvedEvent.Event.Position.CommitPosition, ct)
+                        .ConfigureAwait(false);
                 },
                 new StartActivityOptions
                 {
@@ -119,7 +120,7 @@ await activityScope.Run($"{nameof(EventStoreDBSubscriptionToAll)}/{nameof(Handle
                     Kind = ActivityKind.Consumer
                 },
                 token
-            );
+            ).ConfigureAwait(false);
         }
         catch (Exception e)
         {
@@ -162,7 +163,9 @@ private void Resubscribe()
                 // As this is a background process then we don't need to have async context here.
                 using (NoSynchronizationContextScope.Enter())
                 {
+#pragma warning disable VSTHRD002
                     SubscribeToAll(subscriptionOptions, cancellationToken).Wait(cancellationToken);
+#pragma warning restore VSTHRD002
                 }
 
                 resubscribed = true;
diff --git a/Core.Kafka/Consumers/KafkaConsumer.cs b/Core.Kafka/Consumers/KafkaConsumer.cs
index 07acc6f4c..db71db005 100644
--- a/Core.Kafka/Consumers/KafkaConsumer.cs
+++ b/Core.Kafka/Consumers/KafkaConsumer.cs
@@ -51,7 +51,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
             while (!cancellationToken.IsCancellationRequested)
             {
                 // consume event from Kafka
-                await ConsumeNextEvent(consumer, cancellationToken);
+                await ConsumeNextEvent(consumer, cancellationToken).ConfigureAwait(false);
             }
         }
         catch (Exception e)
@@ -96,7 +96,7 @@ await activityScope.Run($"{nameof(KafkaConsumer)}/{nameof(ConsumeNextEvent)}",
                 async (_, ct) =>
                 {
                     // publish event to internal event bus
-                    await eventBus.Publish(eventEnvelope, ct);
+                    await eventBus.Publish(eventEnvelope, ct).ConfigureAwait(false);
 
                     consumer.Commit();
                 },
@@ -118,7 +118,7 @@ await activityScope.Run($"{nameof(KafkaConsumer)}/{nameof(ConsumeNextEvent)}",
                     Kind = ActivityKind.Consumer
                 },
                 token
-            );
+            ).ConfigureAwait(false);
         }
         catch (Exception e)
         {
diff --git a/Core.Kafka/Core.Kafka.csproj b/Core.Kafka/Core.Kafka.csproj
index 0fea6efa9..1a833f579 100644
--- a/Core.Kafka/Core.Kafka.csproj
+++ b/Core.Kafka/Core.Kafka.csproj
@@ -5,14 +5,15 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Confluent.Kafka" Version="1.9.3" />
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
-        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="Confluent.Kafka" Version="1.9.3"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/>
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Core\Core.csproj" />
+        <ProjectReference Include="..\Core\Core.csproj"/>
     </ItemGroup>
 
+    <Import Project="../Core.Build.props"/>
 </Project>
diff --git a/Core.Kafka/Producers/KafkaProducer.cs b/Core.Kafka/Producers/KafkaProducer.cs
index 3731b9c92..ef93ba612 100644
--- a/Core.Kafka/Producers/KafkaProducer.cs
+++ b/Core.Kafka/Producers/KafkaProducer.cs
@@ -63,7 +63,7 @@ await p.ProduceAsync(config.Topic,
                     Kind = ActivityKind.Producer
                 },
                 token
-            );
+            ).ConfigureAwait(false);
         }
         catch (Exception e)
         {
diff --git a/Core.Marten/Core.Marten.csproj b/Core.Marten/Core.Marten.csproj
index 53452ab1b..dde28e23d 100644
--- a/Core.Marten/Core.Marten.csproj
+++ b/Core.Marten/Core.Marten.csproj
@@ -5,16 +5,17 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Marten" Version="6.0.0-alpha.3" />
-        <PackageReference Include="MediatR" Version="11.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+        <PackageReference Include="Marten" Version="6.0.0-alpha.3"/>
+        <PackageReference Include="MediatR" Version="11.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Core.Serialization\Core.Serialization.csproj" />
-      <ProjectReference Include="..\Core\Core.csproj" />
+        <ProjectReference Include="..\Core.Serialization\Core.Serialization.csproj"/>
+        <ProjectReference Include="..\Core\Core.csproj"/>
     </ItemGroup>
 
+    <Import Project="../Core.Build.props"/>
 </Project>
diff --git a/Core.Marten/ExternalProjections/MartenExternalProjection.cs b/Core.Marten/ExternalProjections/MartenExternalProjection.cs
index 1c20f1461..dc96d6265 100644
--- a/Core.Marten/ExternalProjections/MartenExternalProjection.cs
+++ b/Core.Marten/ExternalProjections/MartenExternalProjection.cs
@@ -25,7 +25,7 @@ public async Task Handle(EventEnvelope<TEvent> eventEnvelope, CancellationToken
     {
         var (@event, eventMetadata) = eventEnvelope;
 
-        var entity = await session.LoadAsync<TView>(getId(@event), ct) ??
+        var entity = await session.LoadAsync<TView>(getId(@event), ct).ConfigureAwait(false) ??
                      (TView)Activator.CreateInstance(typeof(TView), true)!;
 
         var eventLogPosition = eventMetadata.LogPosition;
@@ -39,7 +39,7 @@ public async Task Handle(EventEnvelope<TEvent> eventEnvelope, CancellationToken
 
         session.Store(entity);
 
-        await session.SaveChangesAsync(ct);
+        await session.SaveChangesAsync(ct).ConfigureAwait(false);
     }
 }
 
diff --git a/Core.Marten/Repository/MartenRepository.cs b/Core.Marten/Repository/MartenRepository.cs
index 1c45d4f16..b3f3ba35e 100644
--- a/Core.Marten/Repository/MartenRepository.cs
+++ b/Core.Marten/Repository/MartenRepository.cs
@@ -47,7 +47,7 @@ public Task<long> Add(T aggregate, CancellationToken cancellationToken = default
                     events
                 );
 
-                await documentSession.SaveChangesAsync(ct);
+                await documentSession.SaveChangesAsync(ct).ConfigureAwait(false);
 
                 return (long)events.Length;
             },
@@ -71,7 +71,7 @@ public Task<long> Update(T aggregate, long? expectedVersion = null, Cancellation
                     events
                 );
 
-                await documentSession.SaveChangesAsync(ct);
+                await documentSession.SaveChangesAsync(ct).ConfigureAwait(false);
 
                 return nextVersion;
             },
@@ -95,7 +95,7 @@ public Task<long> Delete(T aggregate, long? expectedVersion = null, Cancellation
                     events
                 );
 
-                await documentSession.SaveChangesAsync(ct);
+                await documentSession.SaveChangesAsync(ct).ConfigureAwait(false);
 
                 return nextVersion;
             },
@@ -121,7 +121,7 @@ private void InjectTelemetryIntoDocumentSession(IDocumentSession session, string
         }
         catch (Exception ex)
         {
-            logger.LogError(ex, "Failed to inject trace context.");
+            logger.LogError(ex, "Failed to inject trace context");
         }
     }
 }
diff --git a/Core.Marten/Repository/RepositoryExtensions.cs b/Core.Marten/Repository/RepositoryExtensions.cs
index 8a308abbb..35c6a25e1 100644
--- a/Core.Marten/Repository/RepositoryExtensions.cs
+++ b/Core.Marten/Repository/RepositoryExtensions.cs
@@ -11,7 +11,7 @@ public static async Task<T> Get<T>(
         CancellationToken cancellationToken = default
     ) where T : class, IAggregate
     {
-        var entity = await repository.Find(id, cancellationToken);
+        var entity = await repository.Find(id, cancellationToken).ConfigureAwait(false);
 
         return entity ?? throw AggregateNotFoundException.For<T>(id);
     }
@@ -24,10 +24,10 @@ public static async Task<long> GetAndUpdate<T>(
         CancellationToken cancellationToken = default
     ) where T : class, IAggregate
     {
-        var entity = await repository.Get(id, cancellationToken);
+        var entity = await repository.Get(id, cancellationToken).ConfigureAwait(false);
 
         action(entity);
 
-        return await repository.Update(entity, expectedVersion, cancellationToken);
+        return await repository.Update(entity, expectedVersion, cancellationToken).ConfigureAwait(false);
     }
 }
diff --git a/Core.Marten/Subscriptions/MartenEventPublisher.cs b/Core.Marten/Subscriptions/MartenEventPublisher.cs
index ddb307a34..4b55cdc64 100644
--- a/Core.Marten/Subscriptions/MartenEventPublisher.cs
+++ b/Core.Marten/Subscriptions/MartenEventPublisher.cs
@@ -48,7 +48,8 @@ await activityScope.Run($"{nameof(MartenEventPublisher)}/{nameof(ConsumeAsync)}"
                         parentContext
                     );
 
-                    await eventBus.Publish(EventEnvelopeFactory.From(@event.Data, eventMetadata), ct);
+                    await eventBus.Publish(EventEnvelopeFactory.From(@event.Data, eventMetadata), ct)
+                        .ConfigureAwait(false);
                 },
                 new StartActivityOptions
                 {
@@ -56,7 +57,7 @@ await activityScope.Run($"{nameof(MartenEventPublisher)}/{nameof(ConsumeAsync)}"
                     Parent = parentContext.ActivityContext
                 },
                 cancellationToken
-            );
+            ).ConfigureAwait(false);
         }
     }
 
diff --git a/Core.Marten/Subscriptions/MartenSubscription.cs b/Core.Marten/Subscriptions/MartenSubscription.cs
index 2d4c529fa..1f6392f44 100644
--- a/Core.Marten/Subscriptions/MartenSubscription.cs
+++ b/Core.Marten/Subscriptions/MartenSubscription.cs
@@ -35,7 +35,7 @@ CancellationToken ct
         {
             foreach (var consumer in consumers)
             {
-                await consumer.ConsumeAsync(operations, streams, ct);
+                await consumer.ConsumeAsync(operations, streams, ct).ConfigureAwait(false);
             }
         }
         catch (Exception exc)
diff --git a/Core.Serialization/Core.Serialization.csproj b/Core.Serialization/Core.Serialization.csproj
index 0690875f1..4343f69a8 100644
--- a/Core.Serialization/Core.Serialization.csproj
+++ b/Core.Serialization/Core.Serialization.csproj
@@ -5,7 +5,8 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
     </ItemGroup>
 
+    <Import Project="../Core.Build.props"/>
 </Project>
diff --git a/Core.Testing/Core.Testing.csproj b/Core.Testing/Core.Testing.csproj
index 36cee6271..0484360e9 100644
--- a/Core.Testing/Core.Testing.csproj
+++ b/Core.Testing/Core.Testing.csproj
@@ -21,4 +21,6 @@
     <ItemGroup>
       <ProjectReference Include="..\Core\Core.csproj" />
     </ItemGroup>
+
+    <Import Project="../Core.Build.props" />
 </Project>
diff --git a/Core.Testing/EventListener.cs b/Core.Testing/EventListener.cs
index ab1d91fce..750dd831b 100644
--- a/Core.Testing/EventListener.cs
+++ b/Core.Testing/EventListener.cs
@@ -21,7 +21,7 @@ public EventListener(EventsLog eventsLog, IEventBus eventBus)
     public async Task Publish(IEventEnvelope eventEnvelope, CancellationToken ct)
     {
         eventsLog.PublishedEvents.Add(eventEnvelope);
-        await eventBus.Publish(eventEnvelope, ct);
+        await eventBus.Publish(eventEnvelope, ct).ConfigureAwait(false);
     }
 }
 
diff --git a/Core.Testing/TestWebApplicationFactory.cs b/Core.Testing/TestWebApplicationFactory.cs
index f74e0d167..ffc2e9a5f 100644
--- a/Core.Testing/TestWebApplicationFactory.cs
+++ b/Core.Testing/TestWebApplicationFactory.cs
@@ -52,7 +52,7 @@ public async Task PublishInternalEvent<TEvent>(EventEnvelope<TEvent> eventEnvelo
         using var scope = Services.CreateScope();
         var eventBus = scope.ServiceProvider.GetRequiredService<IEventBus>();
 
-        await eventBus.Publish(eventEnvelope, ct);
+        await eventBus.Publish(eventEnvelope, ct).ConfigureAwait(false);
     }
 
     public IReadOnlyCollection<TEvent> PublishedInternalEventsOfType<TEvent>() =>
@@ -82,7 +82,7 @@ public async Task ShouldPublishInternalEventOfType<TEvent>(
                     throw;
             }
 
-            await Task.Delay(retryIntervalInMs);
+            await Task.Delay(retryIntervalInMs).ConfigureAwait(false);
             retryCount--;
         } while (!finished);
     }
diff --git a/Core.WebApi/Core.WebApi.csproj b/Core.WebApi/Core.WebApi.csproj
index 30d9b9a52..9dc650719 100644
--- a/Core.WebApi/Core.WebApi.csproj
+++ b/Core.WebApi/Core.WebApi.csproj
@@ -13,4 +13,6 @@
       <ProjectReference Include="..\Core\Core.csproj" />
     </ItemGroup>
 
+
+    <Import Project="../Core.Build.props" />
 </Project>
diff --git a/Core.WebApi/Middlewares/ExceptionHandling/ExceptionHandlingMiddleware.cs b/Core.WebApi/Middlewares/ExceptionHandling/ExceptionHandlingMiddleware.cs
index b1079d325..26792189b 100644
--- a/Core.WebApi/Middlewares/ExceptionHandling/ExceptionHandlingMiddleware.cs
+++ b/Core.WebApi/Middlewares/ExceptionHandling/ExceptionHandlingMiddleware.cs
@@ -25,11 +25,11 @@ public async Task Invoke(HttpContext context /* other scoped dependencies */)
     {
         try
         {
-            await next(context);
+            await next(context).ConfigureAwait(false);
         }
         catch (Exception ex)
         {
-            await HandleExceptionAsync(context, ex);
+            await HandleExceptionAsync(context, ex).ConfigureAwait(false);
         }
     }
 
diff --git a/Core.WebApi/OptimisticConcurrency/OptimisticConcurrencyMiddleware.cs b/Core.WebApi/OptimisticConcurrency/OptimisticConcurrencyMiddleware.cs
index 0b23bc22f..32dda4664 100644
--- a/Core.WebApi/OptimisticConcurrency/OptimisticConcurrencyMiddleware.cs
+++ b/Core.WebApi/OptimisticConcurrency/OptimisticConcurrencyMiddleware.cs
@@ -40,7 +40,7 @@ INextResourceVersionProvider nextResourceVersionProvider
             return Task.CompletedTask;
         });
 
-        await next(context);
+        await next(context).ConfigureAwait(false);
     }
 
     private void TryGetExpectedVersionFromRequestIfMatchHeader(
diff --git a/Core/BackgroundWorkers/BackgroundWorker.cs b/Core/BackgroundWorkers/BackgroundWorker.cs
index cbc553a45..5cb997112 100644
--- a/Core/BackgroundWorkers/BackgroundWorker.cs
+++ b/Core/BackgroundWorkers/BackgroundWorker.cs
@@ -22,7 +22,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) =>
         {
             await Task.Yield();
             logger.LogInformation("Background worker started");
-            await perform(stoppingToken);
+            await perform(stoppingToken).ConfigureAwait(false);
             logger.LogInformation("Background worker stopped");
         }, stoppingToken);
 }
diff --git a/Core/Core.csproj b/Core/Core.csproj
index 6bf96e3d4..5ba811660 100644
--- a/Core/Core.csproj
+++ b/Core/Core.csproj
@@ -5,21 +5,23 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
-        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
-        <PackageReference Include="Polly" Version="7.2.3" />
-        <PackageReference Include="RestSharp" Version="108.0.2" />
-        <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9.9" />
-        <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9.9" />
-        <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.9" />
-        <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.3.1" />
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0"/>
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
+        <PackageReference Include="Polly" Version="7.2.3"/>
+        <PackageReference Include="RestSharp" Version="108.0.2"/>
+        <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9.9"/>
+        <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9.9"/>
+        <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.9"/>
+        <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.3.1"/>
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Core.Serialization\Core.Serialization.csproj" />
+        <ProjectReference Include="..\Core.Serialization\Core.Serialization.csproj"/>
     </ItemGroup>
+
+    <Import Project="../Core.Build.props"/>
 </Project>
diff --git a/Core/Events/AppendScope.cs b/Core/Events/AppendScope.cs
index 069bfb3e6..46874d858 100644
--- a/Core/Events/AppendScope.cs
+++ b/Core/Events/AppendScope.cs
@@ -21,7 +21,7 @@ Action<TVersion> setNextExpectedVersion
 
     public async Task Do(Func<TVersion?, Task<TVersion>> handler)
     {
-        var nextVersion = await handler(getExpectedVersion());
+        var nextVersion = await handler(getExpectedVersion()).ConfigureAwait(false);
 
         setNextExpectedVersion(nextVersion);
     }
diff --git a/Core/Events/EventBus.cs b/Core/Events/EventBus.cs
index 3ae3bdcc7..69d177871 100644
--- a/Core/Events/EventBus.cs
+++ b/Core/Events/EventBus.cs
@@ -51,7 +51,7 @@ await activityScope.Run(
                 (_, token) => retryPolicy.ExecuteAsync(c => eventHandler.Handle(eventEnvelope, c), token),
                 activityOptions,
                 ct
-            );
+            ).ConfigureAwait(false);
         }
 
         // publish also just event data
@@ -68,7 +68,7 @@ await activityScope.Run(
                 (_, token) => retryPolicy.ExecuteAsync(c => eventHandler.Handle(eventEnvelope.Data, c), token),
                 activityOptions,
                 ct
-            );
+            ).ConfigureAwait(false);
         }
     }
 
diff --git a/Core/Events/External/IExternaEventProducer.cs b/Core/Events/External/IExternaEventProducer.cs
index f9d25ab9d..b155496fa 100644
--- a/Core/Events/External/IExternaEventProducer.cs
+++ b/Core/Events/External/IExternaEventProducer.cs
@@ -22,11 +22,11 @@ IExternalEventProducer externalEventProducer
 
     public async Task Publish(IEventEnvelope eventEnvelope, CancellationToken ct)
     {
-        await eventBus.Publish(eventEnvelope, ct);
+        await eventBus.Publish(eventEnvelope, ct).ConfigureAwait(false);
 
         if (eventEnvelope.Data is IExternalEvent)
         {
-            await externalEventProducer.Publish(eventEnvelope, ct);
+            await externalEventProducer.Publish(eventEnvelope, ct).ConfigureAwait(false);
         }
     }
 }
diff --git a/Core/OpenTelemetry/ActivityScope.cs b/Core/OpenTelemetry/ActivityScope.cs
index 5fdda241e..72be437bf 100644
--- a/Core/OpenTelemetry/ActivityScope.cs
+++ b/Core/OpenTelemetry/ActivityScope.cs
@@ -70,7 +70,7 @@ CancellationToken ct
 
         try
         {
-            await run(activity, ct);
+            await run(activity, ct).ConfigureAwait(false);
 
             activity?.SetStatus(ActivityStatusCode.Ok);
         }
@@ -92,7 +92,7 @@ CancellationToken ct
 
         try
         {
-            var result = await run(activity, ct);
+            var result = await run(activity, ct).ConfigureAwait(false);
 
             activity?.SetStatus(ActivityStatusCode.Ok);
 
diff --git a/Dockerfile b/Dockerfile
index b355fa1a4..56820c914 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,9 @@ FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS builder
 # Setup working directory for project
 WORKDIR /app
 
+COPY ./.editorconfig ./
 COPY ./Directory.Build.props ./
+COPY ./Core.Build.props ./
 COPY ./Core/Core.csproj ./Core/
 COPY ./Core.Serialization/Core.Serialization.csproj ./Core.Serialization/
 COPY ./Core.Marten/Core.Marten.csproj ./Core.Marten/
diff --git a/EventSourcing.NetCore.sln b/EventSourcing.NetCore.sln
index dc917d097..fae6dc3ac 100644
--- a/EventSourcing.NetCore.sln
+++ b/EventSourcing.NetCore.sln
@@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		README.md = README.md
 		Directory.Build.props = Directory.Build.props
 		Dockerfile = Dockerfile
+		Core.Build.props = Core.Build.props
 	EndProjectSection
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Tests", "Core.Tests\Core.Tests.csproj", "{E1B97A7B-97C3-4C14-9BE6-ACE0AF45CE61}"