Skip to content

Commit

Permalink
Add Castle.Windsor
Browse files Browse the repository at this point in the history
  • Loading branch information
pomianowski committed May 18, 2024
1 parent be4c339 commit 0b96617
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 6 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<ItemGroup>
<PackageVersion Include="Autofac" Version="4.0.0" />
<PackageVersion Include="Castle.Windsor" Version="6.0.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This repository houses the source code for the ReflectionEventing NuGet package.
ReflectionEventing is available as NuGet package on NuGet.org:
https://www.nuget.org/packages/ReflectionEventing
https://www.nuget.org/packages/ReflectionEventing.Autofac
https://www.nuget.org/packages/ReflectionEventing.Castle.Windsor
https://www.nuget.org/packages/ReflectionEventing.DependencyInjection
https://www.nuget.org/packages/ReflectionEventing.Ninject
https://www.nuget.org/packages/ReflectionEventing.Unity
Expand Down
21 changes: 17 additions & 4 deletions ReflectionEventing.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionEventing.Autofac.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionEventing.DependencyInjection.UnitTests", "tests\ReflectionEventing.DependencyInjection.UnitTests\ReflectionEventing.DependencyInjection.UnitTests.csproj", "{CF2E3E06-48A3-47F7-B152-E86F25C9A182}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Ninject", "src\ReflectionEventing.Ninject\ReflectionEventing.Ninject.csproj", "{194CA822-C3D6-4FBC-989A-039A0B97F2A8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionEventing.Ninject", "src\ReflectionEventing.Ninject\ReflectionEventing.Ninject.csproj", "{194CA822-C3D6-4FBC-989A-039A0B97F2A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Ninject.UnitTests", "tests\ReflectionEventing.Ninject.UnitTests\ReflectionEventing.Ninject.UnitTests.csproj", "{A741FC02-03AE-4A34-AAA0-FBFBAB93377D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionEventing.Ninject.UnitTests", "tests\ReflectionEventing.Ninject.UnitTests\ReflectionEventing.Ninject.UnitTests.csproj", "{A741FC02-03AE-4A34-AAA0-FBFBAB93377D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Unity", "src\ReflectionEventing.Unity\ReflectionEventing.Unity.csproj", "{EBE050AC-CE99-49E0-9836-361D9F0DB515}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionEventing.Unity", "src\ReflectionEventing.Unity\ReflectionEventing.Unity.csproj", "{EBE050AC-CE99-49E0-9836-361D9F0DB515}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Unity.UnitTests", "tests\ReflectionEventing.Unity.UnitTests\ReflectionEventing.Unity.UnitTests.csproj", "{772AA314-8522-4A95-A99B-E69A867704DC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionEventing.Unity.UnitTests", "tests\ReflectionEventing.Unity.UnitTests\ReflectionEventing.Unity.UnitTests.csproj", "{772AA314-8522-4A95-A99B-E69A867704DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Castle.Windsor", "src\ReflectionEventing.Castle.Windsor\ReflectionEventing.Castle.Windsor.csproj", "{DEBBE7D1-7F8A-4013-9BA7-C721AB8A1E1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReflectionEventing.Castle.Windsor.UnitTests", "tests\ReflectionEventing.Castle.Windsor.UnitTests\ReflectionEventing.Castle.Windsor.UnitTests.csproj", "{8D24CF24-984D-4500-B99E-711DFBF64A92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -87,6 +91,14 @@ Global
{772AA314-8522-4A95-A99B-E69A867704DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{772AA314-8522-4A95-A99B-E69A867704DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{772AA314-8522-4A95-A99B-E69A867704DC}.Release|Any CPU.Build.0 = Release|Any CPU
{DEBBE7D1-7F8A-4013-9BA7-C721AB8A1E1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DEBBE7D1-7F8A-4013-9BA7-C721AB8A1E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DEBBE7D1-7F8A-4013-9BA7-C721AB8A1E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DEBBE7D1-7F8A-4013-9BA7-C721AB8A1E1B}.Release|Any CPU.Build.0 = Release|Any CPU
{8D24CF24-984D-4500-B99E-711DFBF64A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D24CF24-984D-4500-B99E-711DFBF64A92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D24CF24-984D-4500-B99E-711DFBF64A92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D24CF24-984D-4500-B99E-711DFBF64A92}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -97,6 +109,7 @@ Global
{CF2E3E06-48A3-47F7-B152-E86F25C9A182} = {7A58AE12-65AA-4E1B-B5DB-CBD2732D1AE7}
{A741FC02-03AE-4A34-AAA0-FBFBAB93377D} = {7A58AE12-65AA-4E1B-B5DB-CBD2732D1AE7}
{772AA314-8522-4A95-A99B-E69A867704DC} = {7A58AE12-65AA-4E1B-B5DB-CBD2732D1AE7}
{8D24CF24-984D-4500-B99E-711DFBF64A92} = {7A58AE12-65AA-4E1B-B5DB-CBD2732D1AE7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B017BA78-67A5-4D0F-92A3-671B0D8A2911}
Expand Down
45 changes: 45 additions & 0 deletions src/ReflectionEventing.Castle.Windsor/EventBusInstaller.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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 Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

namespace ReflectionEventing.Castle.Windsor;

/// <summary>
/// Represents a Castle Windsor installer for configuring the event bus and its related services.
/// </summary>
public class EventBusInstaller(Action<WindsorEventBusBuilder> configure) : IWindsorInstaller
{
/// <summary>
/// Adds the event bus and its related services to the specified Windsor container.
/// </summary>
/// <param name="container">The <see cref="IWindsorContainer"/> to add the event bus to.</param>
/// <param name="store">The <see cref="IConfigurationStore"/> for the container.</param>
public void Install(IWindsorContainer container, IConfigurationStore store)
{
WindsorEventBusBuilder builder = new(container);

if (!container.Kernel.HasComponent(typeof(IWindsorContainer)))
{
_ = container.Register(Component.For<IWindsorContainer>().Instance(container));
}

configure(builder);

_ = container.Register(
Component
.For<IConsumerTypesProvider>()
.Instance(new HashedConsumerTypesProvider(builder.GetConsumers()))
.LifestyleScoped(),
Component
.For<IConsumerProvider>()
.ImplementedBy<WindsorConsumerProvider>()
.LifestyleScoped(),
Component.For<IEventBus>().ImplementedBy<EventBus>().LifestyleTransient()
);
}
}
7 changes: 7 additions & 0 deletions src/ReflectionEventing.Castle.Windsor/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>ReflectionEventing.Castle.Windsor</PackageId>
<TargetFrameworks>netstandard2.0;netstandard2.1;net462;net6.0;net8.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsTrimmable>true</IsTrimmable>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PublishAot>true</PublishAot>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Speed</OptimizationPreference>
</PropertyGroup>

<!-- Necessary polyfills -->
<PropertyGroup>
<PolySharpIncludeGeneratedTypes>
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;
</PolySharpIncludeGeneratedTypes>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Castle.Windsor" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ReflectionEventing\ReflectionEventing.csproj" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions src/ReflectionEventing.Castle.Windsor/WindsorConsumerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 Castle.Windsor;

namespace ReflectionEventing.Castle.Windsor;

/// <summary>
/// Represents a provider for retrieving event consumers from Castle Windsor's IoC container.
/// </summary>
public class WindsorConsumerProvider(IWindsorContainer container) : IConsumerProvider
{
/// <inheritdoc />
public IEnumerable<object> GetConsumerTypes(Type consumerType)
{
if (consumerType is null)
{
throw new ArgumentNullException(nameof(consumerType));
}

return container.ResolveAll(consumerType) as object[] ?? [];
}
}
33 changes: 33 additions & 0 deletions src/ReflectionEventing.Castle.Windsor/WindsorEventBusBuilder.cs
Original file line number Diff line number Diff line change
@@ -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.

using System.Diagnostics.CodeAnalysis;
using Castle.Windsor;

namespace ReflectionEventing.Castle.Windsor;

/// <summary>
/// Represents a builder for configuring the event bus with Castle Windsor's IoC container.
/// </summary>
public class WindsorEventBusBuilder(IWindsorContainer container) : EventBusBuilder
{
/// <inheritdoc />
public override void AddConsumer(
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
Type consumerType
)
{
if (!container.Kernel.HasComponent(consumerType))
{
throw new InvalidOperationException(
"Event consumer must be registered in the container."
);
}

base.AddConsumer(consumerType);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>ReflectionEventing.DependencyInjection</PackageId>
<PackageId>ReflectionEventing.Ninject</PackageId>
<TargetFrameworks>net462;net472;net481</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsTrimmable>true</IsTrimmable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>ReflectionEventing.DependencyInjection</PackageId>
<PackageId>ReflectionEventing.Unity</PackageId>
<TargetFrameworks>netstandard2.0;netstandard2.1;net462;net6.0;net8.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsTrimmable>true</IsTrimmable>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 Castle.MicroKernel.Lifestyle;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace ReflectionEventing.Castle.Windsor.UnitTests;

public sealed class EventBusInstallerTests
{
[Fact]
public void Install_RegistersServicesAndAddsConsumer()
{
IWindsorContainer container = new WindsorContainer();

_ = container.Register(Component.For<TestConsumer>().LifestyleScoped());

EventBusInstaller installer =
new(builder =>
{
_ = builder.AddConsumer<TestConsumer>();
});

installer.Install(container, null!);

using IDisposable scope = container.BeginScope();

IConsumerTypesProvider consumerTypesProvider = container.Resolve<IConsumerTypesProvider>();
_ = consumerTypesProvider.Should().NotBeNull();
_ = consumerTypesProvider.Should().BeOfType<HashedConsumerTypesProvider>();

IConsumerProvider consumerProvider = container.Resolve<IConsumerProvider>();
_ = consumerProvider.Should().NotBeNull();
_ = consumerProvider.Should().BeOfType<WindsorConsumerProvider>();

IEventBus eventBus = container.Resolve<IEventBus>();
_ = eventBus.Should().NotBeNull();
_ = eventBus.Should().BeOfType<EventBus>();

IEnumerable<Type> consumers = consumerTypesProvider.GetConsumerTypes<TestEvent>();

_ = consumers.First().Should().Be(typeof(TestConsumer));
}

public class TestConsumer : IConsumer<TestEvent>
{
public Task ConsumeAsync(TestEvent payload, CancellationToken cancellationToken) =>
Task.CompletedTask;
}

public sealed record TestEvent;
}
59 changes: 59 additions & 0 deletions tests/ReflectionEventing.Castle.Windsor.UnitTests/EventBusTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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 Castle.MicroKernel.Lifestyle;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace ReflectionEventing.Castle.Windsor.UnitTests;

public sealed class EventBusTests : IDisposable
{
private readonly IWindsorContainer _container;

public EventBusTests()
{
_container = new WindsorContainer();
_ = _container.Register(Component.For<TestConsumer>().LifestyleScoped());

EventBusInstaller installer =
new(builder =>
{
_ = builder.AddConsumer<TestConsumer>();
});

installer.Install(_container, null!);
}

[Fact]
public async Task PublishAsync_ShouldCallConsumeAsyncOnAllConsumers()
{
using IDisposable scope = _container.BeginScope();
IEventBus eventBus = _container.Resolve<IEventBus>();

await eventBus.PublishAsync(new TestEvent(), CancellationToken.None);

_ = _container.Resolve<TestConsumer>().ReceivedEvents.Should().Be(1);
}

public void Dispose()
{
_container.Dispose();
}

public class TestConsumer : IConsumer<TestEvent>
{
public int ReceivedEvents { get; private set; } = 0;

public Task ConsumeAsync(TestEvent payload, CancellationToken cancellationToken)
{
ReceivedEvents++;

return Task.CompletedTask;
}
}

public sealed record TestEvent;
}
12 changes: 12 additions & 0 deletions tests/ReflectionEventing.Castle.Windsor.UnitTests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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 FluentAssertions;
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions"/>
<PackageReference Include="NSubstitute"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\ReflectionEventing.Castle.Windsor\ReflectionEventing.Castle.Windsor.csproj" />
</ItemGroup>

</Project>

0 comments on commit 0b96617

Please sign in to comment.