From c239b88d50425bc98c59012f32b5611e3b2ba8b6 Mon Sep 17 00:00:00 2001
From: pomianowski <13592821+pomianowski@users.noreply.github.com>
Date: Sat, 11 May 2024 12:14:19 +0200
Subject: [PATCH] Add support for Autofac
---
.../workflows/reflection-events-cd-nuget.yaml | 6 +++
Directory.Build.props | 3 +-
Directory.Packages.props | 1 +
ReflectionEventing.sln | 12 ++++++
.../AutofacConsumerProvider.cs | 23 ++++++++++
.../AutofacEventBusBuilder.cs | 10 +++++
.../ContainerBuilderExtensions.cs | 37 ++++++++++++++++
.../ReflectionEventing.Autofac.csproj | 42 +++++++++++++++++++
src/ReflectionEventing.Demo.Wpf/App.xaml.cs | 1 +
.../ReflectionEventing.Demo.Wpf.csproj | 2 +-
.../DependencyInjectionConsumerProvider.cs | 33 +++++++++++++++
.../DependencyInjectionEventBusBuilder.cs | 35 ++++++++++++++++
.../GlobalUsings.cs | 11 +++++
...lectionEventing.DependencyInjection.csproj | 42 +++++++++++++++++++
.../ServiceCollectionExtensions.cs | 11 ++---
src/ReflectionEventing/EventBus.cs | 35 +++++++++++-----
src/ReflectionEventing/EventBusBuilder.cs | 18 +-------
src/ReflectionEventing/GlobalUsings.cs | 1 -
...ider.cs => HashedConsumerTypesProvider.cs} | 6 +--
src/ReflectionEventing/IConsumerProvider.cs | 13 +-----
.../IConsumerTypesProvider.cs | 22 ++++++++++
.../ReflectionEventing.csproj | 4 --
22 files changed, 314 insertions(+), 54 deletions(-)
create mode 100644 src/ReflectionEventing.Autofac/AutofacConsumerProvider.cs
create mode 100644 src/ReflectionEventing.Autofac/AutofacEventBusBuilder.cs
create mode 100644 src/ReflectionEventing.Autofac/ContainerBuilderExtensions.cs
create mode 100644 src/ReflectionEventing.Autofac/ReflectionEventing.Autofac.csproj
create mode 100644 src/ReflectionEventing.DependencyInjection/DependencyInjectionConsumerProvider.cs
create mode 100644 src/ReflectionEventing.DependencyInjection/DependencyInjectionEventBusBuilder.cs
create mode 100644 src/ReflectionEventing.DependencyInjection/GlobalUsings.cs
create mode 100644 src/ReflectionEventing.DependencyInjection/ReflectionEventing.DependencyInjection.csproj
rename src/{ReflectionEventing => ReflectionEventing.DependencyInjection}/ServiceCollectionExtensions.cs (74%)
rename src/ReflectionEventing/{StaticConsumerProvider.cs => HashedConsumerTypesProvider.cs} (81%)
create mode 100644 src/ReflectionEventing/IConsumerTypesProvider.cs
diff --git a/.github/workflows/reflection-events-cd-nuget.yaml b/.github/workflows/reflection-events-cd-nuget.yaml
index be2b528..102b1a8 100644
--- a/.github/workflows/reflection-events-cd-nuget.yaml
+++ b/.github/workflows/reflection-events-cd-nuget.yaml
@@ -28,5 +28,11 @@ jobs:
- name: Build
run: dotnet build src\ReflectionEventing\ReflectionEventing.csproj --configuration Release --no-restore -p:SourceLinkEnabled=true
+ - name: Build
+ run: dotnet build src\ReflectionEventing.Autofac\ReflectionEventing.Autofac.csproj --configuration Release --no-restore -p:SourceLinkEnabled=true
+
+ - name: Build
+ run: dotnet build src\ReflectionEventing.DependencyInjection\ReflectionEventing.DependencyInjection.csproj --configuration Release --no-restore -p:SourceLinkEnabled=true
+
- name: Publish the package to NuGet.org
run: nuget push **\*.nupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json'
diff --git a/Directory.Build.props b/Directory.Build.props
index 71e337c..8fad517 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -6,8 +6,7 @@
- 1.0.0
- 1.0.0
+ 2.0.0
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c24f212..4029ec4 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,5 +1,6 @@
+
diff --git a/ReflectionEventing.sln b/ReflectionEventing.sln
index 4b405f4..3624311 100644
--- a/ReflectionEventing.sln
+++ b/ReflectionEventing.sln
@@ -17,6 +17,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Autofac", "src\ReflectionEventing.Autofac\ReflectionEventing.Autofac.csproj", "{6023E50F-2B4D-4315-B85D-9E8A12746045}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.DependencyInjection", "src\ReflectionEventing.DependencyInjection\ReflectionEventing.DependencyInjection.csproj", "{09F0D6A2-3791-4173-A612-98E6EA92FCC4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -31,6 +35,14 @@ Global
{898865CB-C39C-4E69-9FF8-E96AAD7FF0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{898865CB-C39C-4E69-9FF8-E96AAD7FF0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{898865CB-C39C-4E69-9FF8-E96AAD7FF0A4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6023E50F-2B4D-4315-B85D-9E8A12746045}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6023E50F-2B4D-4315-B85D-9E8A12746045}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6023E50F-2B4D-4315-B85D-9E8A12746045}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6023E50F-2B4D-4315-B85D-9E8A12746045}.Release|Any CPU.Build.0 = Release|Any CPU
+ {09F0D6A2-3791-4173-A612-98E6EA92FCC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {09F0D6A2-3791-4173-A612-98E6EA92FCC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {09F0D6A2-3791-4173-A612-98E6EA92FCC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {09F0D6A2-3791-4173-A612-98E6EA92FCC4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/ReflectionEventing.Autofac/AutofacConsumerProvider.cs b/src/ReflectionEventing.Autofac/AutofacConsumerProvider.cs
new file mode 100644
index 0000000..83d309b
--- /dev/null
+++ b/src/ReflectionEventing.Autofac/AutofacConsumerProvider.cs
@@ -0,0 +1,23 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors.
+// All Rights Reserved.
+
+using System;
+using System.Collections.Generic;
+using Autofac;
+
+namespace ReflectionEventing.Autofac;
+
+public class AutofacConsumerProvider(ILifetimeScope lifetimeScope) : IConsumerProvider
+{
+ public IEnumerable
-
+
diff --git a/src/ReflectionEventing.DependencyInjection/DependencyInjectionConsumerProvider.cs b/src/ReflectionEventing.DependencyInjection/DependencyInjectionConsumerProvider.cs
new file mode 100644
index 0000000..ae60d8c
--- /dev/null
+++ b/src/ReflectionEventing.DependencyInjection/DependencyInjectionConsumerProvider.cs
@@ -0,0 +1,33 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors.
+// All Rights Reserved.
+
+namespace ReflectionEventing.DependencyInjection;
+
+public class DependencyInjectionConsumerProvider(IServiceProvider serviceProvider)
+ : IConsumerProvider
+{
+ public IEnumerable GetConsumerTypes(Type consumerType)
+ {
+ if (consumerType is null)
+ {
+ throw new ArgumentNullException(nameof(consumerType));
+ }
+
+ IServiceScopeFactory? scopeFactory = serviceProvider.GetService();
+
+ if (scopeFactory is null)
+ {
+ return GetConsumersFromComputedScope(consumerType);
+ }
+
+ return serviceProvider.GetServices(consumerType);
+ }
+
+ private IEnumerable GetConsumersFromComputedScope(Type consumerType)
+ {
+ using IServiceScope scope = serviceProvider.CreateScope();
+ return scope.ServiceProvider.GetServices(consumerType);
+ }
+}
diff --git a/src/ReflectionEventing.DependencyInjection/DependencyInjectionEventBusBuilder.cs b/src/ReflectionEventing.DependencyInjection/DependencyInjectionEventBusBuilder.cs
new file mode 100644
index 0000000..5ac672c
--- /dev/null
+++ b/src/ReflectionEventing.DependencyInjection/DependencyInjectionEventBusBuilder.cs
@@ -0,0 +1,35 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors.
+// All Rights Reserved.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace ReflectionEventing.DependencyInjection;
+
+public class DependencyInjectionEventBusBuilder(IServiceCollection services) : EventBusBuilder
+{
+ public override void AddConsumer(
+#if NET5_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
+#endif
+ Type consumerType
+ )
+ {
+ ServiceDescriptor? descriptor = services.FirstOrDefault(d => d.ServiceType == consumerType);
+
+ if (descriptor is null)
+ {
+ throw new InvalidOperationException(
+ "Event consumer must be registered in the service collection."
+ );
+ }
+
+ if (descriptor.Lifetime == ServiceLifetime.Transient)
+ {
+ throw new InvalidOperationException("Transient consumers are not supported.");
+ }
+
+ base.AddConsumer(consumerType);
+ }
+}
diff --git a/src/ReflectionEventing.DependencyInjection/GlobalUsings.cs b/src/ReflectionEventing.DependencyInjection/GlobalUsings.cs
new file mode 100644
index 0000000..42880a6
--- /dev/null
+++ b/src/ReflectionEventing.DependencyInjection/GlobalUsings.cs
@@ -0,0 +1,11 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors.
+// All Rights Reserved.
+
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using Microsoft.Extensions.DependencyInjection;
diff --git a/src/ReflectionEventing.DependencyInjection/ReflectionEventing.DependencyInjection.csproj b/src/ReflectionEventing.DependencyInjection/ReflectionEventing.DependencyInjection.csproj
new file mode 100644
index 0000000..c23d8b5
--- /dev/null
+++ b/src/ReflectionEventing.DependencyInjection/ReflectionEventing.DependencyInjection.csproj
@@ -0,0 +1,42 @@
+
+
+
+ ReflectionEventing.DependencyInjection
+ netstandard2.0;netstandard2.1;net462;net6.0;net8.0
+ true
+ true
+ true
+
+
+
+ true
+ true
+ Speed
+
+
+
+
+
+ System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute;
+ System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute;
+ System.Diagnostics.CodeAnalysis.MemberNotNullAttribute;
+ System.Diagnostics.CodeAnalysis.NotNullAttribute;
+ System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute;
+ System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;
+ System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute;
+ System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute;
+ System.Runtime.CompilerServices.CallerArgumentExpressionAttribute;
+ System.Runtime.CompilerServices.IsExternalInit;
+ System.Runtime.CompilerServices.SkipLocalsInitAttribute;
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ReflectionEventing/ServiceCollectionExtensions.cs b/src/ReflectionEventing.DependencyInjection/ServiceCollectionExtensions.cs
similarity index 74%
rename from src/ReflectionEventing/ServiceCollectionExtensions.cs
rename to src/ReflectionEventing.DependencyInjection/ServiceCollectionExtensions.cs
index 0c9325b..dfe04dc 100644
--- a/src/ReflectionEventing/ServiceCollectionExtensions.cs
+++ b/src/ReflectionEventing.DependencyInjection/ServiceCollectionExtensions.cs
@@ -3,7 +3,7 @@
// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors.
// All Rights Reserved.
-namespace ReflectionEventing;
+namespace ReflectionEventing.DependencyInjection;
///
/// Provides extension methods for the interface.
@@ -17,7 +17,7 @@ public static class ServiceCollectionExtensions
/// A delegate that configures the .
/// The same service collection so that multiple calls can be chained.
///
- /// This method adds a singleton service of type that uses a with the consumers from the event bus builder.
+ /// This method adds a singleton service of type that uses a with the consumers from the event bus builder.
/// It also adds a scoped service of type that uses the class.
///
public static IServiceCollection AddEventBus(
@@ -25,13 +25,14 @@ public static IServiceCollection AddEventBus(
Action configure
)
{
- EventBusBuilder builder = new(services);
+ DependencyInjectionEventBusBuilder builder = new(services);
configure(builder);
- _ = services.AddSingleton(
- new StaticConsumerProvider(builder.GetConsumers())
+ _ = services.AddSingleton(
+ new HashedConsumerTypesProvider(builder.GetConsumers())
);
+ _ = services.AddScoped();
_ = services.AddScoped();
return services;
diff --git a/src/ReflectionEventing/EventBus.cs b/src/ReflectionEventing/EventBus.cs
index 2bee933..d3d1c84 100644
--- a/src/ReflectionEventing/EventBus.cs
+++ b/src/ReflectionEventing/EventBus.cs
@@ -11,16 +11,23 @@ namespace ReflectionEventing;
///
/// This class uses a service provider to get required services and a consumer provider to get consumers for a specific event type.
///
-public class EventBus(IServiceProvider serviceProvider, IConsumerProvider consumerProvider)
- : IEventBus
+public class EventBus(
+ IConsumerProvider consumerProviders,
+ IConsumerTypesProvider consumerTypesProvider
+) : IEventBus
{
///
public void Publish(TEvent eventItem)
{
- using CancellationTokenSource cancellationSource = new();
+ Task.Run(() =>
+ {
+ using CancellationTokenSource cancellationSource = new();
- PublishAsync(eventItem, cancellationSource.Token)
- .ConfigureAwait(false)
+ PublishAsync(eventItem, cancellationSource.Token)
+ .ConfigureAwait(false)
+ .GetAwaiter()
+ .GetResult();
+ })
.GetAwaiter()
.GetResult();
}
@@ -28,11 +35,19 @@ public void Publish(TEvent eventItem)
///
public async Task PublishAsync(TEvent eventItem, CancellationToken cancellationToken)
{
- IEnumerable tasks = consumerProvider
- .GetConsumers()
- .Select(serviceProvider.GetRequiredService)
- .OfType>()
- .Select(consumer => consumer.ConsumeAsync(eventItem, cancellationToken));
+ List tasks = new();
+ IEnumerable consumerTypes = consumerTypesProvider.GetConsumerTypes();
+
+ foreach (Type consumerType in consumerTypes)
+ {
+ IEnumerable> consumerObjects = consumerProviders
+ .GetConsumerTypes(consumerType)
+ .OfType>();
+
+ tasks.AddRange(
+ consumerObjects.Select(x => x.ConsumeAsync(eventItem, cancellationToken))
+ );
+ }
await Task.WhenAll(tasks).ConfigureAwait(false);
}
diff --git a/src/ReflectionEventing/EventBusBuilder.cs b/src/ReflectionEventing/EventBusBuilder.cs
index a275db1..a211d92 100644
--- a/src/ReflectionEventing/EventBusBuilder.cs
+++ b/src/ReflectionEventing/EventBusBuilder.cs
@@ -14,7 +14,7 @@ namespace ReflectionEventing;
///
/// This class uses a dictionary of consumers where the key is the consumer type and the value is a collection of event types that the consumer can handle.
///
-public sealed class EventBusBuilder(IServiceCollection services)
+public class EventBusBuilder
{
private readonly IDictionary> consumers =
new Dictionary>();
@@ -37,27 +37,13 @@ public IDictionary> GetConsumers()
/// It then gets the interfaces of the consumer that are generic and have a generic type definition of .
/// For each of these interfaces, it gets the generic argument and adds it to the consumers dictionary.
///
- public void AddConsumer(
+ public virtual void AddConsumer(
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
Type consumerType
)
{
- ServiceDescriptor? descriptor = services.FirstOrDefault(d => d.ServiceType == consumerType);
-
- if (descriptor is null)
- {
- throw new InvalidOperationException(
- "Event consumer must be registered in the service collection."
- );
- }
-
- if (descriptor.Lifetime == ServiceLifetime.Transient)
- {
- throw new InvalidOperationException("Transient consumers are not supported.");
- }
-
IEnumerable consumerInterfaces = consumerType
.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IConsumer<>));
diff --git a/src/ReflectionEventing/GlobalUsings.cs b/src/ReflectionEventing/GlobalUsings.cs
index 42880a6..3266876 100644
--- a/src/ReflectionEventing/GlobalUsings.cs
+++ b/src/ReflectionEventing/GlobalUsings.cs
@@ -8,4 +8,3 @@
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
-global using Microsoft.Extensions.DependencyInjection;
diff --git a/src/ReflectionEventing/StaticConsumerProvider.cs b/src/ReflectionEventing/HashedConsumerTypesProvider.cs
similarity index 81%
rename from src/ReflectionEventing/StaticConsumerProvider.cs
rename to src/ReflectionEventing/HashedConsumerTypesProvider.cs
index 761db32..534d571 100644
--- a/src/ReflectionEventing/StaticConsumerProvider.cs
+++ b/src/ReflectionEventing/HashedConsumerTypesProvider.cs
@@ -12,11 +12,11 @@ namespace ReflectionEventing;
///
/// This class uses a dictionary of consumers where the key is the consumer type and the value is a collection of event types that the consumer can handle.
///
-public class StaticConsumerProvider(IDictionary> consumers)
- : IConsumerProvider
+public class HashedConsumerTypesProvider(IDictionary> consumers)
+ : IConsumerTypesProvider
{
///
- public IEnumerable GetConsumers()
+ public IEnumerable GetConsumerTypes()
{
return consumers.Where(x => x.Value.Contains(typeof(TEvent))).Select(x => x.Key);
}
diff --git a/src/ReflectionEventing/IConsumerProvider.cs b/src/ReflectionEventing/IConsumerProvider.cs
index 638ff8c..659e7fe 100644
--- a/src/ReflectionEventing/IConsumerProvider.cs
+++ b/src/ReflectionEventing/IConsumerProvider.cs
@@ -5,18 +5,7 @@
namespace ReflectionEventing;
-///
-/// Defines a contract for a provider that can supply consumers for a specific event type.
-///
-///
-/// Implementations of this interface are expected to provide a mechanism to retrieve consumers that can handle a specific event type.
-///
public interface IConsumerProvider
{
- ///
- /// Gets the consumers for the specified event type.
- ///
- /// The type of the event to get the consumers for.
- /// A collection of consumer types that can handle the specified event type.
- IEnumerable GetConsumers();
+ IEnumerable GetConsumerTypes(Type consumerType);
}
diff --git a/src/ReflectionEventing/IConsumerTypesProvider.cs b/src/ReflectionEventing/IConsumerTypesProvider.cs
new file mode 100644
index 0000000..2304da3
--- /dev/null
+++ b/src/ReflectionEventing/IConsumerTypesProvider.cs
@@ -0,0 +1,22 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors.
+// All Rights Reserved.
+
+namespace ReflectionEventing;
+
+///
+/// Defines a contract for a provider that can supply consumers for a specific event type.
+///
+///
+/// Implementations of this interface are expected to provide a mechanism to retrieve consumers that can handle a specific event type.
+///
+public interface IConsumerTypesProvider
+{
+ ///
+ /// Gets the consumers for the specified event type.
+ ///
+ /// The type of the event to get the consumers for.
+ /// A collection of consumer types that can handle the specified event type.
+ IEnumerable GetConsumerTypes();
+}
diff --git a/src/ReflectionEventing/ReflectionEventing.csproj b/src/ReflectionEventing/ReflectionEventing.csproj
index 8d6011d..7dc7642 100644
--- a/src/ReflectionEventing/ReflectionEventing.csproj
+++ b/src/ReflectionEventing/ReflectionEventing.csproj
@@ -31,8 +31,4 @@
-
-
-
-