diff --git a/Build.ps1 b/Build.ps1 index aa6cf417..ef1f2b1d 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -30,20 +30,7 @@ exec { & dotnet clean -c Release } exec { & dotnet build -c Release } -exec { & dotnet test -c Release -r $artifacts --no-build -l trx --verbosity=normal } - -$samples = Get-ChildItem .\samples\MediatR.Examples.* - -foreach ($sample in $samples) { - Push-Location -Path $sample - - try { - exec { & dotnet run -c Release --no-build --no-restore } - } catch { - } finally { - Pop-Location - } -} +exec { & dotnet test -c Release --no-build -l trx --verbosity=normal } exec { & dotnet pack .\src\MediatR\MediatR.csproj -c Release -o $artifacts --no-build } diff --git a/MediatR.sln b/MediatR.sln index 461fea7b..d8302f15 100644 --- a/MediatR.sln +++ b/MediatR.sln @@ -7,28 +7,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6267E2ED-942 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{962C5ACA-AB2B-4E9B-9EBB-7E7EE28CDBB1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{95CFF0CD-87A6-4CB6-A99F-42EAD0829E37}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR", "src\MediatR\MediatR.csproj", "{12DA3F16-060B-467A-993F-2DF25EE6E6A8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Tests", "test\MediatR.Tests\MediatR.Tests.csproj", "{4FB0CFC4-90E3-467F-9704-6FBF637F9B4B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples", "samples\MediatR.Examples\MediatR.Examples.csproj", "{18AB74EF-6E32-42C5-B6C4-159624CEDC5D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.Autofac", "samples\MediatR.Examples.Autofac\MediatR.Examples.Autofac.csproj", "{DD286AB8-BA96-4476-82DF-371275EA8C10}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.LightInject", "samples\MediatR.Examples.LightInject\MediatR.Examples.LightInject.csproj", "{0A0796F1-A0BE-4D15-A8F3-B6EFA41CAF0B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.SimpleInjector", "samples\MediatR.Examples.SimpleInjector\MediatR.Examples.SimpleInjector.csproj", "{A7FB2047-A4A0-4630-8C4C-31E7FA6ED902}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.StructureMap", "samples\MediatR.Examples.StructureMap\MediatR.Examples.StructureMap.csproj", "{7487E97A-CDA0-454D-8F62-CEE376DD3C3E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.Windsor", "samples\MediatR.Examples.Windsor\MediatR.Examples.Windsor.csproj", "{BFF350A1-4FB1-4355-8437-675605BF8C52}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.DryIoc", "samples\MediatR.Examples.DryIoc\MediatR.Examples.DryIoc.csproj", "{24A1217D-8438-45DA-A802-0A3A7A3DDFD1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.AspNetCore", "samples\MediatR.Examples.AspNetCore\MediatR.Examples.AspNetCore.csproj", "{4A255E84-AAC5-4ED9-ABAC-822804DBA4F1}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1E133765-6B4E-46C2-8C6C-7238E8EABA79}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -41,16 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.Unity", "samples\MediatR.Examples.Unity\MediatR.Examples.Unity.csproj", "{E6C51E44-59B4-4F4F-AFB3-4032CDDEF07A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.Lamar", "samples\MediatR.Examples.Lamar\MediatR.Examples.Lamar.csproj", "{F014598D-8B85-4D7E-942A-3493107ABE43}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.PublishStrategies", "samples\MediatR.Examples.PublishStrategies\MediatR.Examples.PublishStrategies.csproj", "{867EBA13-62F4-4525-8F92-B0AD828EE6D4}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Benchmarks", "test\MediatR.Benchmarks\MediatR.Benchmarks.csproj", "{1FA62162-F8F1-4CAD-B08E-8DCA603395AD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Examples.Stashbox", "samples\MediatR.Examples.Stashbox\MediatR.Examples.Stashbox.csproj", "{575EC49E-BEC8-4DFF-BD22-8464844093FE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Contracts", "src\MediatR.Contracts\MediatR.Contracts.csproj", "{87F5A238-44B5-4769-82E5-E68B712D6E6D}" EndProject Global @@ -67,58 +41,10 @@ Global {4FB0CFC4-90E3-467F-9704-6FBF637F9B4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FB0CFC4-90E3-467F-9704-6FBF637F9B4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {4FB0CFC4-90E3-467F-9704-6FBF637F9B4B}.Release|Any CPU.Build.0 = Release|Any CPU - {18AB74EF-6E32-42C5-B6C4-159624CEDC5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {18AB74EF-6E32-42C5-B6C4-159624CEDC5D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {18AB74EF-6E32-42C5-B6C4-159624CEDC5D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {18AB74EF-6E32-42C5-B6C4-159624CEDC5D}.Release|Any CPU.Build.0 = Release|Any CPU - {DD286AB8-BA96-4476-82DF-371275EA8C10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DD286AB8-BA96-4476-82DF-371275EA8C10}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DD286AB8-BA96-4476-82DF-371275EA8C10}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DD286AB8-BA96-4476-82DF-371275EA8C10}.Release|Any CPU.Build.0 = Release|Any CPU - {0A0796F1-A0BE-4D15-A8F3-B6EFA41CAF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0A0796F1-A0BE-4D15-A8F3-B6EFA41CAF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0A0796F1-A0BE-4D15-A8F3-B6EFA41CAF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0A0796F1-A0BE-4D15-A8F3-B6EFA41CAF0B}.Release|Any CPU.Build.0 = Release|Any CPU - {A7FB2047-A4A0-4630-8C4C-31E7FA6ED902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A7FB2047-A4A0-4630-8C4C-31E7FA6ED902}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A7FB2047-A4A0-4630-8C4C-31E7FA6ED902}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A7FB2047-A4A0-4630-8C4C-31E7FA6ED902}.Release|Any CPU.Build.0 = Release|Any CPU - {7487E97A-CDA0-454D-8F62-CEE376DD3C3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7487E97A-CDA0-454D-8F62-CEE376DD3C3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7487E97A-CDA0-454D-8F62-CEE376DD3C3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7487E97A-CDA0-454D-8F62-CEE376DD3C3E}.Release|Any CPU.Build.0 = Release|Any CPU - {BFF350A1-4FB1-4355-8437-675605BF8C52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BFF350A1-4FB1-4355-8437-675605BF8C52}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BFF350A1-4FB1-4355-8437-675605BF8C52}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BFF350A1-4FB1-4355-8437-675605BF8C52}.Release|Any CPU.Build.0 = Release|Any CPU - {24A1217D-8438-45DA-A802-0A3A7A3DDFD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24A1217D-8438-45DA-A802-0A3A7A3DDFD1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24A1217D-8438-45DA-A802-0A3A7A3DDFD1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24A1217D-8438-45DA-A802-0A3A7A3DDFD1}.Release|Any CPU.Build.0 = Release|Any CPU - {4A255E84-AAC5-4ED9-ABAC-822804DBA4F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A255E84-AAC5-4ED9-ABAC-822804DBA4F1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A255E84-AAC5-4ED9-ABAC-822804DBA4F1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A255E84-AAC5-4ED9-ABAC-822804DBA4F1}.Release|Any CPU.Build.0 = Release|Any CPU - {E6C51E44-59B4-4F4F-AFB3-4032CDDEF07A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6C51E44-59B4-4F4F-AFB3-4032CDDEF07A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6C51E44-59B4-4F4F-AFB3-4032CDDEF07A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6C51E44-59B4-4F4F-AFB3-4032CDDEF07A}.Release|Any CPU.Build.0 = Release|Any CPU - {F014598D-8B85-4D7E-942A-3493107ABE43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F014598D-8B85-4D7E-942A-3493107ABE43}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F014598D-8B85-4D7E-942A-3493107ABE43}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F014598D-8B85-4D7E-942A-3493107ABE43}.Release|Any CPU.Build.0 = Release|Any CPU - {867EBA13-62F4-4525-8F92-B0AD828EE6D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {867EBA13-62F4-4525-8F92-B0AD828EE6D4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {867EBA13-62F4-4525-8F92-B0AD828EE6D4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {867EBA13-62F4-4525-8F92-B0AD828EE6D4}.Release|Any CPU.Build.0 = Release|Any CPU {1FA62162-F8F1-4CAD-B08E-8DCA603395AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1FA62162-F8F1-4CAD-B08E-8DCA603395AD}.Debug|Any CPU.Build.0 = Debug|Any CPU {1FA62162-F8F1-4CAD-B08E-8DCA603395AD}.Release|Any CPU.ActiveCfg = Release|Any CPU {1FA62162-F8F1-4CAD-B08E-8DCA603395AD}.Release|Any CPU.Build.0 = Release|Any CPU - {575EC49E-BEC8-4DFF-BD22-8464844093FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {575EC49E-BEC8-4DFF-BD22-8464844093FE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {575EC49E-BEC8-4DFF-BD22-8464844093FE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {575EC49E-BEC8-4DFF-BD22-8464844093FE}.Release|Any CPU.Build.0 = Release|Any CPU {87F5A238-44B5-4769-82E5-E68B712D6E6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {87F5A238-44B5-4769-82E5-E68B712D6E6D}.Debug|Any CPU.Build.0 = Debug|Any CPU {87F5A238-44B5-4769-82E5-E68B712D6E6D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -130,19 +56,7 @@ Global GlobalSection(NestedProjects) = preSolution {12DA3F16-060B-467A-993F-2DF25EE6E6A8} = {6267E2ED-942C-497D-BFC9-B3CE0AFC276F} {4FB0CFC4-90E3-467F-9704-6FBF637F9B4B} = {962C5ACA-AB2B-4E9B-9EBB-7E7EE28CDBB1} - {18AB74EF-6E32-42C5-B6C4-159624CEDC5D} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {DD286AB8-BA96-4476-82DF-371275EA8C10} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {0A0796F1-A0BE-4D15-A8F3-B6EFA41CAF0B} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {A7FB2047-A4A0-4630-8C4C-31E7FA6ED902} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {7487E97A-CDA0-454D-8F62-CEE376DD3C3E} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {BFF350A1-4FB1-4355-8437-675605BF8C52} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {24A1217D-8438-45DA-A802-0A3A7A3DDFD1} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {4A255E84-AAC5-4ED9-ABAC-822804DBA4F1} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {E6C51E44-59B4-4F4F-AFB3-4032CDDEF07A} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {F014598D-8B85-4D7E-942A-3493107ABE43} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} - {867EBA13-62F4-4525-8F92-B0AD828EE6D4} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} {1FA62162-F8F1-4CAD-B08E-8DCA603395AD} = {962C5ACA-AB2B-4E9B-9EBB-7E7EE28CDBB1} - {575EC49E-BEC8-4DFF-BD22-8464844093FE} = {95CFF0CD-87A6-4CB6-A99F-42EAD0829E37} {87F5A238-44B5-4769-82E5-E68B712D6E6D} = {6267E2ED-942C-497D-BFC9-B3CE0AFC276F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/samples/MediatR.Examples.AspNetCore/MediatR.Examples.AspNetCore.csproj b/samples/MediatR.Examples.AspNetCore/MediatR.Examples.AspNetCore.csproj deleted file mode 100644 index dd0af346..00000000 --- a/samples/MediatR.Examples.AspNetCore/MediatR.Examples.AspNetCore.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - - diff --git a/samples/MediatR.Examples.AspNetCore/Program.cs b/samples/MediatR.Examples.AspNetCore/Program.cs deleted file mode 100644 index ca7cb223..00000000 --- a/samples/MediatR.Examples.AspNetCore/Program.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using MediatR.Pipeline; -using Microsoft.Extensions.DependencyInjection; - - -namespace MediatR.Examples.AspNetCore; - -public static class Program -{ - public static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - return Runner.Run(mediator, writer, "ASP.NET Core DI", testStreams: true); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var services = new ServiceCollection(); - - services.AddSingleton(writer); - - services.AddMediatR(typeof(Ping), typeof(Sing)); - - services.AddScoped(typeof(IStreamRequestHandler), typeof(SingHandler)); - - services.AddScoped(typeof(IPipelineBehavior<,>), typeof(GenericPipelineBehavior<,>)); - services.AddScoped(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>)); - services.AddScoped(typeof(IRequestPostProcessor<,>), typeof(GenericRequestPostProcessor<,>)); - services.AddScoped(typeof(IStreamPipelineBehavior<,>), typeof(GenericStreamPipelineBehavior<,>)); - - var provider = services.BuildServiceProvider(); - - return provider.GetRequiredService(); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Autofac/MediatR.Examples.Autofac.csproj b/samples/MediatR.Examples.Autofac/MediatR.Examples.Autofac.csproj deleted file mode 100644 index ecc05080..00000000 --- a/samples/MediatR.Examples.Autofac/MediatR.Examples.Autofac.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.Autofac/Program.cs b/samples/MediatR.Examples.Autofac/Program.cs deleted file mode 100644 index 6c0f2d22..00000000 --- a/samples/MediatR.Examples.Autofac/Program.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace MediatR.Examples.Autofac; - -using global::Autofac; -using MediatR.Pipeline; -using System; -using System.IO; -using System.Reflection; -using System.Threading.Tasks; - -internal static class Program -{ - public static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "Autofac", testStreams: true); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var builder = new ContainerBuilder(); - builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces(); - - var mediatrOpenTypes = new[] - { - typeof(IRequestHandler<,>), - typeof(IRequestExceptionHandler<,,>), - typeof(IRequestExceptionAction<,>), - typeof(INotificationHandler<>), - typeof(IStreamRequestHandler<,>) - }; - - foreach (var mediatrOpenType in mediatrOpenTypes) - { - builder - .RegisterAssemblyTypes(typeof(Ping).GetTypeInfo().Assembly) - .AsClosedTypesOf(mediatrOpenType) - // when having a single class implementing several handler types - // this call will cause a handler to be called twice - // in general you should try to avoid having a class implementing for instance `IRequestHandler<,>` and `INotificationHandler<>` - // the other option would be to remove this call - // see also https://github.com/jbogard/MediatR/issues/462 - .AsImplementedInterfaces(); - } - - builder.RegisterInstance(writer).As(); - - // It appears Autofac returns the last registered types first - builder.RegisterGeneric(typeof(GenericStreamPipelineBehavior<,>)).As(typeof(IStreamPipelineBehavior<,>)); - - builder.RegisterGeneric(typeof(RequestPostProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(RequestPreProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(RequestExceptionActionProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(RequestExceptionProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(GenericRequestPreProcessor<>)).As(typeof(IRequestPreProcessor<>)); - builder.RegisterGeneric(typeof(GenericRequestPostProcessor<,>)).As(typeof(IRequestPostProcessor<,>)); - builder.RegisterGeneric(typeof(GenericPipelineBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(ConstrainedRequestPostProcessor<,>)).As(typeof(IRequestPostProcessor<,>)); - builder.RegisterGeneric(typeof(ConstrainedPingedHandler<>)).As(typeof(INotificationHandler<>)); - - builder.Register(ctx => - { - var c = ctx.Resolve(); - return t => c.Resolve(t); - }); - - var container = builder.Build(); - - // The below returns: - // - RequestPreProcessorBehavior - // - RequestPostProcessorBehavior - // - GenericPipelineBehavior - // - GenericStreamPipelineBehavior - // - RequestExceptionActionProcessorBehavior - // - RequestExceptionProcessorBehavior - - //var behaviors = container - // .Resolve>>() - // .ToList(); - - var mediator = container.Resolve(); - - return mediator; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.DryIoc/MediatR.Examples.DryIoc.csproj b/samples/MediatR.Examples.DryIoc/MediatR.Examples.DryIoc.csproj deleted file mode 100644 index 379a8cfe..00000000 --- a/samples/MediatR.Examples.DryIoc/MediatR.Examples.DryIoc.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.DryIoc/Program.cs b/samples/MediatR.Examples.DryIoc/Program.cs deleted file mode 100644 index eb3d9763..00000000 --- a/samples/MediatR.Examples.DryIoc/Program.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using DryIoc; - -namespace MediatR.Examples.DryIoc; - -class Program -{ - static Task Main() - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "DryIoc"); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new Container(); - - container.RegisterDelegate(r => r.Resolve); - container.Use(writer); - - //Pipeline works out of the box here - - container.RegisterMany(new[] { typeof(IMediator).GetAssembly(), typeof(Ping).GetAssembly() }, Registrator.Interfaces); - - return container.Resolve(); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.DryIocZero/Container.Generated.cs b/samples/MediatR.Examples.DryIocZero/Container.Generated.cs deleted file mode 100644 index d8aaf3be..00000000 --- a/samples/MediatR.Examples.DryIocZero/Container.Generated.cs +++ /dev/null @@ -1,383 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2016 Maksim Volkau - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -/* -======================================================================================================== -NOTE: The code below is generated automatically at compile-time and not supposed to be changed by hand. -======================================================================================================== -There are 4 generation issues (may be not an error dependent on context): - -The issues with run-time registrations may be solved by `container.RegisterPlaceholder()` -in Registrations.ttinclude. Then you can replace placeholders using `DryIocZero.Container.Register` -at runtime. - --------------------------------------------------------------------------------------------------------- -1. MediatR.Pipeline.IRequestPreProcessor<> -Error: Resolving open-generic service type is not possible for type: MediatR.Pipeline.IRequestPreProcessor<>. -2. MediatR.Pipeline.IRequestPostProcessor<,> {ServiceKey=DefaultKey(0)} -Error: Resolving open-generic service type is not possible for type: MediatR.Pipeline.IRequestPostProcessor<,>. -3. MediatR.Pipeline.IRequestPostProcessor<,> {ServiceKey=DefaultKey(1)} -Error: Resolving open-generic service type is not possible for type: MediatR.Pipeline.IRequestPostProcessor<,>. -4. MediatR.INotificationHandler {RequiredServiceType=MediatR.INotificationHandler<>} -Error: Open-generic service does not match with registered open-generic implementation constraints MediatR.Examples.ConstrainedPingedHandler<> when resolving: MediatR.Examples.ConstrainedPingedHandler<>: MediatR.INotificationHandler {RequiredServiceType=MediatR.INotificationHandler<>} #42 - from container. -*/ - -using System; -using System.Linq; // for Enumerable.Cast method required by LazyEnumerable -using System.Collections.Generic; -using System.Threading; -using ImTools; -using static DryIocZero.ResolveManyResult; - -namespace DryIocZero -{ - partial class Container - { - [ExcludeFromCodeCoverage] - partial void GetLastGeneratedFactoryID(ref int lastFactoryID) - { - lastFactoryID = 60; // generated: equals to last used Factory.FactoryID - } - - [ExcludeFromCodeCoverage] - partial void ResolveGenerated(ref object service, Type serviceType) - { - if (serviceType == typeof(MediatR.INotificationHandler)) - service = Get10_INotificationHandler(this); - - else - if (serviceType == typeof(MediatR.INotificationHandler)) - service = Get11_INotificationHandler(this); - - else - if (serviceType == typeof(MediatR.IRequestHandler)) - service = Get12_IRequestHandler(this); - - else - if (serviceType == typeof(MediatR.IRequest)) - service = Get13_IRequest(this); - - else - if (serviceType == typeof(MediatR.IRequest)) - service = Get16_IRequest(this); - - else - if (serviceType == typeof(MediatR.IRequestHandler)) - service = Get17_IRequestHandler(this); - - else - if (serviceType == typeof(MediatR.IMediator)) - service = Get18_IMediator(this); - - else - if (serviceType == typeof(MediatR.IRequest)) - service = Get19_IRequest(this); - - else - if (serviceType == typeof(MediatR.INotificationHandler)) - service = Get20_INotificationHandler(this); - } - - [ExcludeFromCodeCoverage] - partial void ResolveGenerated(ref object service, - Type serviceType, object serviceKey, Type requiredServiceType, Request preRequestParent, object[] args) - { - if (serviceType == typeof(MediatR.IPipelineBehavior)) - { - if (DefaultKey.Of(0).Equals(serviceKey)) - service = Get0_IPipelineBehavior(this); - - else - if (DefaultKey.Of(1).Equals(serviceKey)) - service = Get2_IPipelineBehavior(this); - - else - if (DefaultKey.Of(2).Equals(serviceKey)) - service = Get4_IPipelineBehavior(this); - } - - else - if (serviceType == typeof(MediatR.IPipelineBehavior)) - { - if (DefaultKey.Of(0).Equals(serviceKey)) - service = Get1_IPipelineBehavior(this); - - else - if (DefaultKey.Of(1).Equals(serviceKey)) - service = Get3_IPipelineBehavior(this); - - else - if (DefaultKey.Of(2).Equals(serviceKey)) - service = Get5_IPipelineBehavior(this); - } - - else - if (serviceType == typeof(MediatR.INotification)) - { - if (DefaultKey.Of(0).Equals(serviceKey)) - service = Get6_INotification(this); - - else - if (DefaultKey.Of(1).Equals(serviceKey)) - service = Get7_INotification(this); - } - - else - if (serviceType == typeof(MediatR.IBaseRequest)) - { - if (DefaultKey.Of(0).Equals(serviceKey)) - service = Get8_IBaseRequest(this); - - else - if (DefaultKey.Of(1).Equals(serviceKey)) - service = Get9_IBaseRequest(this); - } - - else - if (serviceType == typeof(MediatR.INotificationHandler)) - { - if (DefaultKey.Of(0).Equals(serviceKey)) - service = Get14_INotificationHandler(this); - - else - if (DefaultKey.Of(1).Equals(serviceKey)) - service = Get15_INotificationHandler(this); - } - } - - [ExcludeFromCodeCoverage] - partial void ResolveManyGenerated(ref IEnumerable services, Type serviceType) - { - services = ResolveManyGenerated(serviceType); - } - - [ExcludeFromCodeCoverage] - private IEnumerable ResolveManyGenerated(Type serviceType) - { - if (serviceType == typeof(MediatR.IPipelineBehavior)) - { - yield return Of(Get0_IPipelineBehavior, DefaultKey.Of(0), typeof(MediatR.IPipelineBehavior<,>)); - yield return Of(Get2_IPipelineBehavior, DefaultKey.Of(1), typeof(MediatR.IPipelineBehavior<,>)); - yield return Of(Get4_IPipelineBehavior, DefaultKey.Of(2), typeof(MediatR.IPipelineBehavior<,>)); - } - - if (serviceType == typeof(MediatR.IPipelineBehavior)) - { - yield return Of(Get1_IPipelineBehavior, DefaultKey.Of(0), typeof(MediatR.IPipelineBehavior<,>)); - yield return Of(Get3_IPipelineBehavior, DefaultKey.Of(1), typeof(MediatR.IPipelineBehavior<,>)); - yield return Of(Get5_IPipelineBehavior, DefaultKey.Of(2), typeof(MediatR.IPipelineBehavior<,>)); - } - - if (serviceType == typeof(MediatR.INotification)) - { - yield return Of(Get6_INotification, DefaultKey.Of(0)); - yield return Of(Get7_INotification, DefaultKey.Of(1)); - } - - if (serviceType == typeof(MediatR.IBaseRequest)) - { - yield return Of(Get8_IBaseRequest, DefaultKey.Of(0)); - yield return Of(Get9_IBaseRequest, DefaultKey.Of(1)); - } - - if (serviceType == typeof(MediatR.INotificationHandler)) - { - yield return Of(Get10_INotificationHandler); - yield return Of(Get11_INotificationHandler); // co-variant - } - - if (serviceType == typeof(MediatR.INotificationHandler)) - { - yield return Of(Get11_INotificationHandler); - } - - if (serviceType == typeof(MediatR.IRequestHandler)) - { - yield return Of(Get12_IRequestHandler); - } - - if (serviceType == typeof(MediatR.IRequest)) - { - yield return Of(Get13_IRequest); - } - - if (serviceType == typeof(MediatR.INotificationHandler)) - { - yield return Of(Get14_INotificationHandler, DefaultKey.Of(0)); - yield return Of(Get15_INotificationHandler, DefaultKey.Of(1)); - yield return Of(Get20_INotificationHandler, typeof(MediatR.INotificationHandler<>)); - yield return Of(Get11_INotificationHandler); // co-variant - } - - if (serviceType == typeof(MediatR.IRequest)) - { - yield return Of(Get16_IRequest); - } - - if (serviceType == typeof(MediatR.IRequestHandler)) - { - yield return Of(Get17_IRequestHandler); - } - - if (serviceType == typeof(MediatR.IMediator)) - { - yield return Of(Get18_IMediator); - } - - if (serviceType == typeof(MediatR.IRequest)) - { - yield return Of(Get19_IRequest); - } - - } - - // typeof(MediatR.IPipelineBehavior) - internal static object Get0_IPipelineBehavior(IResolverContext r) - { - return new MediatR.Pipeline.RequestPostProcessorBehavior(new MediatR.Pipeline.IRequestPostProcessor[] { new MediatR.Examples.ConstrainedRequestPostProcessor((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(0), 48, FactoryType.Service, typeof(MediatR.Pipeline.RequestPostProcessorBehavior), Reuse.Transient, RequestFlags.IsResolutionCall).Push(typeof(System.Collections.Generic.IEnumerable>), default(System.Type), (object)null, 2, FactoryType.Wrapper, default(System.Type), Reuse.Transient, ((RequestFlags)0)).Push(typeof(MediatR.Pipeline.IRequestPostProcessor), default(System.Type), (object)DefaultKey.Of(0), IfUnresolved.ReturnDefaultIfNotRegistered, 49, FactoryType.Service, typeof(MediatR.Examples.ConstrainedRequestPostProcessor), Reuse.Transient, ((RequestFlags)0), 0), default(object[]))), new MediatR.Examples.GenericRequestPostProcessor((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(0), 48, FactoryType.Service, typeof(MediatR.Pipeline.RequestPostProcessorBehavior), Reuse.Transient, RequestFlags.IsResolutionCall).Push(typeof(System.Collections.Generic.IEnumerable>), default(System.Type), (object)null, 2, FactoryType.Wrapper, default(System.Type), Reuse.Transient, ((RequestFlags)0)).Push(typeof(MediatR.Pipeline.IRequestPostProcessor), default(System.Type), (object)DefaultKey.Of(1), IfUnresolved.ReturnDefaultIfNotRegistered, 50, FactoryType.Service, typeof(MediatR.Examples.GenericRequestPostProcessor), Reuse.Transient, ((RequestFlags)0), 0), default(object[]))) }); - } - - // typeof(MediatR.IPipelineBehavior) - internal static object Get1_IPipelineBehavior(IResolverContext r) - { - return new MediatR.Pipeline.RequestPostProcessorBehavior(new MediatR.Pipeline.IRequestPostProcessor[] { new MediatR.Examples.GenericRequestPostProcessor((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(0), 51, FactoryType.Service, typeof(MediatR.Pipeline.RequestPostProcessorBehavior), Reuse.Transient, RequestFlags.IsResolutionCall).Push(typeof(System.Collections.Generic.IEnumerable>), default(System.Type), (object)null, 2, FactoryType.Wrapper, default(System.Type), Reuse.Transient, ((RequestFlags)0)).Push(typeof(MediatR.Pipeline.IRequestPostProcessor), default(System.Type), (object)DefaultKey.Of(1), IfUnresolved.ReturnDefaultIfNotRegistered, 52, FactoryType.Service, typeof(MediatR.Examples.GenericRequestPostProcessor), Reuse.Transient, ((RequestFlags)0), 0), default(object[]))) }); - } - - // typeof(MediatR.IPipelineBehavior) - internal static object Get2_IPipelineBehavior(IResolverContext r) - { - return new MediatR.Pipeline.RequestPreProcessorBehavior(new MediatR.Pipeline.IRequestPreProcessor[] { new MediatR.Examples.GenericRequestPreProcessor((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(1), 53, FactoryType.Service, typeof(MediatR.Pipeline.RequestPreProcessorBehavior), Reuse.Transient, RequestFlags.IsResolutionCall).Push(typeof(System.Collections.Generic.IEnumerable>), default(System.Type), (object)null, 2, FactoryType.Wrapper, default(System.Type), Reuse.Transient, ((RequestFlags)0)).Push(typeof(MediatR.Pipeline.IRequestPreProcessor), default(System.Type), (object)DefaultKey.Of(0), IfUnresolved.ReturnDefaultIfNotRegistered, 54, FactoryType.Service, typeof(MediatR.Examples.GenericRequestPreProcessor), Reuse.Transient, ((RequestFlags)0), 0), default(object[]))) }); - } - - // typeof(MediatR.IPipelineBehavior) - internal static object Get3_IPipelineBehavior(IResolverContext r) - { - return new MediatR.Pipeline.RequestPreProcessorBehavior(new MediatR.Pipeline.IRequestPreProcessor[] { new MediatR.Examples.GenericRequestPreProcessor((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(1), 55, FactoryType.Service, typeof(MediatR.Pipeline.RequestPreProcessorBehavior), Reuse.Transient, RequestFlags.IsResolutionCall).Push(typeof(System.Collections.Generic.IEnumerable>), default(System.Type), (object)null, 2, FactoryType.Wrapper, default(System.Type), Reuse.Transient, ((RequestFlags)0)).Push(typeof(MediatR.Pipeline.IRequestPreProcessor), default(System.Type), (object)DefaultKey.Of(0), IfUnresolved.ReturnDefaultIfNotRegistered, 56, FactoryType.Service, typeof(MediatR.Examples.GenericRequestPreProcessor), Reuse.Transient, ((RequestFlags)0), 0), default(object[]))) }); - } - - // typeof(MediatR.IPipelineBehavior) - internal static object Get4_IPipelineBehavior(IResolverContext r) - { - return new MediatR.Examples.GenericPipelineBehavior((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(2), 57, FactoryType.Service, typeof(MediatR.Examples.GenericPipelineBehavior), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.IPipelineBehavior) - internal static object Get5_IPipelineBehavior(IResolverContext r) - { - return new MediatR.Examples.GenericPipelineBehavior((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IPipelineBehavior), typeof(MediatR.IPipelineBehavior<,>), (object)DefaultKey.Of(2), 58, FactoryType.Service, typeof(MediatR.Examples.GenericPipelineBehavior), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.INotification) - internal static object Get6_INotification(IResolverContext r) - { - return new MediatR.Examples.Pinged(); - } - - // typeof(MediatR.INotification) - internal static object Get7_INotification(IResolverContext r) - { - return new MediatR.Examples.Ponged(); - } - - // typeof(MediatR.IBaseRequest) - internal static object Get8_IBaseRequest(IResolverContext r) - { - return new MediatR.Examples.Jing(); - } - - // typeof(MediatR.IBaseRequest) - internal static object Get9_IBaseRequest(IResolverContext r) - { - return new MediatR.Examples.Ping(); - } - - // typeof(MediatR.INotificationHandler) - internal static object Get10_INotificationHandler(IResolverContext r) - { - return new MediatR.Examples.PongedHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.INotificationHandler), default(System.Type), (object)null, 41, FactoryType.Service, typeof(MediatR.Examples.PongedHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.INotificationHandler) - internal static object Get11_INotificationHandler(IResolverContext r) - { - return new MediatR.Examples.GenericHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.INotificationHandler), default(System.Type), (object)null, 32, FactoryType.Service, typeof(MediatR.Examples.GenericHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.IRequestHandler) - internal static object Get12_IRequestHandler(IResolverContext r) - { - return new MediatR.Examples.PingHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IRequestHandler), default(System.Type), (object)null, 44, FactoryType.Service, typeof(MediatR.Examples.PingHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.IRequest) - internal static object Get13_IRequest(IResolverContext r) - { - return new MediatR.Examples.Ping(); - } - - // typeof(MediatR.INotificationHandler) - internal static object Get14_INotificationHandler(IResolverContext r) - { - return new MediatR.Examples.PingedHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.INotificationHandler), default(System.Type), (object)DefaultKey.Of(0), 40, FactoryType.Service, typeof(MediatR.Examples.PingedHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.INotificationHandler) - internal static object Get15_INotificationHandler(IResolverContext r) - { - return new MediatR.Examples.PingedAlsoHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.INotificationHandler), default(System.Type), (object)DefaultKey.Of(1), 43, FactoryType.Service, typeof(MediatR.Examples.PingedAlsoHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.IRequest) - internal static object Get16_IRequest(IResolverContext r) - { - return new MediatR.Examples.Jing(); - } - - // typeof(MediatR.IRequestHandler) - internal static object Get17_IRequestHandler(IResolverContext r) - { - return new MediatR.Examples.JingHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IRequestHandler), default(System.Type), (object)null, 37, FactoryType.Service, typeof(MediatR.Examples.JingHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.IMediator) - internal static object Get18_IMediator(IResolverContext r) - { - return new MediatR.Mediator((MediatR.ServiceFactory)r.Resolve(typeof(MediatR.ServiceFactory), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.IMediator), default(System.Type), (object)null, 28, FactoryType.Service, typeof(MediatR.Mediator), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - // typeof(MediatR.IRequest) - internal static object Get19_IRequest(IResolverContext r) - { - return new MediatR.Examples.Jing(); - } - - // typeof(MediatR.INotificationHandler) - internal static object Get20_INotificationHandler(IResolverContext r) - { - return new MediatR.Examples.ConstrainedPingedHandler((System.IO.TextWriter)r.Resolve(typeof(System.IO.TextWriter), null, IfUnresolved.Throw, default(System.Type), Request.Empty.Push(typeof(MediatR.INotificationHandler), typeof(MediatR.INotificationHandler<>), (object)null, 59, FactoryType.Service, typeof(MediatR.Examples.ConstrainedPingedHandler), Reuse.Transient, RequestFlags.IsResolutionCall), default(object[]))); - } - - } -} diff --git a/samples/MediatR.Examples.DryIocZero/Container.Generated.tt b/samples/MediatR.Examples.DryIocZero/Container.Generated.tt deleted file mode 100644 index 69331fc5..00000000 --- a/samples/MediatR.Examples.DryIocZero/Container.Generated.tt +++ /dev/null @@ -1,286 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2016 Maksim Volkau - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -<#@ template debug="false" hostspecific="true" language="C#" #> -<#@ output extension=".cs" #> -<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #> -<#@ assembly Name="$(DryIocAssembly)" #> -<#@ assembly Name="$(ExpressionToCodeLibAssembly)" #> -<#@ import Namespace="DryIoc" #> -<#@ import Namespace="ImTools" #> -<#@ import Namespace="ExpressionToCodeLib" #> -<#@ assembly Name="System.Core" #> -<#@ assembly Name="System.Runtime" #> -<#@ import Namespace="System.Linq" #> -<#@ import Namespace="System.Linq.Expressions" #> -<#@ include File="$(ProjectDir)\Registrations.ttinclude" #> -<# -var container = GetContainerWithRegistrations() - .With(rules => rules.WithThrowIfRuntimeStateRequired()); - -var includeVariants = container.Rules.VariantGenericTypesInResolvedCollection; - -var result = container.GenerateResolutionExpressions(regs => regs.SelectMany(r => - SpecifyResolutionRoots(r).EmptyIfNull()).Concat(CustomResolutionRoots.EmptyIfNull())); - -var exprToCode = ExpressionToCodeConfiguration.DefaultCodeGenConfiguration - .WithObjectStringifier(ObjectStringify.WithFullTypeNames); - -string Code(object x) => - x == null ? "null" : - x is Expression ? exprToCode.ToCode((Expression)x) - .Replace("DryIoc." + nameof(IfUnresolved), nameof(IfUnresolved)) - .Replace("DryIoc." + nameof(IReuse), nameof(IReuse)) - .Replace("DryIoc." + nameof(Reuse), nameof(Reuse)) - .Replace("DryIoc." + nameof(SingletonReuse), nameof(SingletonReuse)) - .Replace("DryIoc." + nameof(CurrentScopeReuse), nameof(CurrentScopeReuse)) - .Replace("DryIoc." + nameof(FactoryType), nameof(FactoryType)) - .Replace("DryIoc." + nameof(DefaultKey), nameof(DefaultKey)) - .Replace("DryIoc." + nameof(DefaultDynamicKey), nameof(DefaultDynamicKey)) - .Replace("DryIoc." + nameof(Request), nameof(Request)) - .Replace("DryIoc." + nameof(RequestFlags), nameof(RequestFlags)) - .Replace("DryIoc." + nameof(HiddenDisposable), nameof(HiddenDisposable)) - .Replace("DryIoc." + nameof(ResolutionScopeName), nameof(ResolutionScopeName)) - .Replace("DryIoc." + nameof(CompositeScopeName), nameof(CompositeScopeName)) - : x is Request ? Code(container.GetRequestExpression((Request)x).ToExpression()) - : Code(container.GetConstantExpression(x, x.GetType(), true).ToExpression()); - -string GetTypeNameOnly(string typeName) => typeName.Split('`').First().Split('.').Last(); - -string OptArg(string arg) => arg == "null" ? "" : ", " + arg; - -var rootCodes = result.Roots.Select((r, i) => - new { ServiceType = r.Key.ServiceType, - ServiceTypeCode = Code(r.Key.ServiceType), - ServiceKeyCode = Code(r.Key.ServiceKey), - RequiredServiceTypeCode = Code(r.Key.Details.RequiredServiceType), - ExpressionCode = Code(r.Value.Body), - CreateMethodName = "Get" + i + "_" + GetTypeNameOnly(r.Key.ServiceType.Name) }); - -var depCodes = result.ResolveDependencies.Select((r, i) => - new { ServiceType = Code(r.Key.ServiceType), - ServiceKey = Code(r.Key.ServiceKey), ServiceKeyObject = r.Key.ServiceKey, - Expression = Code(r.Value), - RequiredServiceType = Code(r.Key.RequiredServiceType), - PreResolveParent = Code(r.Key.Parent), - CreateMethodName = "GetDep" + i + "_" + GetTypeNameOnly(r.Key.ServiceType.Name) }); -#> -/* -======================================================================================================== -NOTE: The code below is generated automatically at compile-time and not supposed to be changed by hand. -======================================================================================================== -<# var errCount = result.Errors.Count; - if (errCount == 0) { #> -Generation is completed successfully. -<# } else { #> -There are <#=errCount#> generation issues (may be not an error dependent on context): - -The issues with run-time registrations may be solved by `container.RegisterPlaceholder()` -in Registrations.ttinclude. Then you can replace placeholders using `DryIocZero.Container.Register` -at runtime. - -<# } #> --------------------------------------------------------------------------------------------------------- -<# var eNum = 0; - foreach(var e in result.Errors) { #> -<#=++eNum#>. <#=e.Key#> -Error: <#=e.Value.Message#> -<# } #> -*/ - -using System; -using System.Linq; // for Enumerable.Cast method required by LazyEnumerable -using System.Collections.Generic; -using System.Threading; -using ImTools; -using static DryIocZero.ResolveManyResult; - -namespace DryIocZero -{ - partial class Container - { - [ExcludeFromCodeCoverage] - partial void GetLastGeneratedFactoryID(ref int lastFactoryID) - { - lastFactoryID = <#=Factory.GetNextID()#>; // generated: equals to last used Factory.FactoryID - } - - [ExcludeFromCodeCoverage] - partial void ResolveGenerated(ref object service, Type serviceType) - { -<# - var index = 0; - foreach (var root in rootCodes.Where(f => f.ServiceKeyCode == "null")) - { - if (index++ > 0) WriteLine(@" - else"); -#> - if (serviceType == <#=root.ServiceTypeCode#>) - service = <#=root.CreateMethodName#>(this); -<# - } -#> - } - - [ExcludeFromCodeCoverage] - partial void ResolveGenerated(ref object service, - Type serviceType, object serviceKey, Type requiredServiceType, Request preRequestParent, object[] args) - { -<# - index = 0; - foreach (var rootGroup in rootCodes.Where(x => x.ServiceKeyCode != "null").GroupBy(x => x.ServiceTypeCode)) - { - if (index++ > 0) WriteLine(@" - else"); -#> - if (serviceType == <#=rootGroup.Key#>) - { -<# - var innerIndex = 0; - foreach (var root in rootGroup) - { - if (innerIndex++ > 0) WriteLine(@" - else"); -#> - if (<#=root.ServiceKeyCode#>.Equals(serviceKey)) - service = <#=root.CreateMethodName#>(this); -<# - } - -#> - } -<# - } -#> -<# - foreach (var depGroup in depCodes.GroupBy(x => x.ServiceType)) - { - if (index++ > 0) WriteLine(@" - else"); -#> - if (serviceType == <#=depGroup.Key#>) - { -<# - var innerIndex = 0; - foreach (var dep in depGroup) - { - if (innerIndex++ > 0) WriteLine(@" - else"); -#> - if (<#=dep.ServiceKeyObject == null ? "serviceKey == null" - : dep.ServiceKeyObject is DefaultKey ? "(serviceKey == null || " + dep.ServiceKey + ".Equals(serviceKey))" - : dep.ServiceKey + ".Equals(serviceKey)"#> && - requiredServiceType == <#= dep.RequiredServiceType #> && - Equals(preRequestParent, <#= dep.PreResolveParent #>)) - service = <#=dep.CreateMethodName#>(this); -<# - } -#> - } -<# - } -#> - } - - [ExcludeFromCodeCoverage] - partial void ResolveManyGenerated(ref IEnumerable services, Type serviceType) - { - services = ResolveManyGenerated(serviceType); - } - - [ExcludeFromCodeCoverage] - private IEnumerable ResolveManyGenerated(Type serviceType) - { -<# - if (!rootCodes.Any()) - { -#> - yield break; -<# - } - else - { - foreach (var rootGroup in rootCodes.GroupBy(x => x.ServiceType)) - { -#> - if (serviceType == <#=rootGroup.First().ServiceTypeCode#>) - { -<# - foreach (var root in rootGroup) - { -#> - yield return Of(<#=root.CreateMethodName#><#=OptArg(root.ServiceKeyCode)#><#=OptArg(root.RequiredServiceTypeCode)#>); -<# - } - - if (includeVariants && rootGroup.Key.IsGeneric()) - { - var sourceType = rootGroup.Key; - var variants = rootCodes - .Where(x => x.ServiceType.IsGeneric() && - x.ServiceType.GetGenericTypeDefinition() == sourceType.GetGenericTypeDefinition() && - x.ServiceType != sourceType && x.ServiceType.IsAssignableTo(sourceType)); - foreach (var variant in variants) - { -#> - yield return Of(<#=variant.CreateMethodName#><#=OptArg(variant.ServiceKeyCode)#><#=OptArg(variant.RequiredServiceTypeCode)#>); // co-variant -<# - } - } -#> - } - -<# - } - } -#> - } - -<# - foreach (var root in rootCodes) - { -#> - // <#=root.ServiceTypeCode#> - internal static object <#=root.CreateMethodName#>(IResolverContext r) - { - return <#=root.ExpressionCode#>; - } - -<# - } -#> -<# - foreach (var dep in depCodes) - { -#> - // <#=dep.ServiceType#> - internal static object <#=dep.CreateMethodName#>(IResolverContext r) - { - return <#=dep.Expression#>; - } - -<# - } -#> - } -} diff --git a/samples/MediatR.Examples.DryIocZero/Container.cs b/samples/MediatR.Examples.DryIocZero/Container.cs deleted file mode 100644 index 6203993b..00000000 --- a/samples/MediatR.Examples.DryIocZero/Container.cs +++ /dev/null @@ -1,1525 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2016 Maksim Volkau - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - - -namespace DryIocZero -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Threading; - using ImTools; - - /// Minimal container to register service factory delegates and then resolve service from them. - [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", - Justification = "Does not contain any unmanaged resources.")] - public sealed partial class Container : IRegistrator, IResolverContext - { - /// Creates container. - public Container(IScopeContext scopeContext = null) - : this(Ref.Of(ImHashMap.Empty), - Ref.Of(ImHashMap>.Empty), - new Scope(name: ""), scopeContext, - currentScope: null, disposed: 0, parent: null, root: null) - { } - - /// Full constructor with all state included. - public Container(Ref> defaultFactories, - Ref>> keyedFactories, - IScope singletonScope, IScopeContext scopeContext, IScope currentScope, - int disposed, IResolverContext parent, IResolverContext root) - { - _defaultFactories = defaultFactories; - _keyedFactories = keyedFactories; - - SingletonScope = singletonScope; - ScopeContext = scopeContext; - _currentScope = currentScope; - - _disposed = disposed; - - Parent = parent; - Root = root; - - GetLastGeneratedFactoryID(ref _lastFactoryID); - } - - private int _lastFactoryID; - - /// The unique factory ID, which may be used for runtime scoped registrations. - public int GetNextFactoryID() => Interlocked.Increment(ref _lastFactoryID); - - #region IResolver - - partial void GetLastGeneratedFactoryID(ref int lastFactoryID); - - // todo: May be replace with TryResolveGenerated to accommodate for the possible null service - partial void ResolveGenerated(ref object service, Type serviceType); - - /// Directly uses generated factories to resolve service. Or returns default if service does not have generated factory. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ExpressionIsAlwaysNull", Justification = "Per design")] - public object ResolveGeneratedOrGetDefault(Type serviceType) - { - object service = null; - ResolveGenerated(ref service, serviceType); - return service; - } - - /// Resolves service from container and returns created service object. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ConstantNullCoalescingCondition", Justification = "Per design")] - public object Resolve(Type serviceType, IfUnresolved ifUnresolved) - { - object service = null; - if (_defaultFactories.Value.IsEmpty) - ResolveGenerated(ref service, serviceType); - return service - ?? ResolveDefaultFromRuntimeRegistrationsFirst(serviceType, ifUnresolved == IfUnresolved.ReturnDefault); - } - - private object ResolveDefaultFromRuntimeRegistrationsFirst(Type serviceType, bool ifUnresolvedReturnDefault) - { - var factories = _defaultFactories.Value; - var factory = factories.GetValueOrDefault(serviceType); - - object service = null; - if (factory == null) - ResolveGenerated(ref service, serviceType); - else - service = factory(this); - - return service ?? Throw.If(!ifUnresolvedReturnDefault, - Error.UnableToResolveDefaultService, serviceType, factories.IsEmpty ? string.Empty : "non-"); - } - - // todo: May be replace with TryResolveGenerated to accommodate for the possible null service - partial void ResolveGenerated(ref object service, - Type serviceType, object serviceKey, Type requiredServiceType, Request preRequestParent, object[] args); - - /// Directly uses generated factories to resolve service. Or returns default if service does not have generated factory. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ExpressionIsAlwaysNull", Justification = "Per design")] - public object ResolveGeneratedOrGetDefault(Type serviceType, object serviceKey) - { - object service = null; - ResolveGenerated(ref service, serviceType, serviceKey, - requiredServiceType: null, preRequestParent: null, args: null); - return service; - } - - /// Resolves service from container and returns created service object. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ConstantNullCoalescingCondition", Justification = "Per design")] - object IResolver.Resolve(Type serviceType, object serviceKey, - IfUnresolved ifUnresolved, Type requiredServiceType, Request preResolveParent, object[] args) - { - // if no runtime registrations, then fast resolve from generated delegates - object service = null; - if (_keyedFactories.Value.IsEmpty) - { - if (serviceKey == null && requiredServiceType == null && preResolveParent == null && args == null) - ResolveGenerated(ref service, serviceType); - else - ResolveGenerated(ref service, serviceType, serviceKey, requiredServiceType, preResolveParent, args); - } - - // if not resolved from generated fallback to check runtime registrations first - return service - ?? ResolveFromRuntimeRegistrationsFirst(serviceType, serviceKey, - ifUnresolved == IfUnresolved.ReturnDefault, requiredServiceType, preResolveParent, args); - } - - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ConstantNullCoalescingCondition", Justification = "Per design")] - private object ResolveFromRuntimeRegistrationsFirst(Type serviceType, object serviceKey, - bool ifUnresolvedReturnDefault, Type requiredServiceType, Request preResolveParent, object[] args) - { - serviceType = requiredServiceType ?? serviceType; - - // ignore the rest of arguments, e.g. preResolveParent when resolving the runtime substitutes - if (serviceKey == null) - return ResolveDefaultFromRuntimeRegistrationsFirst(serviceType, ifUnresolvedReturnDefault); - - FactoryDelegate factory; - var factories = _keyedFactories.Value.GetValueOrDefault(serviceType); - if (factories != null && (factory = factories.GetValueOrDefault(serviceKey)) != null) - return factory(this); - - // If not resolved from runtime registration then try resolve generated - object service = null; - ResolveGenerated(ref service, serviceType, - serviceKey, requiredServiceType, preResolveParent ?? Request.Empty, args); - - return service ?? Throw.If(!ifUnresolvedReturnDefault, - Error.UnableToResolveKeyedService, serviceType, serviceKey, factories == null ? string.Empty : "non-"); - } - - partial void ResolveManyGenerated(ref IEnumerable services, Type serviceType); - - /// Resolves many generated only services. Ignores runtime registrations. - public IEnumerable ResolveManyGeneratedOrGetEmpty(Type serviceType) - { - var manyGenerated = Enumerable.Empty(); - ResolveManyGenerated(ref manyGenerated, serviceType); - return manyGenerated; - } - - /// - public IEnumerable ResolveMany(Type serviceType, object serviceKey = null, - Type requiredServiceType = null, Request preResolveParent = null, object[] args = null) - { - serviceType = requiredServiceType ?? serviceType; - - var generatedFactories = Enumerable.Empty(); - ResolveManyGenerated(ref generatedFactories, serviceType); - if (serviceKey != null) - generatedFactories = generatedFactories.Where(x => serviceKey.Equals(x.ServiceKey)); - if (requiredServiceType != null) - generatedFactories = generatedFactories.Where(x => requiredServiceType == x.RequiredServiceType); - - foreach (var generated in generatedFactories) - yield return generated.FactoryDelegate(this); - - if (serviceKey == null) - { - var defaultFactory = _defaultFactories.Value.GetValueOrDefault(serviceType); - if (defaultFactory != null) - yield return defaultFactory(this); - } - - var keyedFactories = _keyedFactories.Value.GetValueOrDefault(serviceType); - if (keyedFactories != null) - { - if (serviceKey != null) - { - var factoryDelegate = keyedFactories.GetValueOrDefault(serviceKey); - if (factoryDelegate != null) - yield return factoryDelegate(this); - } - else - { - var parent = preResolveParent?.Parent; - var compositeParentKey = parent != null && parent.ServiceType == serviceType - ? parent.ServiceKey - : null; - - foreach (var resolution in keyedFactories.Enumerate()) - if (compositeParentKey == null || !compositeParentKey.Equals(resolution.Key)) - yield return resolution.Value(this); - } - } - } - - #endregion - - #region IRegistrator - - /// Registers factory delegate with corresponding service type and service key. - public void Register(Type serviceType, FactoryDelegate factoryDelegate, IReuse reuse, object serviceKey) - { - ThrowIfContainerDisposed(); - - if (reuse != null) - factoryDelegate = reuse.Apply(GetNextFactoryID(), factoryDelegate); - - if (serviceKey == null) - _defaultFactories.Swap(it => it.AddOrUpdate(serviceType, factoryDelegate)); - else - _keyedFactories.Swap(it => it.AddOrUpdate(serviceType, - (it.GetValueOrDefault(serviceType) ?? - ImHashMap.Empty).AddOrUpdate(serviceKey, factoryDelegate))); - } - - private Ref> _defaultFactories; - private Ref>> _keyedFactories; - - #endregion - - #region IResolverContext - - /// True if container is disposed. - public bool IsDisposed => _disposed == 1; - - /// Parent context of the scoped context. - public IResolverContext Parent { get; } - - /// The root context of the scoped context. - public IResolverContext Root { get; } - - /// Scope containing container singletons. - public IScope SingletonScope { get; } - - /// Current scope. - public IScope CurrentScope => - ScopeContext == null ? _currentScope : ScopeContext.GetCurrentOrDefault(); - - private readonly IScope _currentScope; - - /// Scope context or null of not necessary. - public IScopeContext ScopeContext { get; } - - /// Specifies to wrap the scope in a resolver context. - public IResolverContext WithCurrentScope(IScope scope) - { - ThrowIfContainerDisposed(); - return new Container(_defaultFactories, _keyedFactories, SingletonScope, ScopeContext, - scope, _disposed, parent: this, root: Root ?? this); - } - - /// Disposes opened scope or root container with Singletons and ScopeContext. - [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", - Justification = "Does not contain any unmanaged resources.")] - public void Dispose() - { - if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1) - return; - - if (_currentScope != null) - { - _currentScope.Dispose(); - ScopeContext?.SetCurrent(scope => scope == _currentScope ? scope.Parent : scope); - } - else - { - _defaultFactories = Ref.Of(ImHashMap.Empty); - _keyedFactories = Ref.Of(ImHashMap>.Empty); - - SingletonScope.Dispose(); - ScopeContext?.Dispose(); - } - } - - private int _disposed; - private void ThrowIfContainerDisposed() => - Throw.If(_disposed == 1, Error.ContainerIsDisposed, this); - - #endregion - - /// Outputs scope info for open scope. - public override string ToString() - { - var scope = CurrentScope; - var scopeStr = scope == null ? "container" - : ScopeContext != null - ? "ambient scoped container with scope " + scope - : "scoped container with scope " + scope; - - if (IsDisposed) - scopeStr = "disposed " + scopeStr; - - return scopeStr; - } - } - - /// Identifies the service when resolving collection - public struct ResolveManyResult - { - /// Factory, the required part - public FactoryDelegate FactoryDelegate; - - /// Optional key - public object ServiceKey; - - /// Optional required service type, can be an open-generic type. - public Type RequiredServiceType; - - /// Constructs the struct. - public static ResolveManyResult Of(FactoryDelegate factoryDelegate, - object serviceKey = null, Type requiredServiceType = null) => - new ResolveManyResult - { - FactoryDelegate = factoryDelegate, - ServiceKey = serviceKey, - RequiredServiceType = requiredServiceType - }; - } - - /// Should return value stored in scope. - public delegate object CreateScopedValue(); - - /// Lazy object storage that will create object with provided factory on first access, - /// then will be returning the same object for subsequent access. - public interface IScope : IDisposable - { - /// Parent scope in scope stack. Null for root scope. - IScope Parent { get; } - - /// Optional name object associated with scope. - object Name { get; } - - /// True if scope is disposed. - bool IsDisposed { get; } - - /// Looks up for stored item by id. - bool TryGet(out object item, int id); - - /// Creates, stores, and returns stored disposable by id. - object GetOrAdd(int id, CreateScopedValue createValue, int disposalIndex = -1); - - /// Tracked item will be disposed with the scope. - /// Smaller will be disposed first. - object TrackDisposable(object item, int disposalIndex = -1); - - /// Sets (replaces) value at specified id, or adds value if no existing id found. - void SetOrAdd(int id, object item); - } - - /// Declares minimal API for service resolution. - /// Resolve default and keyed is separated because of optimization for faster resolution of default service. - public interface IResolver - { - /// Resolves default (non-keyed) service from container and returns created service object. - /// Service type to search and to return. - /// Says what to do if service is unresolved. - /// Created service object or default based on provided. - object Resolve(Type serviceType, IfUnresolved ifUnresolved); - - /// Resolves service instance from container. - /// Service type to search and to return. - /// (optional) service key used for registering service. - /// (optional) Says what to do if service is unresolved. - /// (optional) Registered or wrapped service type to use instead of , - /// or wrapped type for generic wrappers. The type should be assignable to return . - /// (optional) Dependency chain info. - /// (optional) For Func{args} propagation through Resolve call boundaries. - /// Created service object or default based on parameter. - object Resolve(Type serviceType, object serviceKey, - IfUnresolved ifUnresolved, Type requiredServiceType, Request preResolveParent, object[] args); - - /// Resolves all services registered for specified , or if not found returns - /// empty enumerable. If specified then returns only (single) service registered with - /// this type. - /// Return type of an service item. - /// (optional) Resolve only single service registered with the key. - /// (optional) Actual registered service to search for. - /// Dependency resolution path info. - /// (optional) For Func{args} propagation through Resolve call boundaries. - /// Enumerable of found services or empty. Does Not throw if no service found. - IEnumerable ResolveMany(Type serviceType, object serviceKey, - Type requiredServiceType, Request preResolveParent, object[] args); - } - - /// Extends IResolver to provide an access Scope hierarchy - public interface IResolverContext : IResolver, IDisposable - { - /// True if container is disposed. - bool IsDisposed { get; } - - /// Parent context of the scoped context. - IResolverContext Parent { get; } - - /// The root context of the scoped context. - IResolverContext Root { get; } - - /// Singleton scope, always associated with root scope. - IScope SingletonScope { get; } - - /// Current opened scope. - IScope CurrentScope { get; } - - /// Optional scope context associated with container. - IScopeContext ScopeContext { get; } - - /// Wraps the scope in resolver context (or container which implements the context). - IResolverContext WithCurrentScope(IScope scope); - } - - /// Provides APIs used by resolution generated factory delegates. - public static class ResolverContext - { - /// Just a sugar that allow to get root or self container. - public static IResolverContext RootOrSelf(this IResolverContext r) => r.Root ?? r; - - /// Provides access to the current scope. - public static IScope GetCurrentScope(this IResolverContext r, bool throwIfNotFound) => - r.CurrentScope ?? (IScope)Throw.If(throwIfNotFound, Error.NoCurrentScope, r); - - /// Gets current scope matching the - public static IScope GetNamedScope(this IResolverContext r, object name, bool throwIfNotFound) - { - var scope = r.CurrentScope; - if (scope == null) - return (IScope)Throw.If(throwIfNotFound, Error.NoCurrentScope, r); - - if (name == null) - return scope; - - var scopeName = name as IScopeName; - if (scopeName != null) - { - for (; scope != null; scope = scope.Parent) - if (scopeName.Match(scope.Name)) - return scope; - } - else - { - for (; scope != null; scope = scope.Parent) - if (ReferenceEquals(name, scope.Name) || name.Equals(scope.Name)) - return scope; - } - - return (IScope)Throw.If(throwIfNotFound, Error.NoMatchedScopeFound, name, r); - } - - /// Allows to open scope with the provided name and specified tracking option. - public static IResolverContext OpenScope(this IResolverContext r, object name = null, bool trackInParent = false) - { - var openedScope = r.ScopeContext == null - ? new Scope(r.CurrentScope, name) - : r.ScopeContext.SetCurrent(scope => new Scope(scope, name)); - - if (trackInParent) - (openedScope.Parent ?? r.SingletonScope).TrackDisposable(openedScope); - - return r.WithCurrentScope(openedScope); - } - } - - /// Service factory delegate which accepts resolver and resolution scope as parameters and should return service object. - /// It is stateless because does not include state parameter as DryIoc.FactoryDelegate. - public delegate object FactoryDelegate(IResolverContext r); - - /// Provides methods to register default or keyed factory delegates. - public interface IRegistrator - { - /// Registers factory delegate with corresponding service type and service key. - void Register(Type serviceType, FactoryDelegate factoryDelegate, IReuse reuse, object serviceKey); - } - - /// Delegate to get new scope from old/existing current scope. - /// Old/existing scope to change. - /// New scope or old if do not want to change current scope. - public delegate IScope SetCurrentScopeHandler(IScope oldScope); - - /// Provides ambient current scope and optionally scope storage for container, - /// examples are HttpContext storage, Execution context, Thread local. - public interface IScopeContext : IDisposable - { - /// Name associated with context root scope - so the reuse may find scope context. - string RootScopeName { get; } - - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - IScope GetCurrentOrDefault(); - - /// Changes current scope using provided delegate. Delegate receives current scope as input and - /// should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. So it is convenient to use method in using (var newScope = ctx.SetCurrent(...)). - IScope SetCurrent(SetCurrentScopeHandler setCurrentScope); - } - - /// Convenience extensions for registrations on top of delegate registrator. - public static class Registrator - { - /// Registers user provided delegate to create the service - public static void RegisterDelegate(this IRegistrator registrator, - Type serviceType, Func factoryDelegate, - IReuse reuse = null, object serviceKey = null) - { - FactoryDelegate factory = context => - { - var service = factoryDelegate(context); - if (service != null) - Throw.If(!serviceType.GetTypeInfo().IsAssignableFrom(service.GetType().GetTypeInfo()), - Error.ProducedServiceIsNotAssignableToRequiredServiceType, service, serviceType); - return service; - }; - - registrator.Register(serviceType, factory, reuse, serviceKey); - } - - /// Registers user provided delegate to create the service - public static void RegisterDelegate(this IRegistrator registrator, - IReuse reuse, Func factoryDelegate, object serviceKey = null) => - registrator.Register(typeof(TService), r => (TService)factoryDelegate(r), reuse, serviceKey); - - /// Registers user provided delegate to create the service - public static void RegisterDelegate(this IRegistrator registrator, - Func factoryDelegate, object serviceKey = null) => - registrator.RegisterDelegate(null, factoryDelegate, serviceKey); - - /// Registers passed service instance. - /// Service type, may be different from instance type. - /// Registrator to register with. - /// Externally managed service instance. - /// (optional) Service key. - public static void UseInstance(this IRegistrator registrator, - TService instance, object serviceKey = null) => - registrator.RegisterDelegate(_ => instance, serviceKey); - } - - /// Sugar to allow more simple resolve API - public static class Resolver - { - /// Resolves service of specified service type. - public static object Resolve(this IResolver resolver, Type serviceType) => - resolver.Resolve(serviceType, IfUnresolved.Throw); - - /// Resolves service of specified service type allowing to return null for unresolved service. - public static object Resolve(this IResolver resolver, Type serviceType, bool ifUnresolvedReturnDefault) => - resolver.Resolve(serviceType, ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw); - - /// Resolves service of specified service type. - public static object Resolve(this IResolver resolver, Type serviceType, object serviceKey, - Type requiredServiceType = null, Request preResolveParent = null, object[] args = null) => - resolver.Resolve(serviceType, serviceKey, IfUnresolved.Throw, requiredServiceType, preResolveParent, args); - - /// Resolves service of specified service type. - public static TService Resolve(this IResolver resolver) => - (TService)resolver.Resolve(typeof(TService), IfUnresolved.Throw); - - /// Resolves service of specified service type. - public static TService Resolve(this IResolver resolver, bool ifUnresolvedReturnDefault) => - (TService)resolver.Resolve(typeof(TService), ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw); - - /// Resolves service of specified service type. - public static TService Resolve(this IResolver resolver, object serviceKey) => - (TService)resolver.Resolve(typeof(TService), serviceKey, IfUnresolved.Throw, - requiredServiceType: null, preResolveParent: Request.Empty, args: null); - - /// Resolves service of type. - public static TService Resolve(this IResolver resolver, Type requiredServiceType, - bool ifUnresolvedReturnDefault = false, object serviceKey = null) => - (TService)(requiredServiceType == null && serviceKey == null - ? resolver.Resolve(typeof(TService), - ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw) - : resolver.Resolve(typeof(TService), serviceKey, - ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw, - requiredServiceType, Request.Empty, null)); - - /// Resolves collection of services of specified service type. - public static IEnumerable ResolveMany(this IResolver resolver, Type serviceType) => - resolver.ResolveMany(serviceType, null, null, Request.Empty, null); - - /// Resolves collection of services of specified service type. - public static IEnumerable ResolveMany(this IResolver resolver, Type serviceType, object serviceKey) => - resolver.ResolveMany(serviceType, serviceKey, null, Request.Empty, null); - - /// Resolves collection of services of specified service type. - public static IEnumerable ResolveMany(this IResolver resolver) => - resolver.ResolveMany(typeof(TService)).Cast(); - - /// Resolves collection of services of specified service type. - public static IEnumerable ResolveMany(this IResolver resolver, object serviceKey) => - resolver.ResolveMany(typeof(TService), serviceKey).Cast(); - - /// For given instance resolves and sets properties. - /// Target resolver. - /// Service instance with properties to resolve and initialize. - /// (optional) By default only declared properties are injected, if set will add base properties too. - /// If true and property is unresolved, nothing would happen, no exception - /// and property stays untouched - /// Instance with assigned properties. - public static object InjectProperties(this IResolver resolver, object instance, bool includeBase = false, - bool skipIfPropertyUnresolved = false) - { - var instanceType = instance.GetType(); - var ifUnresolved = skipIfPropertyUnresolved ? IfUnresolved.ReturnDefault : IfUnresolved.Throw; - var properties = instanceType.Properties(includeBase); - - foreach (var propertyInfo in properties) - { - var value = resolver.Resolve(propertyInfo.PropertyType, ifUnresolved); - if (value != null) - propertyInfo.SetValue(instance, value, null); - } - - return instance; - } - } - - /// Scope implementation to hold and dispose stored items. - /// lock is used internally to ensure that object factory called only once. - public sealed class Scope : IScope - { - /// Parent scope in scope stack. Null for the root scope. - public IScope Parent { get; } - - /// Optional name associated with scope. - public object Name { get; } - - /// True if scope is disposed. - public bool IsDisposed => _disposed == 1; - - /// Creates scope with optional parent and name. - public Scope(IScope parent = null, object name = null) - : this(parent, name, ImMap.Empty, ImMap.Empty, int.MaxValue) - { } - - private Scope(IScope parent, object name, ImMap items, - ImMap disposables, int nextDisposalIndex) - { - Parent = parent; - Name = name; - _items = items; - _disposables = disposables; - _nextDisposalIndex = nextDisposalIndex; - } - - internal static readonly MethodInfo GetOrAddMethod = - typeof(IScope).GetTypeInfo().DeclaredMethods.First(m => m.Name == nameof(IScope.GetOrAdd)); - - /// - public object GetOrAdd(int id, CreateScopedValue createValue, int disposalIndex = -1) - { - object value; - return _items.TryFind(id, out value) - ? value : TryGetOrAdd(id, createValue, disposalIndex); - } - - private object TryGetOrAdd(int id, CreateScopedValue createValue, int disposalIndex = -1) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - object item; - lock (_locker) - { - if (_items.TryFind(id, out item)) // double check locking - return item; - - item = createValue(); - - // Swap is required because if _items changed inside createValue, then we need to retry - var items = _items; - if (Interlocked.CompareExchange(ref _items, items.AddOrUpdate(id, item), items) != items) - Ref.Swap(ref _items, it => it.AddOrUpdate(id, item)); - } - - return TrackDisposable(item, disposalIndex); - } - - /// Sets (replaces) value at specified id, or adds value if no existing id found. - /// To set value at. Should be >= 0. Value to set. - public void SetOrAdd(int id, object item) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - var items = _items; - - // try to atomically replaced items with the one set item, if attempt failed then lock and replace - if (Interlocked.CompareExchange(ref _items, items.AddOrUpdate(id, item), items) != items) - lock (_locker) - _items = _items.AddOrUpdate(id, item); - - TrackDisposable(item); - } - - /// - public bool TryGet(out object item, int id) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - return _items.TryFind(id, out item); - } - - internal static readonly MethodInfo TrackDisposableMethod = - typeof(IScope).GetTypeInfo().DeclaredMethods.First(m => m.Name == nameof(IScope.TrackDisposable)); - - /// Can be used to manually add service for disposal - public object TrackDisposable(object item, int disposalIndex = -1) - { - if (item == this) - return item; - - var disposable = item as IDisposable; - if (disposable != null) - { - if (disposalIndex == -1) - disposalIndex = Interlocked.Decrement(ref _nextDisposalIndex); - - var it = _disposables; - if (Interlocked.CompareExchange(ref _disposables, it.AddOrUpdate(disposalIndex, disposable), it) != it) - Ref.Swap(ref _disposables, _ => _.AddOrUpdate(disposalIndex, disposable)); - } - return item; - } - - /// Disposes all stored objects and empties item storage. - /// If disposal throws exception, then it won't be propagated outside, - /// so the rest of the items may proceed to be disposed. - public void Dispose() - { - if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1) - return; - - var disposables = _disposables; - if (!disposables.IsEmpty) - foreach (var disposable in disposables.Enumerate()) - { - // Ignoring disposing exception, as it is not important to proceed the disposal - try { disposable.Value.Dispose(); } - catch (Exception) { } - } - - _disposables = ImMap.Empty; - - _items = ImMap.Empty; - } - - /// Prints scope info (name and parent) to string for debug purposes. - public override string ToString() => - (IsDisposed ? "disposed" : "") + "{" - + (Name != null ? "Name=" + Name : "no name") - + (Parent != null ? ", Parent=" + Parent : "") - + "}"; - - #region Implementation - - private ImMap _items; - private ImMap _disposables; - private int _nextDisposalIndex; - private int _disposed; - - // todo: Improve performance by scaling lockers count with the items amount - // Sync root is required to create object only once. The same reason as for Lazy. - private readonly object _locker = new object(); - - #endregion - } - - internal static class ScopedDisposableHandling - { - public static IDisposable TryUnwrapDisposable(object item) - { - var disposable = item as IDisposable; - if (disposable != null) - return disposable; - - // Unwrap WeakReference if item wrapped in it. - var weakRefItem = item as WeakReference; - if (weakRefItem != null) - return weakRefItem.Target as IDisposable; - - return null; - } - - public static void DisposeItem(object item) - { - var disposable = TryUnwrapDisposable(item); - if (disposable != null) - { - try { disposable.Dispose(); } - catch (Exception) - { - // NOTE: Ignoring disposing exception, they not so important for program to proceed. - } - } - } - } - - /// List of error codes and messages. - public static class Error - { - /// First error code to identify error range for other possible error code definitions. - public static readonly int FirstErrorCode = 0; - - /// List of error messages indexed with code. - public static readonly List Messages = new List(100); - -#pragma warning disable 1591 // "Missing XML-comment" - public static readonly int - UnableToResolveDefaultService = Of( - "Unable to resolve {0} from {1}empty runtime registrations and from generated factory delegates."), - UnableToResolveKeyedService = Of( - "Unable to resolve {0} with key [{1}] from {2}empty runtime registrations and from generated factory delegates."), - NoCurrentScope = Of( - "No current scope is available in {0}. Probably you are resolving from outside of scope."), - NoMatchedScopeFound = Of( - "Unable to find scope with name '{0}' in {1}."), - ContainerIsDisposed = Of( - "Container is disposed and not in operation state: {0}."), - ScopeIsDisposed = Of( - "Scope is disposed and scoped instances are no longer available."), - ProducedServiceIsNotAssignableToRequiredServiceType = Of( - "Service {0} produced by registered delegate or instance is not assignable to required service type {1}."); -#pragma warning restore 1591 // "Missing XML-comment" - - /// Generates new code for message. - /// Message. Code. - public static int Of(string message) - { - Messages.Add(message); - return FirstErrorCode + Messages.Count - 1; - } - } - - /// Zero container exception. - [SuppressMessage("Microsoft.Usage", - "CA2237:MarkISerializableTypesWithSerializable", - Justification = "Not available in PCL")] - public class ContainerException : InvalidOperationException - { - /// Error code. - public int Error { get; } - - /// Creates exception. - /// Code. Message. - public ContainerException(int error, string message) - : base(message) - { - Error = error; - } - } - - /// Simplifies throwing exceptions. - public static class Throw - { - /// Just throws exception with specified error code. - public static void It(int error, params object[] args) - { - var messageFormat = Error.Messages[error]; - var message = string.Format(messageFormat, args); - throw new ContainerException(error, message); - } - - /// Throws if condition is true. - public static object If(bool condition, int error, params object[] args) - { - if (condition) - It(error, args); - return null; - } - } - - /// Called from generated code. - public static class ThrowInGeneratedCode - { - /// Throws if object is null. - public static object ThrowNewErrorIfNull(this object obj, string message) - { - if (obj == null) - Throw.It(Error.Of(message)); - return obj; - } - } - - /// Abstracts way to match reuse and scope names - public interface IScopeName - { - /// Does the job. - bool Match(object scopeName); - } - - /// Represents multiple names - public sealed class CompositeScopeName : IScopeName - { - /// Wraps the multiple names - public static CompositeScopeName Of(object[] names) => new CompositeScopeName(names); - - /// Matches all the name in a loop until first match is found, otherwise returns false. - public bool Match(object scopeName) - { - for (int i = 0; i < _names.Length; i++) - { - var name = _names[i]; - if (name == scopeName) - return true; - var aScopeName = name as IScopeName; - if (aScopeName != null && aScopeName.Match(scopeName)) - return true; - if (scopeName != null && scopeName.Equals(name)) - return true; - } - - return false; - } - - private CompositeScopeName(object[] names) - { - _names = names; - } - - private readonly object[] _names; - } - - /// Holds the name for the resolution scope. - public sealed class ResolutionScopeName : IScopeName - { - /// Creates scope with specified service type and key - public static ResolutionScopeName Of(Type serviceType = null, object serviceKey = null) => - new ResolutionScopeName(serviceType, serviceKey); - - /// Creates scope with specified service type and key. - public static ResolutionScopeName Of(object serviceKey = null) => - new ResolutionScopeName(typeof(TService), serviceKey); - - /// Type of service opening the scope. - public readonly Type ServiceType; - - /// Optional service key of service opening the scope. - public readonly object ServiceKey; - - private ResolutionScopeName(Type serviceType, object serviceKey) - { - ServiceType = serviceType; - ServiceKey = serviceKey; - } - - /// - public bool Match(object scopeName) - { - var name = scopeName as ResolutionScopeName; - if (name == null) - return false; - - return (ServiceType == null || - ServiceType.GetTypeInfo().IsAssignableFrom(name.ServiceType.GetTypeInfo()) || - ServiceType.GetTypeInfo().ContainsGenericParameters && - name.ServiceType.GetTypeInfo().IsGenericType && ServiceType.GetTypeInfo() - .IsAssignableFrom(name.ServiceType.GetGenericTypeDefinition().GetTypeInfo())) && - (ServiceKey == null || ServiceKey.Equals(name.ServiceKey)); - } - - /// String representation for easy debugging and understood error messages. - public override string ToString() - { - var s = new StringBuilder(GetType().Name).Append(ServiceType.FullName ?? ServiceType.Name); - if (ServiceKey != null) - s.Append(',').Append(ServiceKey); - return s.Append(")").ToString(); - } - } - - /// Reuse goal is to locate or create scope where reused objects will be stored. - /// implementers supposed to be stateless, and provide scope location behavior only. - /// The reused service instances should be stored in scope(s). - public interface IReuse - { - /// Relative to other reuses lifespan value. - int Lifespan { get; } - - /// Applies reuse to passed service creation factory. - /// Reused item id, used to store and find item in scope. - /// Source factory Transformed factory - FactoryDelegate Apply(int itemId, FactoryDelegate factoryDelegate); - } - - /// Specifies pre-defined reuse behaviors supported by container: - /// used when registering services into container with methods. - public static class Reuse - { - /// Synonym for absence of reuse. - public static readonly IReuse Transient = new TransientReuse(); - - /// Specifies to store single service instance per . - public static readonly IReuse Singleton = new SingletonReuse(); - - /// Scoped reuse - public static readonly IReuse Scoped = new CurrentScopeReuse(); - - /// Scoped to the named scope. - public static IReuse ScopedTo(object name) => new CurrentScopeReuse(name); - - /// Scoped to any scope with the one of specified names. - public static IReuse ScopedTo(params object[] names) => - names.IsNullOrEmpty() ? Scoped : - names.Length == 1 ? ScopedTo(names[0]) : - new CurrentScopeReuse(CompositeScopeName.Of(names)); - - /// Obsolete: please use - public static readonly IReuse InCurrentScope = new CurrentScopeReuse(); - - /// Obsolete: please use - public static readonly IReuse InResolutionScope = Scoped; - - /// Obsolete: please use - public static IReuse InCurrentNamedScope(object name = null) => - name == null ? InCurrentScope : new CurrentScopeReuse(name); - - /// Obsolete: please use - public static IReuse InResolutionScopeOf(Type assignableFromServiceType = null, object serviceKey = null) => - assignableFromServiceType == null && serviceKey == null ? InResolutionScope - : ScopedTo(ResolutionScopeName.Of(assignableFromServiceType, serviceKey)); - - /// Obsolete: please use - public static IReuse InResolutionScopeOf(object serviceKey = null) => - ScopedTo(ResolutionScopeName.Of(serviceKey)); - - /// Special name that by convention recognized by . - public static readonly string WebRequestScopeName = "WebRequestScopeName"; - - /// Web request is just convention for reuse in with special name . - public static readonly IReuse InWebRequest = ScopedTo(WebRequestScopeName); - } - - /// Transient reuse is for completeness, means no reuse. - public sealed class TransientReuse : IReuse - { - /// Value relative to other reuses lifespan value. - public int Lifespan => 0; - - /// Returns the input factory as-is. No reuse is applied. - public FactoryDelegate Apply(int itemId, FactoryDelegate factoryDelegate) => factoryDelegate; - } - - /// Singleton reuse. - public sealed class SingletonReuse : IReuse - { - /// Relative to other reuses lifespan value. - public int Lifespan => 1000; - - /// Before invoking the delegates looks-up for instance in scope. - public FactoryDelegate Apply(int itemId, FactoryDelegate factoryDelegate) => - r => r.SingletonScope.GetOrAdd(itemId, () => factoryDelegate(r)); - } - - /// Scoped reuse. - public sealed class CurrentScopeReuse : IReuse - { - /// Name to find current scope or parent with equal name. - public readonly object Name; - - /// Relative to other reuses lifespan value. - public int Lifespan => 100; - - /// Creates reuse optionally specifying its name. - public CurrentScopeReuse(object name = null) - { - Name = name; - } - - /// - public FactoryDelegate Apply(int itemId, FactoryDelegate factoryDelegate) - { - if (Name == null) - return r => r.GetCurrentScope(true).GetOrAdd(itemId, () => factoryDelegate(r)); - return r => r.GetNamedScope(Name, true).GetOrAdd(itemId, () => factoryDelegate(r)); - } - - internal static object TrackScopedOrSingleton(IResolverContext r, object item) => - (r.CurrentScope ?? r.SingletonScope).TrackDisposable(item); - - internal static object GetScopedOrSingleton(IResolverContext r, - int id, CreateScopedValue createValue, int disposalIndex) => - (r.CurrentScope ?? r.SingletonScope).GetOrAdd(id, createValue, disposalIndex); - - internal static object GetScoped(IResolverContext r, - bool throwIfNoScope, int id, CreateScopedValue createValue, int disposalIndex = -1) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAdd(id, createValue, disposalIndex); - - internal static object GetNameScoped(IResolverContext r, - object scopeName, bool throwIfNoScope, int id, CreateScopedValue createValue, int disposalIndex = -1) => - r.GetNamedScope(scopeName, throwIfNoScope)?.GetOrAdd(id, createValue, disposalIndex); - - internal static object TrackScoped(IResolverContext r, bool throwIfNoScope, object item) => - r.GetCurrentScope(throwIfNoScope)?.TrackDisposable(item); - - internal static object TrackNameScoped(IResolverContext r, - object scopeName, bool throwIfNoScope, object item) => - r.GetNamedScope(scopeName, throwIfNoScope)?.TrackDisposable(item); - } - - /// Stored check results of two kinds: inherited down dependency chain and not. - [Flags] - public enum RequestFlags - { - /// Not inherited - TracksTransientDisposable = 1 << 1, - - /// Not inherited - IsServiceCollection = 1 << 2, - - /// Inherited - IsSingletonOrDependencyOfSingleton = 1 << 3, - - /// Inherited - IsWrappedInFunc = 1 << 4, - - /// Indicates that the request the one from Resolve call. - IsResolutionCall = 1 << 5, - - /// Non inherited - OpensResolutionScope = 1 << 6, - - /// Non inherited - StopRecursiveDependencyCheck = 1 << 7 - } - - /// Type of services supported by Container. - public enum FactoryType - { - /// (default) Defines normal service factory - Service, - /// Defines decorator factory - Decorator, - /// Defines wrapper factory. - Wrapper - } - - /// Policy to handle unresolved service. - public enum IfUnresolved - { - /// If service is unresolved for whatever means, it will throw the respective exception. - Throw, - /// If service is unresolved for whatever means, it will return default(serviceType) value. - ReturnDefault, - /// If service is not registered, then it will return default, for other errors it will throw. - ReturnDefaultIfNotRegistered, - } - - /// Dependency request path information. - public sealed class Request : IEnumerable - { - /// Represents empty info. - public static readonly Request Empty = new Request(); - - /// Represents an empty info and indicates an open resolution scope. - public static readonly Request EmptyOpensResolutionScope = new Request(opensResolutionScope: true); - - /// Returns true for an empty request. - public bool IsEmpty => ServiceType == null; - - /// Returns true if request is the first in a chain. - public bool IsResolutionRoot => !IsEmpty && DirectParent.IsEmpty; - - /// Parent request or null for root resolution request. - public readonly Request DirectParent; - - /// Returns service parent skipping wrappers if any. - /// To get direct parent use . - public Request Parent - { - get - { - if (IsEmpty) - return Empty; - var p = DirectParent; - while (!p.IsEmpty && p.FactoryType == FactoryType.Wrapper) - p = p.DirectParent; - return p; - } - } - - /// Asked service type. - public readonly Type ServiceType; - - /// Required service type if specified. - public readonly Type RequiredServiceType; - - /// Optional service key. - public readonly object ServiceKey; - - /// Metadata key to find in metadata dictionary in resolved service. - public readonly string MetadataKey; - - /// Metadata value to find in resolved service. - public readonly object Metadata; - - /// Policy to deal with unresolved request. - public readonly IfUnresolved IfUnresolved; - - /// Resolved factory ID, used to identify applied decorator. - public readonly int FactoryID; - - /// False for Decorators and Wrappers. - public readonly FactoryType FactoryType; - - /// Implementation type. - public readonly Type ImplementationType; - - /// Service reuse. - public readonly IReuse Reuse; - - /// Relative number representing reuse lifespan. - public int ReuseLifespan => Reuse == null ? 0 : Reuse.Lifespan; - - /// . - public readonly RequestFlags Flags; - - /// Decorated factory ID for decorator request - public readonly int DecoratedFactoryID; - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, int factoryID, Type implementationType, IReuse reuse) => - Push(serviceType, null, null, null, null, IfUnresolved.Throw, - factoryID, FactoryType.Service, implementationType, reuse, default(RequestFlags), 0); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags) => - Push(serviceType, requiredServiceType, serviceKey, null, null, IfUnresolved.Throw, - factoryID, factoryType, implementationType, reuse, flags, 0); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags, - int decoratedFactoryID) => - Push(serviceType, requiredServiceType, serviceKey, null, null, ifUnresolved, - factoryID, factoryType, implementationType, reuse, flags, decoratedFactoryID); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, string metadataKey, object metadata, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags, - int decoratedFactoryID) => - new Request(serviceType, requiredServiceType, serviceKey, metadataKey, metadata, ifUnresolved, - factoryID, factoryType, implementationType, reuse, flags, this, decoratedFactoryID); - - /// Obsolete: now request is directly implements the . - public IEnumerable Enumerate() => this; - - /// Returns all non-empty requests starting from the current request and ending with the root parent. - /// Returns empty sequence for an empty request. - public IEnumerator GetEnumerator() - { - for (var i = this; !i.IsEmpty; i = i.DirectParent) - yield return i; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Prints request with all its parents to string. The string. - public override string ToString() - { - if (IsEmpty) - return "{empty}"; - - var s = new StringBuilder(); - - if (FactoryType != FactoryType.Service) - s.Append(FactoryType.ToString().ToLower()).Append(' '); - - if (ImplementationType != null && ImplementationType != ServiceType) - s.Append(ImplementationType).Append(": "); - - s.Append(ServiceType); - - if (RequiredServiceType != null) - s.Append(" with RequiredServiceType=").Append(RequiredServiceType); - - if (ServiceKey != null) - s.Append(" with ServiceKey=").Append('{').Append(ServiceKey).Append('}'); - - if (MetadataKey != null || Metadata != null) - s.Append(" with Metadata=").Append(MetadataKey.Pair(Metadata)); - - if (IfUnresolved != IfUnresolved.Throw) - s.Append(" if unresolved ").Append(Enum.GetName(typeof(IfUnresolved), IfUnresolved)); - - if (ReuseLifespan != 0) - s.Append(" with ReuseLifespan=").Append(ReuseLifespan); - - if (!DirectParent.IsEmpty) - s.AppendLine().Append(" in ").Append(DirectParent); - - return s.ToString(); - } - - /// Returns true if request info and passed object are equal, and their parents recursively are equal. - public override bool Equals(object obj) => - Equals(obj as Request); - - /// Returns true if request info and passed info are equal, and their parents recursively are equal. - public bool Equals(Request other) => - other != null && EqualsWithoutParent(other) - && (DirectParent == null && other.DirectParent == null - || (DirectParent != null && DirectParent.EqualsWithoutParent(other.DirectParent))); - - /// Compares info's regarding properties but not their parents. - public bool EqualsWithoutParent(Request other) => - other.ServiceType == ServiceType - - && other.Flags == Flags - - && other.RequiredServiceType == RequiredServiceType - && other.IfUnresolved == IfUnresolved - && Equals(other.ServiceKey, ServiceKey) - && other.MetadataKey == MetadataKey - && Equals(other.Metadata, Metadata) - - && other.FactoryType == FactoryType - && other.ImplementationType == ImplementationType - && other.ReuseLifespan == ReuseLifespan; - - /// Calculates the combined hash code based on factory IDs. - public override int GetHashCode() - { - if (IsEmpty) - return 0; - - var hash = FactoryID; - var parent = DirectParent; - while (!parent.IsEmpty) - { - hash = CombineHashCodes(hash, parent.FactoryID); - parent = parent.DirectParent; - } - - return hash; - } - - private Request(bool opensResolutionScope = false) - { - if (opensResolutionScope) - Flags = RequestFlags.OpensResolutionScope; - } - - private Request( - Type serviceType, Type requiredServiceType, object serviceKey, - string metadataKey, object metadata, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, - RequestFlags flags, Request directParent, - int decorateFactoryID) - { - DirectParent = directParent; - - // Service info: - ServiceType = serviceType; - RequiredServiceType = requiredServiceType; - ServiceKey = serviceKey; - MetadataKey = metadataKey; - Metadata = metadata; - IfUnresolved = ifUnresolved; - - // Implementation info: - FactoryID = factoryID; - FactoryType = factoryType; - ImplementationType = implementationType; - Reuse = reuse; - - DecoratedFactoryID = decorateFactoryID; - - Flags = flags; - } - - // Inspired by System.Tuple.CombineHashCodes - private static int CombineHashCodes(int h1, int h2) - { - unchecked - { - return (h1 << 5) + h1 ^ h2; - } - } - } - - /// Used to represent multiple default service keys. - /// Exposes to determine order of service added. - public sealed class DefaultKey - { - /// Default value. - public static readonly DefaultKey Value = new DefaultKey(0); - - /// Allows to determine service registration order. - public readonly int RegistrationOrder; - - /// Returns the default key with specified registration order. - public static DefaultKey Of(int registrationOrder) - { - if (registrationOrder < _keyPool.Length) - return _keyPool[registrationOrder]; - - var nextKey = new DefaultKey(registrationOrder); - if (registrationOrder == _keyPool.Length) - _keyPool = _keyPool.AppendOrUpdate(nextKey); - return nextKey; - } - - /// Returns next default key with increased . - public DefaultKey Next() => Of(RegistrationOrder + 1); - - /// Compares keys based on registration order. - public override bool Equals(object key) => - key == null || (key as DefaultKey)?.RegistrationOrder == RegistrationOrder; - - /// Returns registration order as hash. Hash code. - public override int GetHashCode() => RegistrationOrder; - - /// Prints registration order to string. Printed string. - public override string ToString() => GetType().Name + ".Of(" + RegistrationOrder + ")"; - - #region Implementation - - private static DefaultKey[] _keyPool = { Value }; - - private DefaultKey(int registrationOrder) - { - RegistrationOrder = registrationOrder; - } - - #endregion - } - - internal sealed class HiddenDisposable - { - public readonly object Value; - public HiddenDisposable(object value) - { - Value = value; - } - } - - /// Custom exclude from test code coverage attribute for portability. - public sealed class ExcludeFromCodeCoverageAttribute : Attribute - { - /// Optional reason of why the marked code is excluded from coverage. - public readonly string Reason; - - /// Creates attribute with optional reason message. - public ExcludeFromCodeCoverageAttribute(string reason = null) - { - Reason = reason; - } - } - - /// Helper and portability extensions to Reflection. - public static class ReflectionTools - { - /// Returns specific type members. Does not look into base class by default. - /// Specific members are returned by delegate. - public static IEnumerable Members(this Type type, - Func> getMembers, - bool includeBase = false) - { - var typeInfo = type.GetTypeInfo(); - var members = getMembers(typeInfo); - if (!includeBase) - return members; - var baseType = typeInfo.BaseType; - return baseType == null || baseType == typeof(object) - ? members - : members.Concat(baseType.Members(getMembers, true)); - } - - /// Properties only. - public static IEnumerable Properties(this Type type, bool includeBase = false) => - type.Members(t => t.DeclaredProperties, includeBase: true); - } -} - -namespace DryIoc -{ - static partial class Portable - { - // ReSharper disable once UnusedMember.Local - static partial void GetCurrentManagedThreadID(ref int threadID); - } -} diff --git a/samples/MediatR.Examples.DryIocZero/ImTools.cs b/samples/MediatR.Examples.DryIocZero/ImTools.cs deleted file mode 100644 index 3bf1f2d2..00000000 --- a/samples/MediatR.Examples.DryIocZero/ImTools.cs +++ /dev/null @@ -1,1514 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2016 Maksim Volkau - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included AddOrUpdateServiceFactory -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -namespace ImTools -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading; - using System.Runtime.CompilerServices; // For [MethodImpl(AggressiveInlining)] - - /// Helpers for functional composition - public static class Fun - { - /// Always a true condition. - public static bool Always(T _) => true; - - /// Always a false condition. - public static bool Never(T _) => false; - - /// Identity function returning passed argument as result. - public static T Itself(T x) => x; - } - - /// Methods to work with immutable arrays, and general array sugar. - public static class ArrayTools - { - /// Returns singleton empty array of provided type. - /// Array item type. Empty array. - public static T[] Empty() => EmptyArray.Value; - - private static class EmptyArray - { - public static readonly T[] Value = new T[0]; - } - - /// Wraps item in array. - public static T[] One(this T one) => new[] { one }; - - /// Returns true if array is null or have no items. Type of array item. - /// Source array to check. True if null or has no items, false otherwise. - public static bool IsNullOrEmpty(this T[] source) => source == null || source.Length == 0; - - /// Returns empty array instead of null, or source array otherwise. Type of array item. - public static T[] EmptyIfNull(this T[] source) => source ?? Empty(); - - /// Returns source enumerable if it is array, otherwise converts source to array. - public static T[] ToArrayOrSelf(this IEnumerable source) => - source == null ? Empty() : (source as T[] ?? source.ToArray()); - - /// Returns new array consisting from all items from source array then all items from added array. - /// If source is null or empty, then added array will be returned. - /// If added is null or empty, then source will be returned. - /// Array item type. - /// Array with leading items. - /// Array with following items. - /// New array with items of source and added arrays. - public static T[] Append(this T[] source, params T[] added) - { - if (added == null || added.Length == 0) - return source; - if (source == null || source.Length == 0) - return added; - - var result = new T[source.Length + added.Length]; - Array.Copy(source, 0, result, 0, source.Length); - if (added.Length == 1) - result[source.Length] = added[0]; - else - Array.Copy(added, 0, result, source.Length, added.Length); - return result; - } - - /// Performant concat of enumerables in case of arrays. - /// But performance will degrade if you use Concat().Where(). - /// Type of item. - /// goes first. - /// appended to source. - /// empty array or concat of source and other. - public static T[] Append(this IEnumerable source, IEnumerable other) - { - var sourceArr = source.ToArrayOrSelf(); - var otherArr = other.ToArrayOrSelf(); - return sourceArr.Append(otherArr); - } - - /// Returns new array with appended, - /// or at , if specified. - /// If source array could be null or empty, then single value item array will be created despite any index. - /// Array item type. - /// Array to append value to. - /// Value to append. - /// (optional) Index of value to update. - /// New array with appended or updated value. - public static T[] AppendOrUpdate(this T[] source, T value, int index = -1) - { - if (source == null || source.Length == 0) - return new[] { value }; - var sourceLength = source.Length; - index = index < 0 ? sourceLength : index; - var result = new T[index < sourceLength ? sourceLength : sourceLength + 1]; - Array.Copy(source, result, sourceLength); - result[index] = value; - return result; - } - - /// Calls predicate on each item in array until predicate returns true, - /// then method will return this item index, or if predicate returns false for each item, method will return -1. - /// Type of array items. - /// Source array: if null or empty, then method will return -1. - /// Delegate to evaluate on each array item until delegate returns true. - /// Index of item for which predicate returns true, or -1 otherwise. - public static int IndexOf(this T[] source, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (predicate(source[i])) - return i; - return -1; - } - - /// Looks up for item in source array equal to provided value, and returns its index, or -1 if not found. - /// Type of array items. - /// Source array: if null or empty, then method will return -1. - /// Value to look up. - /// Index of item equal to value, or -1 item is not found. - public static int IndexOf(this T[] source, T value) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - { - var item = source[i]; - if (Equals(item, value)) - return i; - } - return -1; - } - - /// Produces new array without item at specified . - /// Will return array if index is out of bounds, or source is null/empty. - /// Type of array item. - /// Input array. Index if item to remove. - /// New array with removed item at index, or input source array if index is not in array. - public static T[] RemoveAt(this T[] source, int index) - { - if (source == null || source.Length == 0 || index < 0 || index >= source.Length) - return source; - if (index == 0 && source.Length == 1) - return new T[0]; - var result = new T[source.Length - 1]; - if (index != 0) - Array.Copy(source, 0, result, 0, index); - if (index != result.Length) - Array.Copy(source, index + 1, result, index, result.Length - index); - return result; - } - - /// Looks for item in array using equality comparison, and returns new array with found item remove, or original array if not item found. - /// Type of array item. - /// Input array. Value to find and remove. - /// New array with value removed or original array if value is not found. - public static T[] Remove(this T[] source, T value) - { - return source.RemoveAt(source.IndexOf(value)); - } - - /// Returns first item matching the , or default item value. - /// item type - /// items collection to search - /// condition to evaluate for each item. - /// First item matching condition or default value. - public static T FindFirst(this T[] source, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - { - var item = source[i]; - if (predicate(item)) - return item; - } - return default(T); - } - - /// Returns first item matching the , or `default(T)` value. - /// item type - /// items collection to search - /// condition to evaluate for each item. - /// First item matching condition or default value. - public static T FindFirst(this IEnumerable source, Func predicate) - { - var sourceArr = source as T[]; - if (sourceArr != null) - return sourceArr.FindFirst(predicate); - return source.FirstOrDefault(predicate); - } - - private static T[] AppendTo(T[] source, int sourcePos, int count, T[] results = null) - { - if (results == null) - { - var newResults = new T[count]; - if (count == 1) - newResults[0] = source[sourcePos]; - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = source[j]; - return newResults; - } - - var matchCount = results.Length; - var appendedResults = new T[matchCount + count]; - if (matchCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, matchCount); - - if (count == 1) - appendedResults[matchCount] = source[sourcePos]; - else - Array.Copy(source, sourcePos, appendedResults, matchCount, count); - - return appendedResults; - } - - private static R[] AppendTo(T[] source, int sourcePos, int count, Func map, R[] results = null) - { - if (results == null || results.Length == 0) - { - var newResults = new R[count]; - if (count == 1) - newResults[0] = map(source[sourcePos]); - else - { - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = map(source[j]); - } - return newResults; - } - - var oldResultsCount = results.Length; - var appendedResults = new R[oldResultsCount + count]; - if (oldResultsCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, oldResultsCount); - - if (count == 1) - appendedResults[oldResultsCount] = map(source[sourcePos]); - else - { - for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) - appendedResults[i] = map(source[j]); - } - - return appendedResults; - } - - /// Where method similar to Enumerable.Where but more performant and non necessary allocating. - /// It returns source array and does Not create new one if all items match the condition. - /// Type of source items. - /// If null, the null will be returned. - /// Condition to keep items. - /// New array if some items are filter out. Empty array if all items are filtered out. Original array otherwise. - public static T[] Match(this T[] source, Func condition) - { - if (source == null || source.Length == 0) - return source; - - if (source.Length == 1) - return condition(source[0]) ? source : Empty(); - - if (source.Length == 2) - { - var condition0 = condition(source[0]); - var condition1 = condition(source[1]); - return condition0 && condition1 ? new[] { source[0], source[1] } - : condition0 ? new[] { source[0] } - : condition1 ? new[] { source[1] } - : Empty(); - } - - var matchStart = 0; - T[] matches = null; - var matchFound = false; - - var i = 0; - while (i < source.Length) - { - matchFound = condition(source[i]); - if (!matchFound) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - ++i; - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - if (matches != null) - return matches; - - if (matchStart != 0) // no matches - return Empty(); - - return source; - } - - /// Where method similar to Enumerable.Where but more performant and non necessary allocating. - /// It returns source array and does Not create new one if all items match the condition. - /// Type of source items. Type of result items. - /// If null, the null will be returned. - /// Condition to keep items. Converter from source to result item. - /// New array of result items. - public static R[] Match(this T[] source, Func condition, Func map) - { - if (source == null) - return null; - - if (source.Length == 0) - return Empty(); - - if (source.Length == 1) - { - var item = source[0]; - return condition(item) ? new[] { map(item) } : Empty(); - } - - if (source.Length == 2) - { - var condition0 = condition(source[0]); - var condition1 = condition(source[1]); - return condition0 && condition1 ? new[] { map(source[0]), map(source[1]) } - : condition0 ? new[] { map(source[0]) } - : condition1 ? new[] { map(source[1]) } - : Empty(); - } - - var matchStart = 0; - R[] matches = null; - var matchFound = false; - - var i = 0; - while (i < source.Length) - { - matchFound = condition(source[i]); - if (!matchFound) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, map, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - ++i; - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, map, matches); - - if (matches != null) - return matches; - - if (matchStart != 0) // no matches - return Empty(); - - return AppendTo(source, 0, source.Length, map); - } - - /// Maps all items from source to result array. - /// Source item type Result item type - /// Source items Function to convert item from source to result. - /// Converted items - public static R[] Map(this T[] source, Func map) - { - if (source == null) - return null; - - var sourceCount = source.Length; - if (sourceCount == 0) - return Empty(); - - if (sourceCount == 1) - return new[] { map(source[0]) }; - - if (sourceCount == 2) - return new[] { map(source[0]), map(source[1]) }; - - if (sourceCount == 3) - return new[] { map(source[0]), map(source[1]), map(source[2]) }; - - var results = new R[sourceCount]; - for (var i = 0; i < source.Length; i++) - results[i] = map(source[i]); - return results; - } - - /// Maps all items from source to result collection. - /// If possible uses fast array Map otherwise Enumerable.Select. - /// Source item type Result item type - /// Source items Function to convert item from source to result. - /// Converted items - public static IEnumerable Map(this IEnumerable source, Func map) - { - if (source == null) - return null; - var arr = source as T[]; - if (arr != null) - return arr.Map(map); - return source.Select(map); - } - - /// If is array uses more effective Match for array, otherwise just calls Where - /// Type of source items. - /// If null, the null will be returned. - /// Condition to keep items. - /// Result items, may be an array. - public static IEnumerable Match(this IEnumerable source, Func condition) - { - if (source == null) - return null; - var arr = source as T[]; - if (arr != null) - return arr.Match(condition); - return source.Where(condition); - } - - /// If is array uses more effective Match for array, - /// otherwise just calls Where, Select - /// Type of source items. Type of result items. - /// If null, the null will be returned. - /// Condition to keep items. Converter from source to result item. - /// Result items, may be an array. - public static IEnumerable Match(this IEnumerable source, Func condition, Func map) - { - if (source == null) - return null; - var arr = source as T[]; - if (arr != null) - return arr.Match(condition, map); - return source.Where(condition).Select(map); - } - } - - /// Wrapper that provides optimistic-concurrency Swap operation implemented using . - /// Type of object to wrap. - public sealed class Ref where T : class - { - /// Gets the wrapped value. - public T Value { get { return _value; } } - - /// Creates ref to object, optionally with initial value provided. - /// (optional) Initial value. - public Ref(T initialValue = default(T)) - { - _value = initialValue; - } - - /// Exchanges currently hold object with - see for details. - /// Delegate to produce new object value from current one passed as parameter. - /// Returns old object value the same way as - /// Important: May be called multiple times to retry update with value concurrently changed by other code. - public T Swap(Func getNewValue) - { - return Ref.Swap(ref _value, getNewValue); - } - - /// Just sets new value ignoring any intermingled changes. - /// old value - public T Swap(T newValue) - { - return Interlocked.Exchange(ref _value, newValue); - } - - /// Compares current Referred value with and if equal replaces current with - /// - /// True if current value was replaced with new value, and false if current value is outdated (already changed by other party). - /// [!CDATA[ - /// var value = SomeRef.Value; - /// if (!SomeRef.TrySwapIfStillCurrent(value, Update(value)) - /// SomeRef.Swap(v => Update(v)); // fallback to normal Swap with delegate allocation - /// ]] - public bool TrySwapIfStillCurrent(T currentValue, T newValue) - { - return Interlocked.CompareExchange(ref _value, newValue, currentValue) == currentValue; - } - - private T _value; - } - - /// Provides optimistic-concurrency consistent operation. - public static class Ref - { - /// Factory for with type of value inference. - /// Type of value to wrap. - /// Initial value to wrap. - /// New ref. - public static Ref Of(T value) where T : class - { - return new Ref(value); - } - - /// Creates new ref to the value of original ref. Ref value type. - /// Original ref. New ref to original value. - public static Ref NewRef(this Ref original) where T : class - { - return Of(original.Value); - } - - /// First, it evaluates new value using function. - /// Second, it checks that original value is not changed. - /// If it is changed it will retry first step, otherwise it assigns new value and returns original (the one used for ). - /// Type of value to swap. - /// Reference to change to new value - /// Delegate to get value from old one. - /// Old/original value. By analogy with . - /// Important: May be called multiple times to retry update with value concurrently changed by other code. - public static T Swap(ref T value, Func getNewValue) where T : class - { - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - if (++retryCount > RETRY_COUNT_UNTIL_THROW) - throw new InvalidOperationException(_errorRetryCountExceeded); - } - } - - private const int RETRY_COUNT_UNTIL_THROW = 50; - private static readonly string _errorRetryCountExceeded = - "Ref retried to Update for " + RETRY_COUNT_UNTIL_THROW + " times But there is always someone else intervened."; - } - - /// Immutable Key-Value pair. It is reference type (could be check for null), - /// which is different from System value type . - /// In addition provides and implementations. - /// Type of Key.Type of Value. - public class KV - { - /// Key. - public readonly K Key; - - /// Value. - public readonly V Value; - - /// Creates Key-Value object by providing key and value. Does Not check either one for null. - /// key.value. - public KV(K key, V value) - { - Key = key; - Value = value; - } - - /// Creates nice string view.String representation. - public override string ToString() - { - var s = new StringBuilder('{'); - if (Key != null) - s.Append(Key); - s.Append(','); - if (Value != null) - s.Append(Value); - s.Append('}'); - return s.ToString(); - } - - /// Returns true if both key and value are equal to corresponding key-value of other object. - /// Object to check equality with. True if equal. - public override bool Equals(object obj) - { - var other = obj as KV; - return other != null - && (ReferenceEquals(other.Key, Key) || Equals(other.Key, Key)) - && (ReferenceEquals(other.Value, Value) || Equals(other.Value, Value)); - } - - /// Combines key and value hash code. R# generated default implementation. - /// Combined hash code for key-value. - public override int GetHashCode() - { - unchecked - { - return ((object)Key == null ? 0 : Key.GetHashCode() * 397) - ^ ((object)Value == null ? 0 : Value.GetHashCode()); - } - } - } - - /// Helpers for . - public static class KV - { - /// Creates the key value pair. - /// Key type Value type - /// Key Value New pair. - public static KV Of(K key, V value) - { - return new KV(key, value); - } - - /// Creates the new pair with new key and old value. - /// Key type Value type - /// Source value New key New pair - public static KV WithKey(this KV source, K key) - { - return new KV(key, source.Value); - } - - /// Creates the new pair with old key and new value. - /// Key type Value type - /// Source value New value. New pair - public static KV WithValue(this KV source, V value) - { - return new KV(source.Key, value); - } - } - - /// Simple helper for creation of the pair of two parts. - public static class KeyValuePair - { - /// Pairs key with value. - public static KeyValuePair Pair(this K key, V value) => - new KeyValuePair(key, value); - } - - /// Helper structure which allows to distinguish null value from the default value for optional parameter. - public struct Opt - { - /// Allows to transparently convert parameter argument to opt structure. - public static implicit operator Opt(T value) => new Opt(value); - - /// Argument value. - public T Value; - - /// Indicates that value is provided. - public bool HasValue; - - /// Wraps passed value in structure. Sets the flag that value is present. - public Opt(T value) - { - HasValue = true; - Value = value; - } - - /// Helper to get value or default value if value is not present. - public T OrDefault(T defaultValue = default(T)) => HasValue ? Value : defaultValue; - } - - /// Immutable list - simplest linked list with Head and Rest. - /// Type of the item. - public sealed class ImList - { - /// Empty list to Push to. - public static readonly ImList Empty = new ImList(); - - /// True for empty list. - public bool IsEmpty - { - get { return Tail == null; } - } - - /// First value in a list. - public readonly T Head; - - /// The rest of values or Empty if list has a single value. - public readonly ImList Tail; - - /// Prepends new value and returns new list. - /// New first value. - /// List with the new head. - public ImList Prep(T head) - { - return new ImList(head, this); - } - - /// Enumerates the list. - /// Each item in turn. - public IEnumerable Enumerate() - { - if (IsEmpty) - yield break; - for (var list = this; !list.IsEmpty; list = list.Tail) - yield return list.Head; - } - - #region Implementation - - private ImList() { } - - private ImList(T head, ImList tail) - { - Head = head; - Tail = tail; - } - - #endregion - } - - /// Extension methods providing basic operations on a list. - public static class ImList - { - /// This a basically a Fold function, to address needs in Map, Filter, Reduce. - /// Type of list item. - /// Type of result. - /// List to fold. - /// From were to start. - /// Collects list item into result - /// Return result or for empty list. - public static R To(this ImList source, R initialValue, Func collect) - { - if (source.IsEmpty) - return initialValue; - var value = initialValue; - for (; !source.IsEmpty; source = source.Tail) - value = collect(source.Head, value); - return value; - } - - /// Form of fold function with element index for convenience. - /// Type of list item. - /// Type of result. - /// List to fold. - /// From were to start. - /// Collects list item into result - /// Return result or for empty list. - public static R To(this ImList source, R initialValue, Func collect) - { - if (source.IsEmpty) - return initialValue; - var value = initialValue; - for (var i = 0; !source.IsEmpty; source = source.Tail) - value = collect(source.Head, i++, value); - return value; - } - - /// Returns new list in reverse order. - /// List item type List to reverse. - /// New list. If list consist on single element, then the same list. - public static ImList Reverse(this ImList source) - { - if (source.IsEmpty || source.Tail.IsEmpty) - return source; - return source.To(ImList.Empty, (it, _) => _.Prep(it)); - } - - /// Maps the items from the first list to the result list. - /// source item type. - /// result item type. - /// input list. converter func. - /// result list. - public static ImList Map(this ImList source, Func map) - { - return source.To(ImList.Empty, (it, _) => _.Prep(map(it))).Reverse(); - } - - /// Maps the items from the first list to the result list with item index. - /// source item type. - /// result item type. - /// input list. converter func. - /// result list. - public static ImList Map(this ImList source, Func map) - { - return source.To(ImList.Empty, (it, i, _) => _.Prep(map(it, i))).Reverse(); - } - - /// Copies list to array. - /// list to convert. - /// Array with list items. - public static T[] ToArray(this ImList source) - { - if (source.IsEmpty) - return ArrayTools.Empty(); - if (source.Tail.IsEmpty) - return new[] { source.Head }; - return source.Enumerate().ToArray(); - } - } - - /// Given the old value should and the new value should return result updated value. - public delegate V Update(V oldValue, V newValue); - - /// Immutable http://en.wikipedia.org/wiki/AVL_tree with integer keys and values. - public sealed class ImMap - { - /// Empty tree to start with. - public static readonly ImMap Empty = new ImMap(); - - /// Key. - public readonly int Key; - - /// Value. - public readonly V Value; - - /// Left sub-tree/branch, or empty. - public readonly ImMap Left; - - /// Right sub-tree/branch, or empty. - public readonly ImMap Right; - - /// Height of longest sub-tree/branch plus 1. It is 0 for empty tree, and 1 for single node tree. - public readonly int Height; - - /// Returns true is tree is empty. - public bool IsEmpty - { - get { return Height == 0; } - } - - /// Returns new tree with added or updated value for specified key. - /// - /// New tree. - public ImMap AddOrUpdate(int key, V value) - { - return AddOrUpdateImpl(key, value); - } - - /// Returns new tree with added or updated value for specified key. - /// Key Value - /// (optional) Delegate to calculate new value from and old and a new value. - /// New tree. - public ImMap AddOrUpdate(int key, V value, Update updateValue) - { - return AddOrUpdateImpl(key, value, false, updateValue); - } - - /// Returns new tree with updated value for the key, Or the same tree if key was not found. - /// - /// New tree if key is found, or the same tree otherwise. - public ImMap Update(int key, V value) - { - return AddOrUpdateImpl(key, value, true, null); - } - - /// Get value for found key or null otherwise. - /// (optional) Value to return if key is not found. - /// Found value or . - public V GetValueOrDefault(int key, V defaultValue = default(V)) - { - var node = this; - while (node.Height != 0 && node.Key != key) - node = key < node.Key ? node.Left : node.Right; - return node.Height != 0 ? node.Value : defaultValue; - } - - /// Returns true if key is found and sets the value. - /// Key to look for. Result value - /// True if key found, false otherwise. - public bool TryFind(int key, out V value) - { - var hash = key.GetHashCode(); - - var node = this; - while (node.Height != 0 && node.Key != key) - node = hash < node.Key ? node.Left : node.Right; - - if (node.Height != 0) - { - value = node.Value; - return true; - } - - value = default(V); - return false; - } - - /// Returns all sub-trees enumerated from left to right. - /// Enumerated sub-trees or empty if tree is empty. - public IEnumerable> Enumerate() - { - if (Height == 0) - yield break; - - var parents = new ImMap[Height]; - - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parents[++parentCount] = node; - node = node.Left; - } - else - { - node = parents[parentCount--]; - yield return node; - node = node.Right; - } - } - } - - /// Removes or updates value for specified key, or does nothing if key is not found. - /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx - /// Key to look for. - /// New tree with removed or updated value. - public ImMap Remove(int key) - { - return RemoveImpl(key); - } - - /// Outputs key value pair - public override string ToString() - { - return Key + " : " + Value; - } - - #region Implementation - - private ImMap() { } - - private ImMap(int key, V value) - { - Key = key; - Value = value; - Left = Empty; - Right = Empty; - Height = 1; - } - - private ImMap(int key, V value, ImMap left, ImMap right, int height) - { - Key = key; - Value = value; - Left = left; - Right = right; - Height = height; - } - - private ImMap(int key, V value, ImMap left, ImMap right) - { - Key = key; - Value = value; - Left = left; - Right = right; - Height = 1 + (left.Height > right.Height ? left.Height : right.Height); - } - - private ImMap AddOrUpdateImpl(int key, V value) - { - return Height == 0 // add new node - ? new ImMap(key, value) - : (key == Key // update found node - ? new ImMap(key, value, Left, Right) - : (key < Key // search for node - ? (Height == 1 - ? new ImMap(Key, Value, new ImMap(key, value), Right, height: 2) - : new ImMap(Key, Value, Left.AddOrUpdateImpl(key, value), Right).KeepBalance()) - : (Height == 1 - ? new ImMap(Key, Value, Left, new ImMap(key, value), height: 2) - : new ImMap(Key, Value, Left, Right.AddOrUpdateImpl(key, value)).KeepBalance()))); - } - - private ImMap AddOrUpdateImpl(int key, V value, bool updateOnly, Update update) - { - return Height == 0 ? // tree is empty - (updateOnly ? this : new ImMap(key, value)) - : (key == Key ? // actual update - new ImMap(key, update == null ? value : update(Value, value), Left, Right) - : (key < Key // try update on left or right sub-tree - ? new ImMap(Key, Value, Left.AddOrUpdateImpl(key, value, updateOnly, update), Right) - : new ImMap(Key, Value, Left, Right.AddOrUpdateImpl(key, value, updateOnly, update))) - .KeepBalance()); - } - - private ImMap KeepBalance() - { - var delta = Left.Height - Right.Height; - if (delta >= 2) // left is longer by 2, rotate left - { - var left = Left; - var leftLeft = left.Left; - var leftRight = left.Right; - if (leftRight.Height - leftLeft.Height == 1) - { - // double rotation: - // 5 => 5 => 4 - // 2 6 4 6 2 5 - // 1 4 2 3 1 3 6 - // 3 1 - return new ImMap(leftRight.Key, leftRight.Value, - left: new ImMap(left.Key, left.Value, - left: leftLeft, right: leftRight.Left), right: new ImMap(Key, Value, - left: leftRight.Right, right: Right)); - } - - // todo: do we need this? - // one rotation: - // 5 => 2 - // 2 6 1 5 - // 1 4 4 6 - return new ImMap(left.Key, left.Value, - left: leftLeft, right: new ImMap(Key, Value, - left: leftRight, right: Right)); - } - - if (delta <= -2) - { - var right = Right; - var rightLeft = right.Left; - var rightRight = right.Right; - if (rightLeft.Height - rightRight.Height == 1) - { - return new ImMap(rightLeft.Key, rightLeft.Value, - left: new ImMap(Key, Value, - left: Left, right: rightLeft.Left), right: new ImMap(right.Key, right.Value, - left: rightLeft.Right, right: rightRight)); - } - - return new ImMap(right.Key, right.Value, - left: new ImMap(Key, Value, - left: Left, right: rightLeft), right: rightRight); - } - - return this; - } - - private ImMap RemoveImpl(int key, bool ignoreKey = false) - { - if (Height == 0) - return this; - - ImMap result; - if (key == Key || ignoreKey) // found node - { - if (Height == 1) // remove node - return Empty; - - if (Right.IsEmpty) - result = Left; - else if (Left.IsEmpty) - result = Right; - else - { - // we have two children, so remove the next highest node and replace this node with it. - var successor = Right; - while (!successor.Left.IsEmpty) successor = successor.Left; - result = new ImMap(successor.Key, successor.Value, - Left, Right.RemoveImpl(successor.Key, ignoreKey: true)); - } - } - else if (key < Key) - result = new ImMap(Key, Value, Left.RemoveImpl(key), Right); - else - result = new ImMap(Key, Value, Left, Right.RemoveImpl(key)); - - return result.KeepBalance(); - } - - #endregion - } - - /// Immutable http://en.wikipedia.org/wiki/AVL_tree - /// where node key is the hash code of . - public sealed class ImHashMap - { - /// Empty tree to start with. - public static readonly ImHashMap Empty = new ImHashMap(); - - /// Calculated key hash. - public int Hash - { - get { return _data.Hash; } - } - - /// Key of type K that should support and . - public K Key - { - get { return _data.Key; } - } - - /// Value of any type V. - public V Value - { - get { return _data.Value; } - } - - /// In case of conflicts for different keys contains conflicted keys with their values. - public KV[] Conflicts - { - get { return _data.Conflicts; } - } - - /// Left sub-tree/branch, or empty. - public readonly ImHashMap Left; - - /// Right sub-tree/branch, or empty. - public readonly ImHashMap Right; - - /// Height of longest sub-tree/branch plus 1. It is 0 for empty tree, and 1 for single node tree. - public readonly int Height; - - /// Returns true if tree is empty. - public bool IsEmpty - { - get { return Height == 0; } - } - - /// Returns new tree with added key-value. - /// If value with the same key is exist then the value is replaced. - /// Key to add.Value to add. - /// New tree with added or updated key-value. - public ImHashMap AddOrUpdate(K key, V value) - { - return AddOrUpdate(key.GetHashCode(), key, value); - } - - /// Returns new tree with added key-value. If value with the same key is exist, then - /// if is not specified: then existing value will be replaced by ; - /// if is specified: then update delegate will decide what value to keep. - /// Key to add.Value to add. - /// Update handler. - /// New tree with added or updated key-value. - public ImHashMap AddOrUpdate(K key, V value, Update update) - { - return AddOrUpdate(key.GetHashCode(), key, value, update); - } - - /// Looks for and replaces its value with new , or - /// runs custom update handler () with old and new value to get the updated result. - /// Key to look for. - /// New value to replace key value with. - /// (optional) Delegate for custom update logic, it gets old and new - /// as inputs and should return updated value as output. - /// New tree with updated value or the SAME tree if no key found. - public ImHashMap Update(K key, V value, Update update = null) - { - return Update(key.GetHashCode(), key, value, update); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - /// Key to look for. (optional) Value to return if key is not found. - /// Found value or . - [MethodImpl((MethodImplOptions)256)] - public V GetValueOrDefault(K key, V defaultValue = default(V)) - { - var t = this; - var hash = key.GetHashCode(); - while (t.Height != 0 && t.Hash != hash) - t = hash < t.Hash ? t.Left : t.Right; - return t.Height != 0 && (ReferenceEquals(key, t.Key) || key.Equals(t.Key)) - ? t.Value : t.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Returns true if key is found and sets the value. - /// Key to look for. Result value - /// True if key found, false otherwise. - [MethodImpl((MethodImplOptions)256)] - public bool TryFind(K key, out V value) - { - var hash = key.GetHashCode(); - - var t = this; - while (t.Height != 0 && t._data.Hash != hash) - t = hash < t._data.Hash ? t.Left : t.Right; - - if (t.Height != 0 && (ReferenceEquals(key, t._data.Key) || key.Equals(t._data.Key))) - { - value = t._data.Value; - return true; - } - - return t.TryFindConflictedValue(key, out value); - } - - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up (~20% faster than stack). - /// Sequence of enumerated key value pairs. - public IEnumerable> Enumerate() - { - if (Height == 0) - yield break; - - var parents = new ImHashMap[Height]; - - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parents[++parentCount] = node; - node = node.Left; - } - else - { - node = parents[parentCount--]; - yield return new KV(node.Key, node.Value); - - if (node.Conflicts != null) - for (var i = 0; i < node.Conflicts.Length; i++) - yield return node.Conflicts[i]; - - node = node.Right; - } - } - } - - /// Removes or updates value for specified key, or does nothing if key is not found. - /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx - /// Key to look for. - /// New tree with removed or updated value. - public ImHashMap Remove(K key) - { - return Remove(key.GetHashCode(), key); - } - - /// Outputs key value pair - public override string ToString() - { - return Key + " : " + Value; - } - - #region Implementation - - private sealed class Data - { - public readonly int Hash; - public readonly K Key; - public readonly V Value; - - public readonly KV[] Conflicts; - - public Data() { } - - public Data(int hash, K key, V value, KV[] conflicts = null) - { - Hash = hash; - Key = key; - Value = value; - Conflicts = conflicts; - } - } - - private readonly Data _data; - - private ImHashMap() { _data = new Data(); } - - private ImHashMap(Data data) - { - _data = data; - Left = Empty; - Right = Empty; - Height = 1; - } - - private ImHashMap(Data data, ImHashMap left, ImHashMap right) - { - _data = data; - Left = left; - Right = right; - Height = 1 + (left.Height > right.Height ? left.Height : right.Height); - } - - private ImHashMap(Data data, ImHashMap left, ImHashMap right, int height) - { - _data = data; - Left = left; - Right = right; - Height = height; - } - - internal ImHashMap AddOrUpdate(int hash, K key, V value) - { - return Height == 0 // add new node - ? new ImHashMap(new Data(hash, key, value)) - : (hash == Hash // update found node - ? (ReferenceEquals(Key, key) || Key.Equals(key) - ? new ImHashMap(new Data(hash, key, value, Conflicts), Left, Right) - : UpdateValueAndResolveConflicts(key, value, null, false)) - : (hash < Hash // search for node - ? (Height == 1 - ? new ImHashMap(_data, - new ImHashMap(new Data(hash, key, value)), Right, height: 2) - : new ImHashMap(_data, - Left.AddOrUpdate(hash, key, value), Right).KeepBalance()) - : (Height == 1 - ? new ImHashMap(_data, - Left, new ImHashMap(new Data(hash, key, value)), height: 2) - : new ImHashMap(_data, - Left, Right.AddOrUpdate(hash, key, value)).KeepBalance()))); - } - - private ImHashMap AddOrUpdate(int hash, K key, V value, Update update) - { - return Height == 0 - ? new ImHashMap(new Data(hash, key, value)) - : (hash == Hash // update - ? (ReferenceEquals(Key, key) || Key.Equals(key) - ? new ImHashMap(new Data(hash, key, update(Value, value), Conflicts), Left, Right) - : UpdateValueAndResolveConflicts(key, value, update, false)) - : (hash < Hash - ? With(Left.AddOrUpdate(hash, key, value, update), Right) - : With(Left, Right.AddOrUpdate(hash, key, value, update))) - .KeepBalance()); - } - - internal ImHashMap Update(int hash, K key, V value, Update update) - { - return Height == 0 ? this - : (hash == Hash - ? (ReferenceEquals(Key, key) || Key.Equals(key) - ? new ImHashMap(new Data(hash, key, update == null ? value : update(Value, value), Conflicts), Left, Right) - : UpdateValueAndResolveConflicts(key, value, update, true)) - : (hash < Hash - ? With(Left.Update(hash, key, value, update), Right) - : With(Left, Right.Update(hash, key, value, update))) - .KeepBalance()); - } - - private ImHashMap UpdateValueAndResolveConflicts(K key, V value, Update update, bool updateOnly) - { - if (Conflicts == null) // add only if updateOnly is false. - return updateOnly ? this - : new ImHashMap(new Data(Hash, Key, Value, new[] { new KV(key, value) }), Left, Right); - - var found = Conflicts.Length - 1; - while (found >= 0 && !Equals(Conflicts[found].Key, Key)) --found; - if (found == -1) - { - if (updateOnly) return this; - var newConflicts = new KV[Conflicts.Length + 1]; - Array.Copy(Conflicts, 0, newConflicts, 0, Conflicts.Length); - newConflicts[Conflicts.Length] = new KV(key, value); - return new ImHashMap(new Data(Hash, Key, Value, newConflicts), Left, Right); - } - - var conflicts = new KV[Conflicts.Length]; - Array.Copy(Conflicts, 0, conflicts, 0, Conflicts.Length); - conflicts[found] = new KV(key, update == null ? value : update(Conflicts[found].Value, value)); - return new ImHashMap(new Data(Hash, Key, Value, conflicts), Left, Right); - } - - internal V GetConflictedValueOrDefault(K key, V defaultValue) - { - if (Conflicts != null) - for (var i = Conflicts.Length - 1; i >= 0; --i) - if (Equals(Conflicts[i].Key, key)) - return Conflicts[i].Value; - return defaultValue; - } - - private bool TryFindConflictedValue(K key, out V value) - { - if (Height != 0 && Conflicts != null) - for (var i = Conflicts.Length - 1; i >= 0; --i) - if (Equals(Conflicts[i].Key, key)) - { - value = Conflicts[i].Value; - return true; - } - - value = default(V); - return false; - } - - private ImHashMap KeepBalance() - { - var delta = Left.Height - Right.Height; - if (delta >= 2) // left is longer by 2, rotate left - { - var left = Left; - var leftLeft = left.Left; - var leftRight = left.Right; - if (leftRight.Height - leftLeft.Height == 1) - { - // double rotation: - // 5 => 5 => 4 - // 2 6 4 6 2 5 - // 1 4 2 3 1 3 6 - // 3 1 - return new ImHashMap(leftRight._data, - left: new ImHashMap(left._data, - left: leftLeft, right: leftRight.Left), right: new ImHashMap(_data, - left: leftRight.Right, right: Right)); - } - - // todo: do we need this? - // one rotation: - // 5 => 2 - // 2 6 1 5 - // 1 4 4 6 - return new ImHashMap(left._data, - left: leftLeft, right: new ImHashMap(_data, - left: leftRight, right: Right)); - } - - if (delta <= -2) - { - var right = Right; - var rightLeft = right.Left; - var rightRight = right.Right; - if (rightLeft.Height - rightRight.Height == 1) - { - return new ImHashMap(rightLeft._data, - left: new ImHashMap(_data, - left: Left, right: rightLeft.Left), right: new ImHashMap(right._data, - left: rightLeft.Right, right: rightRight)); - } - - return new ImHashMap(right._data, - left: new ImHashMap(_data, - left: Left, right: rightLeft), right: rightRight); - } - - return this; - } - - private ImHashMap With(ImHashMap left, ImHashMap right) - { - return left == Left && right == Right ? this : new ImHashMap(_data, left, right); - } - - internal ImHashMap Remove(int hash, K key, bool ignoreKey = false) - { - if (Height == 0) - return this; - - ImHashMap result; - if (hash == Hash) // found node - { - if (ignoreKey || Equals(Key, key)) - { - if (!ignoreKey && Conflicts != null) - return ReplaceRemovedWithConflicted(); - - if (Height == 1) // remove node - return Empty; - - if (Right.IsEmpty) - result = Left; - else if (Left.IsEmpty) - result = Right; - else - { - // we have two children, so remove the next highest node and replace this node with it. - var successor = Right; - while (!successor.Left.IsEmpty) successor = successor.Left; - result = new ImHashMap(successor._data, - Left, Right.Remove(successor.Hash, default(K), ignoreKey: true)); - } - } - else if (Conflicts != null) - return TryRemoveConflicted(key); - else - return this; // if key is not matching and no conflicts to lookup - just return - } - else if (hash < Hash) - result = new ImHashMap(_data, Left.Remove(hash, key, ignoreKey), Right); - else - result = new ImHashMap(_data, Left, Right.Remove(hash, key, ignoreKey)); - - if (result.Height == 1) - return result; - - return result.KeepBalance(); - } - - private ImHashMap TryRemoveConflicted(K key) - { - var index = Conflicts.Length - 1; - while (index >= 0 && !Equals(Conflicts[index].Key, key)) --index; - if (index == -1) // key is not found in conflicts - just return - return this; - - if (Conflicts.Length == 1) - return new ImHashMap(new Data(Hash, Key, Value), Left, Right); - var shrinkConflicts = new KV[Conflicts.Length - 1]; - var newIndex = 0; - for (var i = 0; i < Conflicts.Length; ++i) - if (i != index) shrinkConflicts[newIndex++] = Conflicts[i]; - return new ImHashMap(new Data(Hash, Key, Value, shrinkConflicts), Left, Right); - } - - private ImHashMap ReplaceRemovedWithConflicted() - { - if (Conflicts.Length == 1) - return new ImHashMap(new Data(Hash, Conflicts[0].Key, Conflicts[0].Value), Left, Right); - var shrinkConflicts = new KV[Conflicts.Length - 1]; - Array.Copy(Conflicts, 1, shrinkConflicts, 0, shrinkConflicts.Length); - return new ImHashMap(new Data(Hash, Conflicts[0].Key, Conflicts[0].Value, shrinkConflicts), Left, Right); - } - - #endregion - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.DryIocZero/MediatR.Examples.DryIocZero.csproj b/samples/MediatR.Examples.DryIocZero/MediatR.Examples.DryIocZero.csproj deleted file mode 100644 index 27b18cd0..00000000 --- a/samples/MediatR.Examples.DryIocZero/MediatR.Examples.DryIocZero.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - Exe - net461 - - - - - - - - - - - - - - - TextTemplatingFileGenerator - Container.Generated.cs - - - - - - - - - - True - True - Container.Generated.tt - - - - diff --git a/samples/MediatR.Examples.DryIocZero/Program.cs b/samples/MediatR.Examples.DryIocZero/Program.cs deleted file mode 100644 index 84d15121..00000000 --- a/samples/MediatR.Examples.DryIocZero/Program.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using DryIocZero; - -namespace MediatR.Examples.DryIocZero -{ - static class Program - { - public static Task Main() - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "DryIocZero"); - } - - private static IMediator BuildMediator(TextWriter writer) - { - var container = new Container(); - - container.RegisterDelegate(r => r.Resolve); - container.UseInstance(writer); - - return container.Resolve(); - } - } -} diff --git a/samples/MediatR.Examples.DryIocZero/Properties/launchSettings.json b/samples/MediatR.Examples.DryIocZero/Properties/launchSettings.json deleted file mode 100644 index 327b77ca..00000000 --- a/samples/MediatR.Examples.DryIocZero/Properties/launchSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "profiles": { - "MediatR.Examples.DryIocZero": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.DryIocZero/Registrations.ttinclude b/samples/MediatR.Examples.DryIocZero/Registrations.ttinclude deleted file mode 100644 index 17ce9cbf..00000000 --- a/samples/MediatR.Examples.DryIocZero/Registrations.ttinclude +++ /dev/null @@ -1,61 +0,0 @@ -<# -// TODO: -// 1. Fill-in `GetContainerWithRegistrations` method below with creation of DryIoc `Container` and registrations. -// 2. Specify the resolution roots via `SpecifyResolutionRoots`, see example below. -// 3. Re-save the "Container.Generated.tt" file. Confirm the Visual Studio prompt if any. -// 4. Check the "Container.Generated.cs" for the generated results and issues. -#> -<#@ assembly Name="$(DryIocAssembly)" #> -<#@ import Namespace="DryIoc" #> -<#@ import Namespace="ImTools" #> -<# -// TODO: Insert assemblies and namespaces with your services to register in container -#> -<#@ assembly Name="$(SolutionDir)src\MediatR\bin\Debug\net45\MediatR.dll" #> -<#@ import namespace="MediatR" #> -<#@ import namespace="MediatR.Pipeline" #> -<#@ assembly Name="$(SolutionDir)samples\MediatR.Examples\bin\Debug\net45\MediatR.Examples.dll" #> -<#@ import namespace="MediatR.Examples" #> -<#+ -// TODO: Specify the container and registrations ... -IContainer GetContainerWithRegistrations() -{ - var container = new Container(); - - container.RegisterPlaceholder(); - container.RegisterPlaceholder(); - - container.RegisterMany( - new[] { typeof(IMediator).GetAssembly(), typeof(Ping).GetAssembly() }, - Registrator.Interfaces); - - return container; -} - -// TODO: Filter the root services to generate expressions for ... -ServiceInfo[] SpecifyResolutionRoots(ServiceRegistrationInfo reg) -{ - var type = reg.ServiceType; - return // Skip not relevant service types - !(type.IsInterface() && type.Namespace?.StartsWith(nameof(MediatR)) == true) ? null - // Close known open-generics. - // NOTE: This only needed because multiple services are located! via `MultiInstanceFactory` instead of to be injected as collection - : type == typeof(IRequestHandler<,>) ? new []{ - reg.ToServiceInfo>(), - reg.ToServiceInfo>() - } - : type == typeof(IPipelineBehavior<,>) ? new []{ - reg.ToServiceInfo>(), - reg.ToServiceInfo>(), - } - : type == typeof(INotificationHandler<>) ? new []{ - reg.ToServiceInfo>(), - reg.ToServiceInfo>(), - } - // Default treatment - : reg.ToServiceInfo().One(); -} - -// TODO: Additional roots to generate ... -ServiceInfo[] CustomResolutionRoots = {}; -#> \ No newline at end of file diff --git a/samples/MediatR.Examples.Lamar/MediatR.Examples.Lamar.csproj b/samples/MediatR.Examples.Lamar/MediatR.Examples.Lamar.csproj deleted file mode 100644 index ae542eb6..00000000 --- a/samples/MediatR.Examples.Lamar/MediatR.Examples.Lamar.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net6.0 - - - - - - - - - - - diff --git a/samples/MediatR.Examples.Lamar/Program.cs b/samples/MediatR.Examples.Lamar/Program.cs deleted file mode 100644 index 730c9159..00000000 --- a/samples/MediatR.Examples.Lamar/Program.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Lamar; -using MediatR.Pipeline; - -namespace MediatR.Examples.Lamar; - -class Program -{ - static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "Lamar"); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(); - scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>)); - scanner.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>)); - scanner.ConnectImplementationsToTypesClosing(typeof(IRequestExceptionAction<>)); - scanner.ConnectImplementationsToTypesClosing(typeof(IRequestExceptionHandler<,,>)); - }); - - //Pipeline - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>)); - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionActionProcessorBehavior<,>)); - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPreProcessorBehavior<,>)); - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPostProcessorBehavior<,>)); - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(GenericPipelineBehavior<,>)); - cfg.For(typeof(IRequestPreProcessor<>)).Add(typeof(GenericRequestPreProcessor<>)); - cfg.For(typeof(IRequestPostProcessor<,>)).Add(typeof(GenericRequestPostProcessor<,>)); - cfg.For(typeof(IRequestPostProcessor<,>)).Add(typeof(ConstrainedRequestPostProcessor<,>)); - - //Constrained notification handlers - cfg.For(typeof(INotificationHandler<>)).Add(typeof(ConstrainedPingedHandler<>)); - - // This is the default but let's be explicit. At most we should be container scoped. - cfg.For().Use().Transient(); - - cfg.For().Use(ctx => ctx.GetInstance); - cfg.For().Use(writer); - }); - - - var mediator = container.GetInstance(); - - return mediator; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.LightInject/MediatR.Examples.LightInject.csproj b/samples/MediatR.Examples.LightInject/MediatR.Examples.LightInject.csproj deleted file mode 100644 index fc7d0ea3..00000000 --- a/samples/MediatR.Examples.LightInject/MediatR.Examples.LightInject.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.LightInject/Program.cs b/samples/MediatR.Examples.LightInject/Program.cs deleted file mode 100644 index 8f70992f..00000000 --- a/samples/MediatR.Examples.LightInject/Program.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using System.Threading.Tasks; -using LightInject; -using MediatR.Pipeline; - -namespace MediatR.Examples.LightInject; - -class Program -{ - static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "LightInject"); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var serviceContainer = new ServiceContainer(); - serviceContainer.Register(); - serviceContainer.RegisterInstance(writer); - - serviceContainer.RegisterAssembly(typeof(Ping).GetTypeInfo().Assembly, (serviceType, implementingType) => - serviceType.IsConstructedGenericType && - ( - serviceType.GetGenericTypeDefinition() == typeof(IRequestHandler<,>) || - serviceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>) - )); - - serviceContainer.RegisterOrdered(typeof(IPipelineBehavior<,>), - new[] - { - typeof(RequestPreProcessorBehavior<,>), - typeof(RequestPostProcessorBehavior<,>), - typeof(GenericPipelineBehavior<,>) - }, type => null); - - - serviceContainer.RegisterOrdered(typeof(IRequestPostProcessor<,>), - new[] - { - typeof(GenericRequestPostProcessor<,>), - typeof(ConstrainedRequestPostProcessor<,>) - }, type => null); - - serviceContainer.Register(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>)); - - - serviceContainer.Register(fac => fac.GetInstance); - return serviceContainer.GetInstance(); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Ninject/ContravariantBindingResolver.cs b/samples/MediatR.Examples.Ninject/ContravariantBindingResolver.cs deleted file mode 100644 index 52bf2beb..00000000 --- a/samples/MediatR.Examples.Ninject/ContravariantBindingResolver.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace MediatR.Examples.Ninject -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using global::Ninject.Components; - using global::Ninject.Infrastructure; - using global::Ninject.Planning.Bindings; - using global::Ninject.Planning.Bindings.Resolvers; - - public class ContravariantBindingResolver : NinjectComponent, IBindingResolver - { - /// - /// Returns any bindings from the specified collection that match the specified service. - /// - public IEnumerable Resolve(Multimap bindings, Type service) - { - if (service.IsGenericType) - { - var genericType = service.GetGenericTypeDefinition(); - var genericArguments = genericType.GetGenericArguments(); - if (genericArguments.Count() == 1 - && genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant)) - { - var argument = service.GetGenericArguments().Single(); - var matches = bindings.Where(kvp => kvp.Key.IsGenericType - && kvp.Key.GetGenericTypeDefinition().Equals(genericType) - && kvp.Key.GetGenericArguments().Single() != argument - && kvp.Key.GetGenericArguments().Single().IsAssignableFrom(argument)) - .SelectMany(kvp => kvp.Value); - return matches; - } - } - - return Enumerable.Empty(); - } - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Ninject/MediatR.Examples.Ninject.csproj b/samples/MediatR.Examples.Ninject/MediatR.Examples.Ninject.csproj deleted file mode 100644 index 966e060d..00000000 --- a/samples/MediatR.Examples.Ninject/MediatR.Examples.Ninject.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net461 - Exe - - - - - - - - - - - - - - - - - - diff --git a/samples/MediatR.Examples.Ninject/Program.cs b/samples/MediatR.Examples.Ninject/Program.cs deleted file mode 100644 index c00aa2b0..00000000 --- a/samples/MediatR.Examples.Ninject/Program.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using MediatR.Pipeline; -using Ninject.Syntax; - -namespace MediatR.Examples.Ninject -{ - using System; - using System.IO; - using global::Ninject; - using global::Ninject.Extensions.Conventions; - using global::Ninject.Planning.Bindings.Resolvers; - - internal class Program - { - private static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "Ninject"); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var kernel = new StandardKernel(); - kernel.Components.Add(); - kernel.Bind(scan => scan.FromAssemblyContaining().SelectAllClasses().BindDefaultInterface()); - kernel.Bind().ToConstant(writer); - - kernel.Bind(scan => scan.FromAssemblyContaining().SelectAllClasses().InheritedFrom(typeof(IRequestHandler<,>)).BindAllInterfaces()); - kernel.Bind(scan => scan.FromAssemblyContaining().SelectAllClasses().InheritedFrom(typeof(INotificationHandler<>)).BindAllInterfaces()); - - //Pipeline - kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(RequestPreProcessorBehavior<,>)); - kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(RequestPostProcessorBehavior<,>)); - kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(GenericPipelineBehavior<,>)); - kernel.Bind(typeof(IRequestPreProcessor<>)).To(typeof(GenericRequestPreProcessor<>)); - kernel.Bind(typeof(IRequestPostProcessor<,>)).To(typeof(GenericRequestPostProcessor<,>)); - kernel.Bind(typeof(IRequestPostProcessor<,>)).To(typeof(ConstrainedRequestPostProcessor<,>)); - kernel.Bind(typeof(INotificationHandler<>)).To(typeof(ConstrainedPingedHandler<>)).WhenNotificationMatchesType(); - - kernel.Bind().ToMethod(ctx => t => ctx.Kernel.TryGet(t)); - - var mediator = kernel.Get(); - - return mediator; - } - } - - public static class BindingExtensions - { - public static IBindingInNamedWithOrOnSyntax WhenNotificationMatchesType(this IBindingWhenSyntax syntax) - where TNotification : INotification - { - return syntax.When(request => typeof(TNotification).IsAssignableFrom(request.Service.GenericTypeArguments.Single())); - } - } - -} diff --git a/samples/MediatR.Examples.PublishStrategies/AsyncPingedHandler.cs b/samples/MediatR.Examples.PublishStrategies/AsyncPingedHandler.cs deleted file mode 100644 index 52700797..00000000 --- a/samples/MediatR.Examples.PublishStrategies/AsyncPingedHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.PublishStrategies; - -public class AsyncPingedHandler : INotificationHandler -{ - public AsyncPingedHandler(string name) - { - Name = name; - } - - public string Name { get; set; } - - public async Task Handle(Pinged notification, CancellationToken cancellationToken) - { - if (Name == "2") - { - throw new ArgumentException("Name cannot be '2'"); - } - - Console.WriteLine($"[AsyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : Pinged"); - await Task.Delay(100).ConfigureAwait(false); - Console.WriteLine($"[AsyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : After pinged"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.PublishStrategies/CustomMediator.cs b/samples/MediatR.Examples.PublishStrategies/CustomMediator.cs deleted file mode 100644 index 09585312..00000000 --- a/samples/MediatR.Examples.PublishStrategies/CustomMediator.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.PublishStrategies; - -public class CustomMediator : Mediator -{ - private Func>, INotification, CancellationToken, Task> _publish; - - public CustomMediator(ServiceFactory serviceFactory, Func>, INotification, CancellationToken, Task> publish) : base(serviceFactory) - { - _publish = publish; - } - - protected override Task PublishCore(IEnumerable> allHandlers, INotification notification, CancellationToken cancellationToken) - { - return _publish(allHandlers, notification, cancellationToken); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.PublishStrategies/MediatR.Examples.PublishStrategies.csproj b/samples/MediatR.Examples.PublishStrategies/MediatR.Examples.PublishStrategies.csproj deleted file mode 100644 index 5511431c..00000000 --- a/samples/MediatR.Examples.PublishStrategies/MediatR.Examples.PublishStrategies.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Exe - net6.0 - - - - - - - - - - - - diff --git a/samples/MediatR.Examples.PublishStrategies/Program.cs b/samples/MediatR.Examples.PublishStrategies/Program.cs deleted file mode 100644 index d9e84b72..00000000 --- a/samples/MediatR.Examples.PublishStrategies/Program.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; - -namespace MediatR.Examples.PublishStrategies; - -class Program -{ - static async Task Main(string[] args) - { - var services = new ServiceCollection(); - services.AddScoped(p => p.GetService); - - services.AddSingleton(); - - services.AddTransient>(sp => new SyncPingedHandler("1")); - services.AddTransient>(sp => new AsyncPingedHandler("2")); - services.AddTransient>(sp => new AsyncPingedHandler("3")); - services.AddTransient>(sp => new SyncPingedHandler("4")); - - var provider = services.BuildServiceProvider(); - - var publisher = provider.GetRequiredService(); - - var pinged = new Pinged(); - - foreach (PublishStrategy strategy in Enum.GetValues(typeof(PublishStrategy))) - { - Console.WriteLine($"Strategy: {strategy}"); - Console.WriteLine("----------"); - - try - { - await publisher.Publish(pinged, strategy); - } - catch (Exception ex) - { - Console.WriteLine($"{ex.GetType()}: {ex.Message}"); - } - - await Task.Delay(1000); - Console.WriteLine("----------"); - } - - Console.WriteLine("done"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.PublishStrategies/PublishStrategy.cs b/samples/MediatR.Examples.PublishStrategies/PublishStrategy.cs deleted file mode 100644 index b0b1e4c8..00000000 --- a/samples/MediatR.Examples.PublishStrategies/PublishStrategy.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace MediatR.Examples.PublishStrategies; - -/// -/// Strategy to use when publishing notifications -/// -public enum PublishStrategy -{ - /// - /// Run each notification handler after one another. Returns when all handlers are finished. In case of any exception(s), they will be captured in an AggregateException. - /// - SyncContinueOnException = 0, - - /// - /// Run each notification handler after one another. Returns when all handlers are finished or an exception has been thrown. In case of an exception, any handlers after that will not be run. - /// - SyncStopOnException = 1, - - /// - /// Run all notification handlers asynchronously. Returns when all handlers are finished. In case of any exception(s), they will be captured in an AggregateException. - /// - Async = 2, - - /// - /// Run each notification handler on its own thread using Task.Run(). Returns immediately and does not wait for any handlers to finish. Note that you cannot capture any exceptions, even if you await the call to Publish. - /// - ParallelNoWait = 3, - - /// - /// Run each notification handler on its own thread using Task.Run(). Returns when all threads (handlers) are finished. In case of any exception(s), they are captured in an AggregateException by Task.WhenAll. - /// - ParallelWhenAll = 4, - - /// - /// Run each notification handler on its own thread using Task.Run(). Returns when any thread (handler) is finished. Note that you cannot capture any exceptions (See msdn documentation of Task.WhenAny) - /// - ParallelWhenAny = 5, -} diff --git a/samples/MediatR.Examples.PublishStrategies/Publisher.cs b/samples/MediatR.Examples.PublishStrategies/Publisher.cs deleted file mode 100644 index 0a0d51cd..00000000 --- a/samples/MediatR.Examples.PublishStrategies/Publisher.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.PublishStrategies; - -public class Publisher -{ - private readonly ServiceFactory _serviceFactory; - - public Publisher(ServiceFactory serviceFactory) - { - _serviceFactory = serviceFactory; - - PublishStrategies[PublishStrategy.Async] = new CustomMediator(_serviceFactory, AsyncContinueOnException); - PublishStrategies[PublishStrategy.ParallelNoWait] = new CustomMediator(_serviceFactory, ParallelNoWait); - PublishStrategies[PublishStrategy.ParallelWhenAll] = new CustomMediator(_serviceFactory, ParallelWhenAll); - PublishStrategies[PublishStrategy.ParallelWhenAny] = new CustomMediator(_serviceFactory, ParallelWhenAny); - PublishStrategies[PublishStrategy.SyncContinueOnException] = new CustomMediator(_serviceFactory, SyncContinueOnException); - PublishStrategies[PublishStrategy.SyncStopOnException] = new CustomMediator(_serviceFactory, SyncStopOnException); - } - - public IDictionary PublishStrategies = new Dictionary(); - public PublishStrategy DefaultStrategy { get; set; } = PublishStrategy.SyncContinueOnException; - - public Task Publish(TNotification notification) - { - return Publish(notification, DefaultStrategy, default(CancellationToken)); - } - - public Task Publish(TNotification notification, PublishStrategy strategy) - { - return Publish(notification, strategy, default(CancellationToken)); - } - - public Task Publish(TNotification notification, CancellationToken cancellationToken) - { - return Publish(notification, DefaultStrategy, cancellationToken); - } - - public Task Publish(TNotification notification, PublishStrategy strategy, CancellationToken cancellationToken) - { - if (!PublishStrategies.TryGetValue(strategy, out var mediator)) - { - throw new ArgumentException($"Unknown strategy: {strategy}"); - } - - return mediator.Publish(notification, cancellationToken); - } - - private Task ParallelWhenAll(IEnumerable> handlers, INotification notification, CancellationToken cancellationToken) - { - var tasks = new List(); - - foreach (var handler in handlers) - { - tasks.Add(Task.Run(() => handler(notification, cancellationToken))); - } - - return Task.WhenAll(tasks); - } - - private Task ParallelWhenAny(IEnumerable> handlers, INotification notification, CancellationToken cancellationToken) - { - var tasks = new List(); - - foreach (var handler in handlers) - { - tasks.Add(Task.Run(() => handler(notification, cancellationToken))); - } - - return Task.WhenAny(tasks); - } - - private Task ParallelNoWait(IEnumerable> handlers, INotification notification, CancellationToken cancellationToken) - { - foreach (var handler in handlers) - { - Task.Run(() => handler(notification, cancellationToken)); - } - - return Task.CompletedTask; - } - - private async Task AsyncContinueOnException(IEnumerable> handlers, INotification notification, CancellationToken cancellationToken) - { - var tasks = new List(); - var exceptions = new List(); - - foreach (var handler in handlers) - { - try - { - tasks.Add(handler(notification, cancellationToken)); - } - catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException)) - { - exceptions.Add(ex); - } - } - - try - { - await Task.WhenAll(tasks).ConfigureAwait(false); - } - catch (AggregateException ex) - { - exceptions.AddRange(ex.Flatten().InnerExceptions); - } - catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException)) - { - exceptions.Add(ex); - } - - if (exceptions.Any()) - { - throw new AggregateException(exceptions); - } - } - - private async Task SyncStopOnException(IEnumerable> handlers, INotification notification, CancellationToken cancellationToken) - { - foreach (var handler in handlers) - { - await handler(notification, cancellationToken).ConfigureAwait(false); - } - } - - private async Task SyncContinueOnException(IEnumerable> handlers, INotification notification, CancellationToken cancellationToken) - { - var exceptions = new List(); - - foreach (var handler in handlers) - { - try - { - await handler(notification, cancellationToken).ConfigureAwait(false); - } - catch (AggregateException ex) - { - exceptions.AddRange(ex.Flatten().InnerExceptions); - } - catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException)) - { - exceptions.Add(ex); - } - } - - if (exceptions.Any()) - { - throw new AggregateException(exceptions); - } - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.PublishStrategies/SyncPingedHandler.cs b/samples/MediatR.Examples.PublishStrategies/SyncPingedHandler.cs deleted file mode 100644 index 0ee00386..00000000 --- a/samples/MediatR.Examples.PublishStrategies/SyncPingedHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.PublishStrategies; - -public class SyncPingedHandler : INotificationHandler -{ - public SyncPingedHandler(string name) - { - Name = name; - } - - public string Name { get; set; } - - public Task Handle(Pinged notification, CancellationToken cancellationToken) - { - if (Name == "2") - { - throw new ArgumentException("Name cannot be '2'"); - } - - Console.WriteLine($"[SyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : Pinged"); - Thread.Sleep(100); - Console.WriteLine($"[SyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : After pinged"); - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.SimpleInjector/MediatR.Examples.SimpleInjector.csproj b/samples/MediatR.Examples.SimpleInjector/MediatR.Examples.SimpleInjector.csproj deleted file mode 100644 index 8257e437..00000000 --- a/samples/MediatR.Examples.SimpleInjector/MediatR.Examples.SimpleInjector.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.SimpleInjector/Program.cs b/samples/MediatR.Examples.SimpleInjector/Program.cs deleted file mode 100644 index e6f20258..00000000 --- a/samples/MediatR.Examples.SimpleInjector/Program.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using MediatR.Pipeline; - -namespace MediatR.Examples.SimpleInjector; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using global::SimpleInjector; - -internal static class Program -{ - private static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "SimpleInjector", true); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new Container(); - var assemblies = GetAssemblies().ToArray(); - container.RegisterSingleton(); - container.Register(typeof(IRequestHandler<,>), assemblies); - - RegisterHandlers(container, typeof(INotificationHandler<>), assemblies); - RegisterHandlers(container, typeof(IRequestExceptionAction<,>), assemblies); - RegisterHandlers(container, typeof(IRequestExceptionHandler<,,>), assemblies); - RegisterHandlers(container, typeof(IStreamRequestHandler<,>), assemblies); - - container.Register(() => (TextWriter)writer, Lifestyle.Singleton); - - //Pipeline - container.Collection.Register(typeof(IPipelineBehavior<,>), new [] - { - typeof(RequestExceptionProcessorBehavior<,>), - typeof(RequestExceptionActionProcessorBehavior<,>), - typeof(RequestPreProcessorBehavior<,>), - typeof(RequestPostProcessorBehavior<,>), - typeof(GenericPipelineBehavior<,>) - }); - container.Collection.Register(typeof(IRequestPreProcessor<>), new [] { typeof(GenericRequestPreProcessor<>) }); - container.Collection.Register(typeof(IRequestPostProcessor<,>), new[] { typeof(GenericRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>) }); - container.Collection.Register(typeof(IStreamPipelineBehavior<,>), new[] - { - typeof(GenericStreamPipelineBehavior<,>) - }); - - container.Register(() => new ServiceFactory(container.GetInstance), Lifestyle.Singleton); - - container.Verify(); - - var mediator = container.GetInstance(); - - return mediator; - } - - private static void RegisterHandlers(Container container, Type collectionType, Assembly[] assemblies) - { - // we have to do this because by default, generic type definitions (such as the Constrained Notification Handler) won't be registered - var handlerTypes = container.GetTypesToRegister(collectionType, assemblies, new TypesToRegisterOptions - { - IncludeGenericTypeDefinitions = true, - IncludeComposites = false, - }); - - container.Collection.Register(collectionType, handlerTypes); - } - - private static IEnumerable GetAssemblies() - { - yield return typeof(IMediator).GetTypeInfo().Assembly; - yield return typeof(Ping).GetTypeInfo().Assembly; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Stashbox/MediatR.Examples.Stashbox.csproj b/samples/MediatR.Examples.Stashbox/MediatR.Examples.Stashbox.csproj deleted file mode 100644 index 23a54e24..00000000 --- a/samples/MediatR.Examples.Stashbox/MediatR.Examples.Stashbox.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net6.0 - - - - - - - - - - diff --git a/samples/MediatR.Examples.Stashbox/Program.cs b/samples/MediatR.Examples.Stashbox/Program.cs deleted file mode 100644 index 0019f262..00000000 --- a/samples/MediatR.Examples.Stashbox/Program.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Stashbox; -using Stashbox.Configuration; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace MediatR.Examples.Stashbox; - -class Program -{ - static Task Main() - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - return Runner.Run(mediator, writer, "Stashbox", testStreams: true); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new StashboxContainer() - .RegisterInstance(writer) - .Register(c => c.WithFactory(r => type => r.Resolve(type))) - .RegisterAssemblies(new[] { typeof(Mediator).Assembly, typeof(Ping).Assembly }, - serviceTypeSelector: Rules.ServiceRegistrationFilters.Interfaces, registerSelf: false); - - return container.Resolve(); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.StructureMap/MediatR.Examples.StructureMap.csproj b/samples/MediatR.Examples.StructureMap/MediatR.Examples.StructureMap.csproj deleted file mode 100644 index 2c71c4e8..00000000 --- a/samples/MediatR.Examples.StructureMap/MediatR.Examples.StructureMap.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.StructureMap/Program.cs b/samples/MediatR.Examples.StructureMap/Program.cs deleted file mode 100644 index 90d1beba..00000000 --- a/samples/MediatR.Examples.StructureMap/Program.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Threading.Tasks; -using MediatR.Pipeline; -using StructureMap.Pipeline; - -namespace MediatR.Examples.StructureMap; - -using System; -using System.IO; -using global::StructureMap; - -class Program -{ - static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "StructureMap"); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(); - scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>)); - scanner.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>)); - }); - - //Pipeline - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPreProcessorBehavior<,>)); - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPostProcessorBehavior<,>)); - cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(GenericPipelineBehavior<,>)); - cfg.For(typeof(IRequestPreProcessor<>)).Add(typeof(GenericRequestPreProcessor<>)); - cfg.For(typeof(IRequestPostProcessor<,>)).Add(typeof(GenericRequestPostProcessor<,>)); - cfg.For(typeof(IRequestPostProcessor<,>)).Add(typeof(ConstrainedRequestPostProcessor<,>)); - - //Constrained notification handlers - cfg.For(typeof(INotificationHandler<>)).Add(typeof(ConstrainedPingedHandler<>)); - - // This is the default but let's be explicit. At most we should be container scoped. - cfg.For().LifecycleIs().Use(); - - cfg.For().Use(ctx => ctx.GetInstance); - cfg.For().Use(writer); - }); - - - var mediator = container.GetInstance(); - - return mediator; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Unity/MediatR.Examples.Unity.csproj b/samples/MediatR.Examples.Unity/MediatR.Examples.Unity.csproj deleted file mode 100644 index cbe118c0..00000000 --- a/samples/MediatR.Examples.Unity/MediatR.Examples.Unity.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.Unity/Program.cs b/samples/MediatR.Examples.Unity/Program.cs deleted file mode 100644 index f7974903..00000000 --- a/samples/MediatR.Examples.Unity/Program.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using MediatR.Pipeline; -using Unity; -using Unity.Lifetime; - -namespace MediatR.Examples.Unity; - -internal class Program -{ - private static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - writer.WriteLine("Unity is not a very good container and breaks immediately"); - - return Runner.Run(mediator, writer, "Unity"); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new UnityContainer(); - - container.RegisterInstance(writer) - .RegisterMediator(new HierarchicalLifetimeManager()) - .RegisterMediatorHandlers(Assembly.GetAssembly(typeof(Ping))); - - container.RegisterType(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>), "RequestPreProcessorBehavior"); - container.RegisterType(typeof(IPipelineBehavior<,>), typeof(RequestPostProcessorBehavior<,>), "RequestPostProcessorBehavior"); - container.RegisterType(typeof(IPipelineBehavior<,>), typeof(GenericPipelineBehavior<,>), "GenericPipelineBehavior"); - container.RegisterType(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>), "GenericRequestPreProcessor"); - container.RegisterType(typeof(IRequestPostProcessor<,>), typeof(GenericRequestPostProcessor<,>), "GenericRequestPostProcessor"); - container.RegisterType(typeof(IRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>), "ConstrainedRequestPostProcessor"); - - // Unity doesn't support generic constraints - //container.RegisterType(typeof(INotificationHandler<>), typeof(ConstrainedPingedHandler<>), "ConstrainedPingedHandler"); - - return container.Resolve(); - } -} - -// ReSharper disable once InconsistentNaming -public static class IUnityContainerExtensions -{ - public static IUnityContainer RegisterMediator(this IUnityContainer container, ITypeLifetimeManager lifetimeManager) - { - return container.RegisterType(lifetimeManager) - .RegisterInstance(type => - { - var enumerableType = type - .GetInterfaces() - .Concat(new[] { type }) - .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); - - return enumerableType != null - ? container.ResolveAll(enumerableType.GetGenericArguments()[0]) - : container.IsRegistered(type) - ? container.Resolve(type) - : null; - }); - } - - public static IUnityContainer RegisterMediatorHandlers(this IUnityContainer container, Assembly assembly) - { - return container.RegisterTypesImplementingType(assembly, typeof(IRequestHandler<,>)) - .RegisterNamedTypesImplementingType(assembly, typeof(INotificationHandler<>)); - } - - internal static bool IsGenericTypeOf(this Type type, Type genericType) - { - return type.IsGenericType && - type.GetGenericTypeDefinition() == genericType; - } - - internal static void AddGenericTypes(this List list, IUnityContainer container, Type genericType) - { - var genericHandlerRegistrations = - container.Registrations.Where(reg => reg.RegisteredType == genericType); - - foreach (var handlerRegistration in genericHandlerRegistrations) - { - if (list.All(item => item.GetType() != handlerRegistration.MappedToType)) - { - list.Add(container.Resolve(handlerRegistration.MappedToType)); - } - } - } - - /// - /// Register all implementations of a given type for provided assembly. - /// - public static IUnityContainer RegisterTypesImplementingType(this IUnityContainer container, Assembly assembly, Type type) - { - foreach (var implementation in assembly.GetTypes().Where(t => t.GetInterfaces().Any(implementation => IsSubclassOfRawGeneric(type, implementation)))) - { - var interfaces = implementation.GetInterfaces(); - foreach (var @interface in interfaces) - container.RegisterType(@interface, implementation); - } - - return container; - } - - /// - /// Register all implementations of a given type for provided assembly. - /// - public static IUnityContainer RegisterNamedTypesImplementingType(this IUnityContainer container, Assembly assembly, Type type) - { - foreach (var implementation in assembly.GetTypes().Where(t => t.GetInterfaces().Any(implementation => IsSubclassOfRawGeneric(type, implementation)))) - { - var interfaces = implementation.GetInterfaces(); - foreach (var @interface in interfaces) - container.RegisterType(@interface, implementation, implementation.FullName); - } - - return container; - } - - private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) - { - while (toCheck != null && toCheck != typeof(object)) - { - var currentType = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; - if (generic == currentType) - return true; - - toCheck = toCheck.BaseType; - } - - return false; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Windsor/ContravariantFilter.cs b/samples/MediatR.Examples.Windsor/ContravariantFilter.cs deleted file mode 100644 index f60b32b7..00000000 --- a/samples/MediatR.Examples.Windsor/ContravariantFilter.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MediatR.Examples.Windsor; - -using System; -using System.Linq; -using System.Reflection; -using Castle.MicroKernel; - -public class ContravariantFilter : IHandlersFilter -{ - public bool HasOpinionAbout(Type service) - { - if (!service.IsGenericType) - return false; - - var genericType = service.GetGenericTypeDefinition(); - var genericArguments = genericType.GetGenericArguments(); - return genericArguments.Count() == 1 - && genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant); - } - - public IHandler[] SelectHandlers(Type service, IHandler[] handlers) - { - return handlers; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples.Windsor/MediatR.Examples.Windsor.csproj b/samples/MediatR.Examples.Windsor/MediatR.Examples.Windsor.csproj deleted file mode 100644 index 42de0193..00000000 --- a/samples/MediatR.Examples.Windsor/MediatR.Examples.Windsor.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net6.0 - Exe - - - - - - - - - - - diff --git a/samples/MediatR.Examples.Windsor/Program.cs b/samples/MediatR.Examples.Windsor/Program.cs deleted file mode 100644 index 8303a085..00000000 --- a/samples/MediatR.Examples.Windsor/Program.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Castle.MicroKernel.Resolvers.SpecializedResolvers; -using MediatR.Pipeline; - -namespace MediatR.Examples.Windsor; - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using Castle.MicroKernel; -using Castle.MicroKernel.Registration; -using Castle.Windsor; - -internal class Program -{ - private static Task Main(string[] args) - { - var writer = new WrappingWriter(Console.Out); - var mediator = BuildMediator(writer); - - return Runner.Run(mediator, writer, "Castle.Windsor", true); - } - - private static IMediator BuildMediator(WrappingWriter writer) - { - var container = new WindsorContainer(); - container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel)); - container.Kernel.AddHandlersFilter(new ContravariantFilter()); - - // *** The default lifestyle for Windsor is Singleton - // *** If you are using ASP.net, it's better to register your services with 'Per Web Request LifeStyle'. - - var fromAssemblyContainingPing = Classes.FromAssemblyContaining(); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(INotificationHandler<>)).WithServiceAllInterfaces().AllowMultipleMatches()); - container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionProcessorBehavior<,>))); - container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionActionProcessorBehavior<,>))); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionAction<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionHandler<,,>)).WithServiceAllInterfaces().AllowMultipleMatches()); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(IStreamRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPreProcessor<>)).WithServiceAllInterfaces().AllowMultipleMatches()); - container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPostProcessor<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); - - container.Register(Component.For().ImplementedBy()); - container.Register(Component.For().Instance(writer)); - container.Register(Component.For().UsingFactoryMethod(k => (type => - { - var enumerableType = type - .GetInterfaces() - .Concat(new[] { type }) - .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); - - var service = enumerableType?.GetGenericArguments()?[0]; - var resolvedType = enumerableType != null ? k.ResolveAll(service) : k.Resolve(type); - var genericArguments = service?.GetGenericArguments(); - - // Handle exceptions even using the base request types for IRequestExceptionHandler<,,> - var isRequestExceptionHandler = service?.GetGenericTypeDefinition() - ?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false; - if (isRequestExceptionHandler) - return ResolveRequestExceptionHandler(k, type, service, resolvedType, genericArguments); - - // Handle exceptions even using the base request types for IRequestExceptionAction<,> - var isRequestExceptionAction = service?.GetGenericTypeDefinition() - ?.IsAssignableTo(typeof(IRequestExceptionAction<,>)) ?? false; - if (isRequestExceptionAction) - return ResolveRequestExceptionAction(k, type, service, resolvedType, genericArguments); - - return resolvedType; - }))); - - //Pipeline - container.Register(Component.For(typeof(IStreamPipelineBehavior<,>)).ImplementedBy(typeof(GenericStreamPipelineBehavior<,>))); - container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestPreProcessorBehavior<,>)).NamedAutomatically("PreProcessorBehavior")); - container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestPostProcessorBehavior<,>)).NamedAutomatically("PostProcessorBehavior")); - container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(GenericPipelineBehavior<,>)).NamedAutomatically("Pipeline")); - container.Register(Component.For(typeof(IRequestPreProcessor<>)).ImplementedBy(typeof(GenericRequestPreProcessor<>)).NamedAutomatically("PreProcessor")); - container.Register(Component.For(typeof(IRequestPostProcessor<,>)).ImplementedBy(typeof(GenericRequestPostProcessor<,>)).NamedAutomatically("PostProcessor")); - container.Register(Component.For(typeof(IRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>)).NamedAutomatically("ConstrainedRequestPostProcessor")); - container.Register(Component.For(typeof(INotificationHandler<>), typeof(ConstrainedPingedHandler<>)).NamedAutomatically("ConstrainedPingedHandler")); - - var mediator = container.Resolve(); - - return mediator; - } - - private static object ResolveRequestExceptionHandler(IKernel k, Type type, Type service, object resolvedType, Type[] genericArguments) - { - if (service == null - || genericArguments == null - || !service.IsInterface - || !service.IsGenericType - || !service.IsConstructedGenericType - || !(service.GetGenericTypeDefinition() - ?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false) - || genericArguments.Length != 3) - { - return resolvedType; - } - - var serviceFactory = k.Resolve(); - var baseRequestType = genericArguments[0].BaseType; - var response = genericArguments[1]; - var exceptionType = genericArguments[2]; - - // Check if the base request type is valid - if (baseRequestType == null - || !baseRequestType.IsClass - || baseRequestType == typeof(object) - || ((!baseRequestType.GetInterfaces() - ?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true)) - { - return resolvedType; - } - - var exceptionHandlerInterfaceType = typeof(IRequestExceptionHandler<,,>).MakeGenericType(baseRequestType, response, exceptionType); - var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType); - Array resultArray = CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(type, resolvedType, serviceFactory, enumerableExceptionHandlerInterfaceType); - - return resultArray; - } - - private static object ResolveRequestExceptionAction(IKernel k, Type type, Type service, object resolvedType, Type[] genericArguments) - { - if (service == null - || genericArguments == null - || !service.IsInterface - || !service.IsGenericType - || !service.IsConstructedGenericType - || !(service.GetGenericTypeDefinition() - ?.IsAssignableTo(typeof(IRequestExceptionAction<,>)) ?? false) - || genericArguments.Length != 2) - { - return resolvedType; - } - - var serviceFactory = k.Resolve(); - var baseRequestType = genericArguments[0].BaseType; - var exceptionType = genericArguments[1]; - - // Check if the base request type is valid - if (baseRequestType == null - || !baseRequestType.IsClass - || baseRequestType == typeof(object) - || ((!baseRequestType.GetInterfaces() - ?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true)) - { - return resolvedType; - } - - var exceptionHandlerInterfaceType = typeof(IRequestExceptionAction<,>).MakeGenericType(baseRequestType, exceptionType); - var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType); - Array resultArray = CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(type, resolvedType, serviceFactory, enumerableExceptionHandlerInterfaceType); - - return resultArray; - } - - private static Array CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(Type type, object resolvedType, ServiceFactory serviceFactory, Type enumerableExceptionHandlerInterfaceType) - { - var firstArray = serviceFactory.Invoke(enumerableExceptionHandlerInterfaceType) as Array; - Debug.Assert(firstArray != null, $"Array '{nameof(firstArray)}' should not be null because this method calls ResolveAll when a {typeof(IEnumerable<>).FullName} " + - $"is passed as argument in argument named '{nameof(type)}'"); - - var secondArray = resolvedType is Array ? resolvedType as Array : new[] { resolvedType }; - Debug.Assert(secondArray != null, $"Array '{nameof(secondArray)}' should not be null because '{nameof(resolvedType)}' is an array or created as an array"); - - var resultArray = Array.CreateInstance(typeof(object), firstArray.Length + secondArray.Length); - Array.Copy(firstArray, resultArray, firstArray.Length); - Array.Copy(secondArray, 0, resultArray, firstArray.Length, secondArray.Length); - return resultArray; - } -} diff --git a/samples/MediatR.Examples/ConstrainedRequestPostProcessor.cs b/samples/MediatR.Examples/ConstrainedRequestPostProcessor.cs deleted file mode 100644 index 62e2364d..00000000 --- a/samples/MediatR.Examples/ConstrainedRequestPostProcessor.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediatR.Pipeline; - -namespace MediatR.Examples; - -public class ConstrainedRequestPostProcessor - : IRequestPostProcessor - where TRequest : Ping, IRequest -{ - private readonly TextWriter _writer; - - public ConstrainedRequestPostProcessor(TextWriter writer) - { - _writer = writer; - } - - public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("- All Done with Ping"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/ExceptionHandler/Exceptions.cs b/samples/MediatR.Examples/ExceptionHandler/Exceptions.cs deleted file mode 100644 index 3ecb79fd..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/Exceptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace MediatR.Examples.ExceptionHandler; - -public class ConnectionException : Exception { } - -public class ForbiddenException : ConnectionException { } - -public class ResourceNotFoundException : ConnectionException { } - -public class ServerException : Exception { } \ No newline at end of file diff --git a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs b/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs deleted file mode 100644 index 84441fd1..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs +++ /dev/null @@ -1,83 +0,0 @@ -using MediatR.Pipeline; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.ExceptionHandler; - -public class CommonExceptionHandler : AsyncRequestExceptionHandler -{ - private readonly TextWriter _writer; - - public CommonExceptionHandler(TextWriter writer) => _writer = writer; - - protected override async Task Handle(PingResource request, - Exception exception, - RequestExceptionHandlerState state, - CancellationToken cancellationToken) - { - // Exception type name must be written in messages by LogExceptionAction before - // Exception handler type name required because it is checked later in messages - await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false); - - state.SetHandled(new Pong()); - } -} - -public class ConnectionExceptionHandler : IRequestExceptionHandler -{ - private readonly TextWriter _writer; - - public ConnectionExceptionHandler(TextWriter writer) => _writer = writer; - - public async Task Handle(PingResource request, - ConnectionException exception, - RequestExceptionHandlerState state, - CancellationToken cancellationToken) - { - // Exception type name must be written in messages by LogExceptionAction before - // Exception handler type name required because it is checked later in messages - await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ConnectionExceptionHandler).FullName}'").ConfigureAwait(false); - - state.SetHandled(new Pong()); - } -} - -public class AccessDeniedExceptionHandler : IRequestExceptionHandler -{ - private readonly TextWriter _writer; - - public AccessDeniedExceptionHandler(TextWriter writer) => _writer = writer; - - public async Task Handle(PingResource request, - ForbiddenException exception, - RequestExceptionHandlerState state, - CancellationToken cancellationToken) - { - // Exception type name must be written in messages by LogExceptionAction before - // Exception handler type name required because it is checked later in messages - await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(AccessDeniedExceptionHandler).FullName}'").ConfigureAwait(false); - - state.SetHandled(new Pong()); - } -} - -public class ServerExceptionHandler : IRequestExceptionHandler -{ - private readonly TextWriter _writer; - - public ServerExceptionHandler(TextWriter writer) => _writer = writer; - - public virtual async Task Handle(PingNewResource request, - ServerException exception, - RequestExceptionHandlerState state, - CancellationToken cancellationToken) - { - // Exception type name must be written in messages by LogExceptionAction before - // Exception handler type name required because it is checked later in messages - await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false); - - state.SetHandled(new Pong()); - } -} diff --git a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs b/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs deleted file mode 100644 index ebd48d87..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediatR.Pipeline; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.ExceptionHandler.Overrides; - -public class CommonExceptionHandler : AsyncRequestExceptionHandler -{ - private readonly TextWriter _writer; - - public CommonExceptionHandler(TextWriter writer) => _writer = writer; - - protected override async Task Handle(PingResourceTimeout request, - Exception exception, - RequestExceptionHandlerState state, - CancellationToken cancellationToken) - { - // Exception type name must be written in messages by LogExceptionAction before - // Exception handler type name required because it is checked later in messages - await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false); - - state.SetHandled(new Pong()); - } -} - -public class ServerExceptionHandler : ExceptionHandler.ServerExceptionHandler -{ - private readonly TextWriter _writer; - - public ServerExceptionHandler(TextWriter writer) : base(writer) => _writer = writer; - - public override async Task Handle(PingNewResource request, - ServerException exception, - RequestExceptionHandlerState state, - CancellationToken cancellationToken) - { - // Exception type name must be written in messages by LogExceptionAction before - // Exception handler type name required because it is checked later in messages - await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false); - - state.SetHandled(new Pong()); - } -} diff --git a/samples/MediatR.Examples/ExceptionHandler/Handlers.cs b/samples/MediatR.Examples/ExceptionHandler/Handlers.cs deleted file mode 100644 index 13e25b4b..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/Handlers.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples.ExceptionHandler; - -public class PingResourceHandler : IRequestHandler -{ - private readonly TextWriter _writer; - - public PingResourceHandler(TextWriter writer) => _writer = writer; - - public Task Handle(PingResource request, CancellationToken cancellationToken) - { - throw new ResourceNotFoundException(); - } -} - -public class PingNewResourceHandler : IRequestHandler -{ - private readonly TextWriter _writer; - - public PingNewResourceHandler(TextWriter writer) => _writer = writer; - - public Task Handle(PingNewResource request, CancellationToken cancellationToken) - { - throw new ServerException(); - } -} - -public class PingResourceTimeoutHandler : IRequestHandler -{ - private readonly TextWriter _writer; - - public PingResourceTimeoutHandler(TextWriter writer) => _writer = writer; - - public Task Handle(PingResourceTimeout request, CancellationToken cancellationToken) - { - throw new TaskCanceledException(); - } -} - -public class PingResourceTimeoutOverrideHandler : IRequestHandler -{ - private readonly TextWriter _writer; - - public PingResourceTimeoutOverrideHandler(TextWriter writer) => _writer = writer; - - public Task Handle(Overrides.PingResourceTimeout request, CancellationToken cancellationToken) - { - throw new TaskCanceledException(); - } -} - -public class PingProtectedResourceHandler : IRequestHandler -{ - private readonly TextWriter _writer; - - public PingProtectedResourceHandler(TextWriter writer) => _writer = writer; - - public Task Handle(PingProtectedResource request, CancellationToken cancellationToken) - { - throw new ForbiddenException(); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/ExceptionHandler/LogExceptionAction.cs b/samples/MediatR.Examples/ExceptionHandler/LogExceptionAction.cs deleted file mode 100644 index 458bb7c4..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/LogExceptionAction.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MediatR.Pipeline; -using System; -using System.IO; - -namespace MediatR.Examples.ExceptionHandler; - -public class LogExceptionAction : RequestExceptionAction -{ - private readonly TextWriter _writer; - - public LogExceptionAction(TextWriter writer) => _writer = writer; - - protected override void Execute(Ping request, Exception exception) - { - _writer.WriteLineAsync($"--- Exception: '{exception.GetType().FullName}'"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/ExceptionHandler/Requests.cs b/samples/MediatR.Examples/ExceptionHandler/Requests.cs deleted file mode 100644 index 0cc894c4..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/Requests.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MediatR.Examples.ExceptionHandler; - -public class PingResource : Ping { } - -public class PingNewResource : Ping { } - -public class PingResourceTimeout : PingResource { } - -public class PingProtectedResource : PingResource { } \ No newline at end of file diff --git a/samples/MediatR.Examples/ExceptionHandler/RequestsOverrides.cs b/samples/MediatR.Examples/ExceptionHandler/RequestsOverrides.cs deleted file mode 100644 index d480f67d..00000000 --- a/samples/MediatR.Examples/ExceptionHandler/RequestsOverrides.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace MediatR.Examples.ExceptionHandler.Overrides; - -public class PingResourceTimeout : ExceptionHandler.PingResourceTimeout { } \ No newline at end of file diff --git a/samples/MediatR.Examples/GenericHandler.cs b/samples/MediatR.Examples/GenericHandler.cs deleted file mode 100644 index 584337df..00000000 --- a/samples/MediatR.Examples/GenericHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples; - -public class GenericHandler : INotificationHandler -{ - private readonly TextWriter _writer; - - public GenericHandler(TextWriter writer) - { - _writer = writer; - } - - public Task Handle(INotification notification, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("Got notified."); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/GenericPipelineBehavior.cs b/samples/MediatR.Examples/GenericPipelineBehavior.cs deleted file mode 100644 index 195d8adc..00000000 --- a/samples/MediatR.Examples/GenericPipelineBehavior.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples; - -public class GenericPipelineBehavior : IPipelineBehavior - where TRequest : IRequest -{ - private readonly TextWriter _writer; - - public GenericPipelineBehavior(TextWriter writer) - { - _writer = writer; - } - - public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) - { - await _writer.WriteLineAsync("-- Handling Request"); - var response = await next(); - await _writer.WriteLineAsync("-- Finished Request"); - return response; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/GenericRequestPostProcessor.cs b/samples/MediatR.Examples/GenericRequestPostProcessor.cs deleted file mode 100644 index 564de890..00000000 --- a/samples/MediatR.Examples/GenericRequestPostProcessor.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediatR.Pipeline; - -namespace MediatR.Examples; - -public class GenericRequestPostProcessor : IRequestPostProcessor - where TRequest : IRequest -{ - private readonly TextWriter _writer; - - public GenericRequestPostProcessor(TextWriter writer) - { - _writer = writer; - } - - public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("- All Done"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/GenericRequestPreProcessor.cs b/samples/MediatR.Examples/GenericRequestPreProcessor.cs deleted file mode 100644 index e53d9367..00000000 --- a/samples/MediatR.Examples/GenericRequestPreProcessor.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediatR.Pipeline; - -namespace MediatR.Examples; - -public class GenericRequestPreProcessor : IRequestPreProcessor -{ - private readonly TextWriter _writer; - - public GenericRequestPreProcessor(TextWriter writer) - { - _writer = writer; - } - - public Task Process(TRequest request, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("- Starting Up"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Jing.cs b/samples/MediatR.Examples/Jing.cs deleted file mode 100644 index 4164812e..00000000 --- a/samples/MediatR.Examples/Jing.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Jing : IRequest -{ - public string Message { get; set; } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/JingHandler.cs b/samples/MediatR.Examples/JingHandler.cs deleted file mode 100644 index a5010273..00000000 --- a/samples/MediatR.Examples/JingHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples; - -public class JingHandler : AsyncRequestHandler -{ - private readonly TextWriter _writer; - - public JingHandler(TextWriter writer) - { - _writer = writer; - } - - protected override Task Handle(Jing request, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync($"--- Handled Jing: {request.Message}, no Jong"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/MediatR.Examples.csproj b/samples/MediatR.Examples/MediatR.Examples.csproj deleted file mode 100644 index a6e65bf0..00000000 --- a/samples/MediatR.Examples/MediatR.Examples.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - netstandard2.1 - - - - - - - - - - - diff --git a/samples/MediatR.Examples/Ping.cs b/samples/MediatR.Examples/Ping.cs deleted file mode 100644 index 8a632dae..00000000 --- a/samples/MediatR.Examples/Ping.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Ping : IRequest -{ - public string Message { get; set; } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/PingHandler.cs b/samples/MediatR.Examples/PingHandler.cs deleted file mode 100644 index b451d49d..00000000 --- a/samples/MediatR.Examples/PingHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.IO; -using System.Threading; - -namespace MediatR.Examples; - -using System.Threading.Tasks; - -public class PingHandler : IRequestHandler -{ - private readonly TextWriter _writer; - - public PingHandler(TextWriter writer) - { - _writer = writer; - } - - public async Task Handle(Ping request, CancellationToken cancellationToken) - { - await _writer.WriteLineAsync($"--- Handled Ping: {request.Message}"); - return new Pong { Message = request.Message + " Pong" }; - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Pinged.cs b/samples/MediatR.Examples/Pinged.cs deleted file mode 100644 index 8df96afd..00000000 --- a/samples/MediatR.Examples/Pinged.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Pinged : INotification -{ - -} \ No newline at end of file diff --git a/samples/MediatR.Examples/PingedHandler.cs b/samples/MediatR.Examples/PingedHandler.cs deleted file mode 100644 index 3bcddafa..00000000 --- a/samples/MediatR.Examples/PingedHandler.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Threading; - -namespace MediatR.Examples; - -using System.IO; -using System.Threading.Tasks; - -public class PingedHandler : INotificationHandler -{ - private readonly TextWriter _writer; - - public PingedHandler(TextWriter writer) - { - _writer = writer; - } - - public Task Handle(Pinged notification, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("Got pinged async."); - } -} - -public class PongedHandler : INotificationHandler -{ - private readonly TextWriter _writer; - - public PongedHandler(TextWriter writer) - { - _writer = writer; - } - - public Task Handle(Ponged notification, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("Got ponged async."); - } -} - -public class ConstrainedPingedHandler : INotificationHandler - where TNotification : Pinged -{ - private readonly TextWriter _writer; - - public ConstrainedPingedHandler(TextWriter writer) - { - _writer = writer; - } - - public Task Handle(TNotification notification, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("Got pinged constrained async."); - } -} - -public class PingedAlsoHandler : INotificationHandler -{ - private readonly TextWriter _writer; - - public PingedAlsoHandler(TextWriter writer) - { - _writer = writer; - } - - public Task Handle(Pinged notification, CancellationToken cancellationToken) - { - return _writer.WriteLineAsync("Got pinged also async."); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Pong.cs b/samples/MediatR.Examples/Pong.cs deleted file mode 100644 index 4e69a594..00000000 --- a/samples/MediatR.Examples/Pong.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Pong -{ - public string Message { get; set; } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Ponged.cs b/samples/MediatR.Examples/Ponged.cs deleted file mode 100644 index b92c2bb4..00000000 --- a/samples/MediatR.Examples/Ponged.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Ponged : INotification -{ - -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Runner.cs b/samples/MediatR.Examples/Runner.cs deleted file mode 100644 index 20b9ed6e..00000000 --- a/samples/MediatR.Examples/Runner.cs +++ /dev/null @@ -1,343 +0,0 @@ -using System; -using System.Linq; -using System.Text; - -namespace MediatR.Examples; - -using MediatR.Examples.ExceptionHandler; -using System.IO; -using System.Threading.Tasks; - -public static class Runner -{ - public static async Task Run(IMediator mediator, WrappingWriter writer, string projectName, bool testStreams = false) - { - await writer.WriteLineAsync("==============="); - await writer.WriteLineAsync(projectName); - await writer.WriteLineAsync("==============="); - await writer.WriteLineAsync(); - - await writer.WriteLineAsync("Sending Ping..."); - var pong = await mediator.Send(new Ping { Message = "Ping" }); - await writer.WriteLineAsync("Received: " + pong.Message); - await writer.WriteLineAsync(); - - await writer.WriteLineAsync("Publishing Pinged..."); - await mediator.Publish(new Pinged()); - await writer.WriteLineAsync(); - - await writer.WriteLineAsync("Publishing Ponged..."); - var failedPong = false; - try - { - await mediator.Publish(new Ponged()); - } - catch (Exception e) - { - failedPong = true; - await writer.WriteLineAsync(e.ToString()); - } - await writer.WriteLineAsync(); - - var failedJing = false; - await writer.WriteLineAsync("Sending Jing..."); - try - { - await mediator.Send(new Jing { Message = "Jing" }); - } - catch (Exception e) - { - failedJing = true; - await writer.WriteLineAsync(e.ToString()); - } - await writer.WriteLineAsync(); - - bool failedSing = false; - if (testStreams) - { - await writer.WriteLineAsync("Sending Sing..."); - try - { - int i = 0; - await foreach (Song s in mediator.CreateStream(new Sing { Message = "Sing" })) - { - if (i == 0) { - failedSing = !(s.Message.Contains("Singing do")); - } - else if (i == 1) - { - failedSing = !(s.Message.Contains("Singing re")); - } - else if (i == 2) - { - failedSing = !(s.Message.Contains("Singing mi")); - } - else if (i == 3) - { - failedSing = !(s.Message.Contains("Singing fa")); - } - else if (i == 4) - { - failedSing = !(s.Message.Contains("Singing so")); - } - else if (i == 5) - { - failedSing = !(s.Message.Contains("Singing la")); - } - else if (i == 6) - { - failedSing = !(s.Message.Contains("Singing ti")); - } - else if (i == 7) - { - failedSing = !(s.Message.Contains("Singing do")); - } - - failedSing = failedSing || (++i) > 10; - } - } - catch (Exception e) - { - failedSing = true; - await writer.WriteLineAsync(e.ToString()); - } - await writer.WriteLineAsync(); - } - - var isHandlerForSameExceptionWorks = await IsHandlerForSameExceptionWorks(mediator, writer).ConfigureAwait(false); - var isHandlerForBaseExceptionWorks = await IsHandlerForBaseExceptionWorks(mediator, writer).ConfigureAwait(false); - var isHandlerForLessSpecificExceptionWorks = await IsHandlerForLessSpecificExceptionWorks(mediator, writer).ConfigureAwait(false); - var isPreferredHandlerForBaseExceptionWorks = await IsPreferredHandlerForBaseExceptionWorks(mediator, writer).ConfigureAwait(false); - var isOverriddenHandlerForBaseExceptionWorks = await IsOverriddenHandlerForBaseExceptionWorks(mediator, writer).ConfigureAwait(false); - - await writer.WriteLineAsync("---------------"); - var contents = writer.Contents; - var order = new[] { - contents.IndexOf("- Starting Up", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("-- Handling Request", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("--- Handled Ping", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("-- Finished Request", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("- All Done", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("- All Done with Ping", StringComparison.OrdinalIgnoreCase), - }; - - var streamOrder = new[] { - contents.IndexOf("-- Handling StreamRequest", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("--- Handled Sing: Sing, Song", StringComparison.OrdinalIgnoreCase), - contents.IndexOf("-- Finished StreamRequest", StringComparison.OrdinalIgnoreCase), - }; - - var results = new RunResults - { - RequestHandlers = contents.Contains("--- Handled Ping:"), - VoidRequestsHandlers = contents.Contains("--- Handled Jing:"), - PipelineBehaviors = contents.Contains("-- Handling Request"), - RequestPreProcessors = contents.Contains("- Starting Up"), - RequestPostProcessors = contents.Contains("- All Done"), - ConstrainedGenericBehaviors = contents.Contains("- All Done with Ping") && !failedJing, - OrderedPipelineBehaviors = order.SequenceEqual(order.OrderBy(i => i)), - NotificationHandler = contents.Contains("Got pinged async"), - MultipleNotificationHandlers = contents.Contains("Got pinged async") && contents.Contains("Got pinged also async"), - ConstrainedGenericNotificationHandler = contents.Contains("Got pinged constrained async") && !failedPong, - CovariantNotificationHandler = contents.Contains("Got notified"), - HandlerForSameException = isHandlerForSameExceptionWorks, - HandlerForBaseException = isHandlerForBaseExceptionWorks, - HandlerForLessSpecificException = isHandlerForLessSpecificExceptionWorks, - PreferredHandlerForBaseException = isPreferredHandlerForBaseExceptionWorks, - OverriddenHandlerForBaseException = isOverriddenHandlerForBaseExceptionWorks, - - // Streams - StreamRequestHandlers = contents.Contains("--- Handled Sing: Sing, Song") && !failedSing, - StreamPipelineBehaviors = contents.Contains("-- Handling StreamRequest"), - StreamOrderedPipelineBehaviors = streamOrder.SequenceEqual(streamOrder.OrderBy(i => i)) - }; - - await writer.WriteLineAsync($"Request Handler....................................................{(results.RequestHandlers ? "Y" : "N")}"); - await writer.WriteLineAsync($"Void Request Handler...............................................{(results.VoidRequestsHandlers ? "Y" : "N")}"); - await writer.WriteLineAsync($"Pipeline Behavior..................................................{(results.PipelineBehaviors ? "Y" : "N")}"); - await writer.WriteLineAsync($"Pre-Processor......................................................{(results.RequestPreProcessors ? "Y" : "N")}"); - await writer.WriteLineAsync($"Post-Processor.....................................................{(results.RequestPostProcessors ? "Y" : "N")}"); - await writer.WriteLineAsync($"Constrained Post-Processor.........................................{(results.ConstrainedGenericBehaviors ? "Y" : "N")}"); - await writer.WriteLineAsync($"Ordered Behaviors..................................................{(results.OrderedPipelineBehaviors ? "Y" : "N")}"); - await writer.WriteLineAsync($"Notification Handler...............................................{(results.NotificationHandler ? "Y" : "N")}"); - await writer.WriteLineAsync($"Notification Handlers..............................................{(results.MultipleNotificationHandlers ? "Y" : "N")}"); - await writer.WriteLineAsync($"Constrained Notification Handler...................................{(results.ConstrainedGenericNotificationHandler ? "Y" : "N")}"); - await writer.WriteLineAsync($"Covariant Notification Handler.....................................{(results.CovariantNotificationHandler ? "Y" : "N")}"); - await writer.WriteLineAsync($"Handler for inherited request with same exception used.............{(results.HandlerForSameException ? "Y" : "N")}"); - await writer.WriteLineAsync($"Handler for inherited request with base exception used.............{(results.HandlerForBaseException ? "Y" : "N")}"); - await writer.WriteLineAsync($"Handler for request with less specific exception used by priority..{(results.HandlerForLessSpecificException ? "Y" : "N")}"); - await writer.WriteLineAsync($"Preferred handler for inherited request with base exception used...{(results.PreferredHandlerForBaseException ? "Y" : "N")}"); - await writer.WriteLineAsync($"Overridden handler for inherited request with same exception used..{(results.OverriddenHandlerForBaseException ? "Y" : "N")}"); - - if (testStreams) - { - await writer.WriteLineAsync($"Stream Request Handler.............................................{(results.StreamRequestHandlers ? "Y" : "N")}"); - await writer.WriteLineAsync($"Stream Pipeline Behavior...........................................{(results.StreamPipelineBehaviors ? "Y" : "N")}"); - await writer.WriteLineAsync($"Stream Ordered Behaviors...........................................{(results.StreamOrderedPipelineBehaviors ? "Y" : "N")}"); - } - - await writer.WriteLineAsync(); - } - - private static async Task IsHandlerForSameExceptionWorks(IMediator mediator, WrappingWriter writer) - { - var isHandledCorrectly = false; - - await writer.WriteLineAsync("Checking handler to catch exact exception..."); - try - { - await mediator.Send(new PingProtectedResource { Message = "Ping to protected resource" }); - isHandledCorrectly = IsExceptionHandledBy(writer); - } - catch (Exception e) - { - await writer.WriteLineAsync(e.Message); - } - await writer.WriteLineAsync(); - - return isHandledCorrectly; - } - - private static async Task IsHandlerForBaseExceptionWorks(IMediator mediator, WrappingWriter writer) - { - var isHandledCorrectly = false; - - await writer.WriteLineAsync("Checking shared handler to catch exception by base type..."); - try - { - await mediator.Send(new PingResource { Message = "Ping to missed resource" }); - isHandledCorrectly = IsExceptionHandledBy(writer); - } - catch (Exception e) - { - await writer.WriteLineAsync(e.Message); - } - await writer.WriteLineAsync(); - - return isHandledCorrectly; - } - - private static async Task IsHandlerForLessSpecificExceptionWorks(IMediator mediator, WrappingWriter writer) - { - var isHandledCorrectly = false; - - await writer.WriteLineAsync("Checking base handler to catch any exception..."); - try - { - await mediator.Send(new PingResourceTimeout { Message = "Ping to ISS resource" }); - isHandledCorrectly = IsExceptionHandledBy (writer); - } - catch (Exception e) - { - await writer.WriteLineAsync(e.Message); - } - await writer.WriteLineAsync(); - - return isHandledCorrectly; - } - - private static async Task IsPreferredHandlerForBaseExceptionWorks(IMediator mediator, WrappingWriter writer) - { - var isHandledCorrectly = false; - - await writer.WriteLineAsync("Selecting preferred handler to handle exception..."); - - try - { - await mediator.Send(new ExceptionHandler.Overrides.PingResourceTimeout { Message = "Ping to ISS resource (preferred)" }); - isHandledCorrectly = IsExceptionHandledBy (writer); - } - catch (Exception e) - { - await writer.WriteLineAsync(e.Message); - } - await writer.WriteLineAsync(); - - return isHandledCorrectly; - } - - private static async Task IsOverriddenHandlerForBaseExceptionWorks(IMediator mediator, WrappingWriter writer) - { - var isHandledCorrectly = false; - - await writer.WriteLineAsync("Selecting new handler to handle exception..."); - - try - { - await mediator.Send(new PingNewResource { Message = "Ping to ISS resource (override)" }); - isHandledCorrectly = IsExceptionHandledBy (writer); - } - catch (Exception e) - { - await writer.WriteLineAsync(e.Message); - } - await writer.WriteLineAsync(); - - return isHandledCorrectly; - } - - private static bool IsExceptionHandledBy(WrappingWriter writer) - where TException : Exception - { - var messages = writer.Contents.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList(); - if (messages.Count - 3 < 0) - return false; - - // Note: For this handler type to be found in messages, it must be written in messages by LogExceptionAction - return messages[messages.Count - 2].Contains(typeof(THandler).FullName) - // Note: For this exception type to be found in messages, exception must be written in all tested exception handlers - && messages[messages.Count - 3].Contains(typeof(TException).FullName); - } -} - -public class RunResults -{ - public bool RequestHandlers { get; set; } - public bool VoidRequestsHandlers { get; set; } - public bool PipelineBehaviors { get; set; } - public bool RequestPreProcessors { get; set; } - public bool RequestPostProcessors { get; set; } - public bool OrderedPipelineBehaviors { get; set; } - public bool ConstrainedGenericBehaviors { get; set; } - public bool NotificationHandler { get; set; } - public bool MultipleNotificationHandlers { get; set; } - public bool CovariantNotificationHandler { get; set; } - public bool ConstrainedGenericNotificationHandler { get; set; } - public bool HandlerForSameException { get; set; } - public bool HandlerForBaseException { get; set; } - public bool HandlerForLessSpecificException { get; set; } - public bool PreferredHandlerForBaseException { get; set; } - public bool OverriddenHandlerForBaseException { get; set; } - - // Stream results - public bool StreamRequestHandlers { get; set; } - public bool StreamPipelineBehaviors { get; set; } - public bool StreamOrderedPipelineBehaviors { get; set; } -} - -public class WrappingWriter : TextWriter -{ - private readonly TextWriter _innerWriter; - private readonly StringBuilder _stringWriter = new StringBuilder(); - - public WrappingWriter(TextWriter innerWriter) - { - _innerWriter = innerWriter; - } - - public override void Write(char value) - { - _stringWriter.Append(value); - _innerWriter.Write(value); - } - - public override Task WriteLineAsync(string value) - { - _stringWriter.AppendLine(value); - return _innerWriter.WriteLineAsync(value); - } - - public override Encoding Encoding => _innerWriter.Encoding; - - public string Contents => _stringWriter.ToString(); -} diff --git a/samples/MediatR.Examples/Streams/GenericStreamPipelineBehavior.cs b/samples/MediatR.Examples/Streams/GenericStreamPipelineBehavior.cs deleted file mode 100644 index 8ab1384b..00000000 --- a/samples/MediatR.Examples/Streams/GenericStreamPipelineBehavior.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples; - -public class GenericStreamPipelineBehavior : IStreamPipelineBehavior - where TRequest : IStreamRequest -{ - private readonly TextWriter _writer; - - public GenericStreamPipelineBehavior(TextWriter writer) - { - _writer = writer; - } - - public async IAsyncEnumerable Handle(TRequest request, StreamHandlerDelegate next, [EnumeratorCancellation]CancellationToken cancellationToken) - { - await _writer.WriteLineAsync("-- Handling StreamRequest"); - await foreach (var response in next().WithCancellation(cancellationToken).ConfigureAwait(false)) - { - yield return response; - } - await _writer.WriteLineAsync("-- Finished StreamRequest"); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Streams/Sing.cs b/samples/MediatR.Examples/Streams/Sing.cs deleted file mode 100644 index d59b7176..00000000 --- a/samples/MediatR.Examples/Streams/Sing.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Sing : IStreamRequest -{ - public string Message { get; set; } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Streams/SingHandler.cs b/samples/MediatR.Examples/Streams/SingHandler.cs deleted file mode 100644 index 5f63843d..00000000 --- a/samples/MediatR.Examples/Streams/SingHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace MediatR.Examples; - -public class SingHandler : IStreamRequestHandler -{ - private readonly TextWriter _writer; - - public SingHandler(TextWriter writer) - { - _writer = writer; - } - - public async IAsyncEnumerable Handle(Sing request, [EnumeratorCancellation]CancellationToken cancellationToken) - { - await _writer.WriteLineAsync($"--- Handled Sing: {request.Message}, Song"); - yield return await Task.Run(() => new Song { Message = request.Message + "ing do" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing re" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing mi" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing fa" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing so" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing la" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing ti" }); - yield return await Task.Run(() => new Song { Message = request.Message + "ing do" }); - } -} \ No newline at end of file diff --git a/samples/MediatR.Examples/Streams/Song.cs b/samples/MediatR.Examples/Streams/Song.cs deleted file mode 100644 index 64364d4b..00000000 --- a/samples/MediatR.Examples/Streams/Song.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MediatR.Examples; - -public class Song -{ - public string Message { get; set; } -} \ No newline at end of file diff --git a/src/MediatR/MediatR.csproj b/src/MediatR/MediatR.csproj index a5085668..57a12299 100644 --- a/src/MediatR/MediatR.csproj +++ b/src/MediatR/MediatR.csproj @@ -28,6 +28,7 @@ + diff --git a/src/MediatR/Mediator.cs b/src/MediatR/Mediator.cs index 974aff0c..79a66077 100644 --- a/src/MediatR/Mediator.cs +++ b/src/MediatR/Mediator.cs @@ -13,7 +13,7 @@ namespace MediatR; /// public class Mediator : IMediator { - private readonly ServiceFactory _serviceFactory; + private readonly IServiceProvider _serviceProvider; private static readonly ConcurrentDictionary _requestHandlers = new(); private static readonly ConcurrentDictionary _notificationHandlers = new(); private static readonly ConcurrentDictionary _streamRequestHandlers = new(); @@ -21,9 +21,9 @@ public class Mediator : IMediator /// /// Initializes a new instance of the class. /// - /// The single instance factory. - public Mediator(ServiceFactory serviceFactory) - => _serviceFactory = serviceFactory; + /// Service provider. Can be a scoped or root provider + public Mediator(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider; public Task Send(IRequest request, CancellationToken cancellationToken = default) { @@ -38,7 +38,7 @@ public Task Send(IRequest request, Cancellation static t => (RequestHandlerBase)(Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(t, typeof(TResponse))) ?? throw new InvalidOperationException($"Could not create wrapper type for {t}"))); - return handler.Handle(request, _serviceFactory, cancellationToken); + return handler.Handle(request, _serviceProvider, cancellationToken); } public Task Send(object request, CancellationToken cancellationToken = default) @@ -68,7 +68,7 @@ public Task Send(IRequest request, Cancellation }); // call via dynamic dispatch to avoid calling through reflection for performance reasons - return handler.Handle(request, _serviceFactory, cancellationToken); + return handler.Handle(request, _serviceProvider, cancellationToken); } public Task Publish(TNotification notification, CancellationToken cancellationToken = default) @@ -112,7 +112,7 @@ private Task PublishNotification(INotification notification, CancellationToken c static t => (NotificationHandlerWrapper) (Activator.CreateInstance(typeof(NotificationHandlerWrapperImpl<>).MakeGenericType(t)) ?? throw new InvalidOperationException($"Could not create wrapper for type {t}"))); - return handler.Handle(notification, _serviceFactory, PublishCore, cancellationToken); + return handler.Handle(notification, _serviceProvider, PublishCore, cancellationToken); } @@ -128,7 +128,7 @@ public IAsyncEnumerable CreateStream(IStreamRequest) _streamRequestHandlers.GetOrAdd(requestType, t => (StreamRequestHandlerBase) Activator.CreateInstance(typeof(StreamRequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse)))); - var items = streamHandler.Handle(request, _serviceFactory, cancellationToken); + var items = streamHandler.Handle(request, _serviceProvider, cancellationToken); return items; } @@ -161,7 +161,7 @@ public IAsyncEnumerable CreateStream(IStreamRequest TypeEvaluator { get; private set; } = t => true; + public Type MediatorImplementationType { get; private set; } + public ServiceLifetime Lifetime { get; private set; } + public RequestExceptionActionProcessorStrategy RequestExceptionActionProcessorStrategy { get; set; } + + public MediatRServiceConfiguration() + { + MediatorImplementationType = typeof(Mediator); + Lifetime = ServiceLifetime.Transient; + } + + public MediatRServiceConfiguration Using() where TMediator : IMediator + { + MediatorImplementationType = typeof(TMediator); + return this; + } + + public MediatRServiceConfiguration AsSingleton() + { + Lifetime = ServiceLifetime.Singleton; + return this; + } + + public MediatRServiceConfiguration AsScoped() + { + Lifetime = ServiceLifetime.Scoped; + return this; + } + + public MediatRServiceConfiguration AsTransient() + { + Lifetime = ServiceLifetime.Transient; + return this; + } + + public MediatRServiceConfiguration WithEvaluator(Func evaluator) + { + TypeEvaluator = evaluator; + return this; + } +} \ No newline at end of file diff --git a/src/MediatR/MicrosoftExtensionsDI/RequestExceptionActionProcessorStrategy.cs b/src/MediatR/MicrosoftExtensionsDI/RequestExceptionActionProcessorStrategy.cs new file mode 100644 index 00000000..09ec296b --- /dev/null +++ b/src/MediatR/MicrosoftExtensionsDI/RequestExceptionActionProcessorStrategy.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Extensions.DependencyInjection; + +public enum RequestExceptionActionProcessorStrategy +{ + ApplyForUnhandledExceptions, + ApplyForAllExceptions +} \ No newline at end of file diff --git a/src/MediatR/MicrosoftExtensionsDI/ServiceCollectionExtensions.cs b/src/MediatR/MicrosoftExtensionsDI/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..3376069b --- /dev/null +++ b/src/MediatR/MicrosoftExtensionsDI/ServiceCollectionExtensions.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using MediatR; +using MediatR.Pipeline; +using MediatR.Registration; + +namespace Microsoft.Extensions.DependencyInjection; + +/// +/// Extensions to scan for MediatR handlers and registers them. +/// - Scans for any handler interface implementations and registers them as +/// - Scans for any and implementations and registers them as transient instances +/// Registers as a transient instance +/// After calling AddMediatR you can use the container to resolve an instance. +/// This does not scan for any instances including and . +/// To register behaviors, use the with the open generic or closed generic types. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers handlers and mediator types from the specified assemblies + /// + /// Service collection + /// Assemblies to scan + /// Service collection + public static IServiceCollection AddMediatR(this IServiceCollection services, params Assembly[] assemblies) + => services.AddMediatR(assemblies, configuration: null); + + /// + /// Registers handlers and mediator types from the specified assemblies + /// + /// Service collection + /// Assemblies to scan + /// The action used to configure the options + /// Service collection + public static IServiceCollection AddMediatR(this IServiceCollection services, Action? configuration, params Assembly[] assemblies) + => services.AddMediatR(assemblies, configuration); + + /// + /// Registers handlers and mediator types from the specified assemblies + /// + /// Service collection + /// Assemblies to scan + /// The action used to configure the options + /// Service collection + public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable assemblies, Action? configuration) + { + if (!assemblies.Any()) + { + throw new ArgumentException("No assemblies found to scan. Supply at least one assembly to scan for handlers."); + } + var serviceConfig = new MediatRServiceConfiguration(); + + configuration?.Invoke(serviceConfig); + + ServiceRegistrar.AddRequiredServices(services, serviceConfig); + + ServiceRegistrar.AddMediatRClasses(services, assemblies, serviceConfig); + + return services; + } + + /// + /// Registers handlers and mediator types from the assemblies that contain the specified types + /// + /// + /// + /// Service collection + public static IServiceCollection AddMediatR(this IServiceCollection services, params Type[] handlerAssemblyMarkerTypes) + => services.AddMediatR(handlerAssemblyMarkerTypes, configuration: null); + + /// + /// Registers handlers and mediator types from the assemblies that contain the specified types + /// + /// + /// + /// The action used to configure the options + /// Service collection + public static IServiceCollection AddMediatR(this IServiceCollection services, Action? configuration, params Type[] handlerAssemblyMarkerTypes) + => services.AddMediatR(handlerAssemblyMarkerTypes, configuration); + + /// + /// Registers handlers and mediator types from the assemblies that contain the specified types + /// + /// + /// + /// The action used to configure the options + /// Service collection + public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable handlerAssemblyMarkerTypes, Action? configuration) + => services.AddMediatR(handlerAssemblyMarkerTypes.Select(t => t.GetTypeInfo().Assembly), configuration); +} \ No newline at end of file diff --git a/src/MediatR/Pipeline/RequestExceptionActionProcessorBehavior.cs b/src/MediatR/Pipeline/RequestExceptionActionProcessorBehavior.cs index 702be133..f1354454 100644 --- a/src/MediatR/Pipeline/RequestExceptionActionProcessorBehavior.cs +++ b/src/MediatR/Pipeline/RequestExceptionActionProcessorBehavior.cs @@ -19,9 +19,9 @@ namespace MediatR.Pipeline; public class RequestExceptionActionProcessorBehavior : IPipelineBehavior where TRequest : IRequest { - private readonly ServiceFactory _serviceFactory; + private readonly IServiceProvider _serviceProvider; - public RequestExceptionActionProcessorBehavior(ServiceFactory serviceFactory) => _serviceFactory = serviceFactory; + public RequestExceptionActionProcessorBehavior(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) { @@ -72,7 +72,7 @@ private static IEnumerable GetExceptionTypes(Type? exceptionType) var exceptionActionInterfaceType = typeof(IRequestExceptionAction<,>).MakeGenericType(typeof(TRequest), exceptionType); var enumerableExceptionActionInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionActionInterfaceType); - var actionsForException = (IEnumerable)_serviceFactory(enumerableExceptionActionInterfaceType); + var actionsForException = (IEnumerable)_serviceProvider.GetService(enumerableExceptionActionInterfaceType); return HandlersOrderer.Prioritize(actionsForException.ToList(), request) .Select(action => (exceptionType, action)); diff --git a/src/MediatR/Pipeline/RequestExceptionProcessorBehavior.cs b/src/MediatR/Pipeline/RequestExceptionProcessorBehavior.cs index a63dbe5f..8b76c305 100644 --- a/src/MediatR/Pipeline/RequestExceptionProcessorBehavior.cs +++ b/src/MediatR/Pipeline/RequestExceptionProcessorBehavior.cs @@ -19,9 +19,9 @@ namespace MediatR.Pipeline; public class RequestExceptionProcessorBehavior : IPipelineBehavior where TRequest : IRequest { - private readonly ServiceFactory _serviceFactory; + private readonly IServiceProvider _serviceProvider; - public RequestExceptionProcessorBehavior(ServiceFactory serviceFactory) => _serviceFactory = serviceFactory; + public RequestExceptionProcessorBehavior(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) { @@ -88,7 +88,7 @@ private static IEnumerable GetExceptionTypes(Type? exceptionType) var exceptionHandlerInterfaceType = typeof(IRequestExceptionHandler<,,>).MakeGenericType(typeof(TRequest), typeof(TResponse), exceptionType); var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType); - var exceptionHandlers = (IEnumerable) _serviceFactory(enumerableExceptionHandlerInterfaceType); + var exceptionHandlers = (IEnumerable) _serviceProvider.GetService(enumerableExceptionHandlerInterfaceType); return HandlersOrderer.Prioritize(exceptionHandlers.ToList(), request) .Select(handler => (exceptionType, action: handler)); diff --git a/src/MediatR/Registration/ServiceRegistrar.cs b/src/MediatR/Registration/ServiceRegistrar.cs new file mode 100644 index 00000000..5e53fe20 --- /dev/null +++ b/src/MediatR/Registration/ServiceRegistrar.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using MediatR.Pipeline; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace MediatR.Registration; + +public static class ServiceRegistrar +{ + public static void AddMediatRClasses(IServiceCollection services, IEnumerable assembliesToScan, MediatRServiceConfiguration configuration) + { + assembliesToScan = assembliesToScan.Distinct().ToArray(); + + ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>), services, assembliesToScan, false, configuration); + ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>), services, assembliesToScan, true, configuration); + ConnectImplementationsToTypesClosing(typeof(IStreamRequestHandler<,>), services, assembliesToScan, false, configuration); + ConnectImplementationsToTypesClosing(typeof(IRequestPreProcessor<>), services, assembliesToScan, true, configuration); + ConnectImplementationsToTypesClosing(typeof(IRequestPostProcessor<,>), services, assembliesToScan, true, configuration); + ConnectImplementationsToTypesClosing(typeof(IRequestExceptionHandler<,,>), services, assembliesToScan, true, configuration); + ConnectImplementationsToTypesClosing(typeof(IRequestExceptionAction<,>), services, assembliesToScan, true, configuration); + + var multiOpenInterfaces = new[] + { + typeof(INotificationHandler<>), + typeof(IRequestPreProcessor<>), + typeof(IRequestPostProcessor<,>), + typeof(IRequestExceptionHandler<,,>), + typeof(IRequestExceptionAction<,>) + }; + + foreach (var multiOpenInterface in multiOpenInterfaces) + { + var arity = multiOpenInterface.GetGenericArguments().Length; + + var concretions = assembliesToScan + .SelectMany(a => a.DefinedTypes) + .Where(type => type.FindInterfacesThatClose(multiOpenInterface).Any()) + .Where(type => type.IsConcrete() && type.IsOpenGeneric()) + .Where(type => type.GetGenericArguments().Length == arity) + .Where(configuration.TypeEvaluator) + .ToList(); + + foreach (var type in concretions) + { + services.AddTransient(multiOpenInterface, type); + } + } + } + + private static void ConnectImplementationsToTypesClosing(Type openRequestInterface, + IServiceCollection services, + IEnumerable assembliesToScan, + bool addIfAlreadyExists, + MediatRServiceConfiguration configuration) + { + var concretions = new List(); + var interfaces = new List(); + foreach (var type in assembliesToScan.SelectMany(a => a.DefinedTypes).Where(t => !t.IsOpenGeneric()).Where(configuration.TypeEvaluator)) + { + var interfaceTypes = type.FindInterfacesThatClose(openRequestInterface).ToArray(); + if (!interfaceTypes.Any()) continue; + + if (type.IsConcrete()) + { + concretions.Add(type); + } + + foreach (var interfaceType in interfaceTypes) + { + interfaces.Fill(interfaceType); + } + } + + foreach (var @interface in interfaces) + { + var exactMatches = concretions.Where(x => x.CanBeCastTo(@interface)).ToList(); + if (addIfAlreadyExists) + { + foreach (var type in exactMatches) + { + services.AddTransient(@interface, type); + } + } + else + { + if (exactMatches.Count > 1) + { + exactMatches.RemoveAll(m => !IsMatchingWithInterface(m, @interface)); + } + + foreach (var type in exactMatches) + { + services.TryAddTransient(@interface, type); + } + } + + if (!@interface.IsOpenGeneric()) + { + AddConcretionsThatCouldBeClosed(@interface, concretions, services); + } + } + } + + private static bool IsMatchingWithInterface(Type handlerType, Type handlerInterface) + { + if (handlerType == null || handlerInterface == null) + { + return false; + } + + if (handlerType.IsInterface) + { + if (handlerType.GenericTypeArguments.SequenceEqual(handlerInterface.GenericTypeArguments)) + { + return true; + } + } + else + { + return IsMatchingWithInterface(handlerType.GetInterface(handlerInterface.Name), handlerInterface); + } + + return false; + } + + private static void AddConcretionsThatCouldBeClosed(Type @interface, List concretions, IServiceCollection services) + { + foreach (var type in concretions + .Where(x => x.IsOpenGeneric() && x.CouldCloseTo(@interface))) + { + try + { + services.TryAddTransient(@interface, type.MakeGenericType(@interface.GenericTypeArguments)); + } + catch (Exception) + { + } + } + } + + private static bool CouldCloseTo(this Type openConcretion, Type closedInterface) + { + var openInterface = closedInterface.GetGenericTypeDefinition(); + var arguments = closedInterface.GenericTypeArguments; + + var concreteArguments = openConcretion.GenericTypeArguments; + return arguments.Length == concreteArguments.Length && openConcretion.CanBeCastTo(openInterface); + } + + private static bool CanBeCastTo(this Type pluggedType, Type pluginType) + { + if (pluggedType == null) return false; + + if (pluggedType == pluginType) return true; + + return pluginType.GetTypeInfo().IsAssignableFrom(pluggedType.GetTypeInfo()); + } + + private static bool IsOpenGeneric(this Type type) + { + return type.GetTypeInfo().IsGenericTypeDefinition || type.GetTypeInfo().ContainsGenericParameters; + } + + private static IEnumerable FindInterfacesThatClose(this Type pluggedType, Type templateType) + { + return FindInterfacesThatClosesCore(pluggedType, templateType).Distinct(); + } + + private static IEnumerable FindInterfacesThatClosesCore(Type pluggedType, Type templateType) + { + if (pluggedType == null) yield break; + + if (!pluggedType.IsConcrete()) yield break; + + if (templateType.GetTypeInfo().IsInterface) + { + foreach ( + var interfaceType in + pluggedType.GetInterfaces() + .Where(type => type.GetTypeInfo().IsGenericType && (type.GetGenericTypeDefinition() == templateType))) + { + yield return interfaceType; + } + } + else if (pluggedType.GetTypeInfo().BaseType.GetTypeInfo().IsGenericType && + (pluggedType.GetTypeInfo().BaseType.GetGenericTypeDefinition() == templateType)) + { + yield return pluggedType.GetTypeInfo().BaseType; + } + + if (pluggedType.GetTypeInfo().BaseType == typeof(object)) yield break; + + foreach (var interfaceType in FindInterfacesThatClosesCore(pluggedType.GetTypeInfo().BaseType, templateType)) + { + yield return interfaceType; + } + } + + private static bool IsConcrete(this Type type) + { + return !type.GetTypeInfo().IsAbstract && !type.GetTypeInfo().IsInterface; + } + + private static void Fill(this IList list, T value) + { + if (list.Contains(value)) return; + list.Add(value); + } + + public static void AddRequiredServices(IServiceCollection services, MediatRServiceConfiguration serviceConfiguration) + { + // Use TryAdd, so any existing ServiceFactory/IMediator registration doesn't get overriden + services.TryAdd(new ServiceDescriptor(typeof(IMediator), serviceConfiguration.MediatorImplementationType, serviceConfiguration.Lifetime)); + services.TryAdd(new ServiceDescriptor(typeof(ISender), sp => sp.GetRequiredService(), serviceConfiguration.Lifetime)); + services.TryAdd(new ServiceDescriptor(typeof(IPublisher), sp => sp.GetRequiredService(), serviceConfiguration.Lifetime)); + + // Use TryAddTransientExact (see below), we dó want to register our Pre/Post processor behavior, even if (a more concrete) + // registration for IPipelineBehavior<,> already exists. But only once. + services.TryAddTransientExact(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>)); + services.TryAddTransientExact(typeof(IPipelineBehavior<,>), typeof(RequestPostProcessorBehavior<,>)); + + if (serviceConfiguration.RequestExceptionActionProcessorStrategy == RequestExceptionActionProcessorStrategy.ApplyForUnhandledExceptions) + { + services.TryAddTransientExact(typeof(IPipelineBehavior<,>), typeof(RequestExceptionActionProcessorBehavior<,>)); + services.TryAddTransientExact(typeof(IPipelineBehavior<,>), typeof(RequestExceptionProcessorBehavior<,>)); + } + else + { + services.TryAddTransientExact(typeof(IPipelineBehavior<,>), typeof(RequestExceptionProcessorBehavior<,>)); + services.TryAddTransientExact(typeof(IPipelineBehavior<,>), typeof(RequestExceptionActionProcessorBehavior<,>)); + } + } + + /// + /// Adds a new transient registration to the service collection only when no existing registration of the same service type and implementation type exists. + /// In contrast to TryAddTransient, which only checks the service type. + /// + /// The service collection + /// Service type + /// Implementation type + private static void TryAddTransientExact(this IServiceCollection services, Type serviceType, Type implementationType) + { + if (services.Any(reg => reg.ServiceType == serviceType && reg.ImplementationType == implementationType)) + { + return; + } + + services.AddTransient(serviceType, implementationType); + } +} \ No newline at end of file diff --git a/src/MediatR/ServiceFactory.cs b/src/MediatR/ServiceFactory.cs deleted file mode 100644 index 84b0ed51..00000000 --- a/src/MediatR/ServiceFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MediatR; - -/// -/// Factory method used to resolve all services. For multiple instances, it will resolve against -/// -/// Type of service to resolve -/// An instance of type -public delegate object ServiceFactory(Type serviceType); - -public static class ServiceFactoryExtensions -{ - public static T GetInstance(this ServiceFactory factory) - => (T) factory(typeof(T)); - - public static IEnumerable GetInstances(this ServiceFactory factory) - => (IEnumerable) factory(typeof(IEnumerable)); -} \ No newline at end of file diff --git a/src/MediatR/Wrappers/HandlerBase.cs b/src/MediatR/Wrappers/HandlerBase.cs deleted file mode 100644 index 46c03b35..00000000 --- a/src/MediatR/Wrappers/HandlerBase.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace MediatR.Wrappers; - -using System; - -public abstract class HandlerBase -{ - protected static THandler GetHandler(ServiceFactory factory) - { - THandler handler; - - try - { - handler = factory.GetInstance(); - } - catch (Exception e) - { - throw new InvalidOperationException($"Error constructing handler for request of type {typeof(THandler)}. Register your handlers with the container. See the samples in GitHub for examples.", e); - } - - if (handler == null) - { - throw new InvalidOperationException($"Handler was not found for request of type {typeof(THandler)}. Register your handlers with the container. See the samples in GitHub for examples."); - } - - return handler; - } -} \ No newline at end of file diff --git a/src/MediatR/Wrappers/NotificationHandlerWrapper.cs b/src/MediatR/Wrappers/NotificationHandlerWrapper.cs index c8981730..b778cf88 100644 --- a/src/MediatR/Wrappers/NotificationHandlerWrapper.cs +++ b/src/MediatR/Wrappers/NotificationHandlerWrapper.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.DependencyInjection; + namespace MediatR.Wrappers; using System; @@ -8,7 +10,7 @@ namespace MediatR.Wrappers; public abstract class NotificationHandlerWrapper { - public abstract Task Handle(INotification notification, ServiceFactory serviceFactory, + public abstract Task Handle(INotification notification, IServiceProvider serviceFactory, Func>, INotification, CancellationToken, Task> publish, CancellationToken cancellationToken); } @@ -16,12 +18,12 @@ public abstract Task Handle(INotification notification, ServiceFactory serviceFa public class NotificationHandlerWrapperImpl : NotificationHandlerWrapper where TNotification : INotification { - public override Task Handle(INotification notification, ServiceFactory serviceFactory, + public override Task Handle(INotification notification, IServiceProvider serviceFactory, Func>, INotification, CancellationToken, Task> publish, CancellationToken cancellationToken) { var handlers = serviceFactory - .GetInstances>() + .GetServices>() .Select(static x => new Func((theNotification, theToken) => x.Handle((TNotification)theNotification, theToken))); return publish(handlers, notification, cancellationToken); diff --git a/src/MediatR/Wrappers/RequestHandlerWrapper.cs b/src/MediatR/Wrappers/RequestHandlerWrapper.cs index ea64d039..fb5e00a0 100644 --- a/src/MediatR/Wrappers/RequestHandlerWrapper.cs +++ b/src/MediatR/Wrappers/RequestHandlerWrapper.cs @@ -1,36 +1,39 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + namespace MediatR.Wrappers; using System.Linq; using System.Threading; using System.Threading.Tasks; -public abstract class RequestHandlerBase : HandlerBase +public abstract class RequestHandlerBase { - public abstract Task Handle(object request, ServiceFactory serviceFactory, + public abstract Task Handle(object request, IServiceProvider serviceProvider, CancellationToken cancellationToken); } public abstract class RequestHandlerWrapper : RequestHandlerBase { - public abstract Task Handle(IRequest request, ServiceFactory serviceFactory, + public abstract Task Handle(IRequest request, IServiceProvider serviceProvider, CancellationToken cancellationToken); } public class RequestHandlerWrapperImpl : RequestHandlerWrapper where TRequest : IRequest { - public override async Task Handle(object request, ServiceFactory serviceFactory, + public override async Task Handle(object request, IServiceProvider serviceProvider, CancellationToken cancellationToken) => - await Handle((IRequest)request, serviceFactory, cancellationToken).ConfigureAwait(false); + await Handle((IRequest)request, serviceProvider, cancellationToken).ConfigureAwait(false); - public override Task Handle(IRequest request, ServiceFactory serviceFactory, + public override Task Handle(IRequest request, IServiceProvider serviceProvider, CancellationToken cancellationToken) { - Task Handler() => GetHandler>(serviceFactory).Handle((TRequest) request, cancellationToken); + Task Handler() => serviceProvider.GetRequiredService>().Handle((TRequest) request, cancellationToken); - return serviceFactory - .GetInstances>() + return serviceProvider + .GetServices>() .Reverse() .Aggregate((RequestHandlerDelegate) Handler, (next, pipeline) => () => pipeline.Handle((TRequest)request, next, cancellationToken))(); } diff --git a/src/MediatR/Wrappers/StreamRequestHandlerWrapper.cs b/src/MediatR/Wrappers/StreamRequestHandlerWrapper.cs index d383da53..5f3a7b2b 100644 --- a/src/MediatR/Wrappers/StreamRequestHandlerWrapper.cs +++ b/src/MediatR/Wrappers/StreamRequestHandlerWrapper.cs @@ -1,3 +1,6 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + namespace MediatR.Wrappers; using System.Collections.Generic; @@ -6,34 +9,41 @@ namespace MediatR.Wrappers; using System.Threading; using System.Threading.Tasks; -internal abstract class StreamRequestHandlerBase : HandlerBase +internal abstract class StreamRequestHandlerBase { - public abstract IAsyncEnumerable Handle(object request, ServiceFactory serviceFactory, CancellationToken cancellationToken); + public abstract IAsyncEnumerable Handle(object request, IServiceProvider serviceProvider, CancellationToken cancellationToken); } internal abstract class StreamRequestHandlerWrapper : StreamRequestHandlerBase { - public abstract IAsyncEnumerable Handle(IStreamRequest request, ServiceFactory serviceFactory, + public abstract IAsyncEnumerable Handle( + IStreamRequest request, + IServiceProvider serviceProvider, CancellationToken cancellationToken); } -internal class StreamRequestHandlerWrapperImpl : StreamRequestHandlerWrapper +internal class StreamRequestHandlerWrapperImpl + : StreamRequestHandlerWrapper where TRequest : IStreamRequest { - public override async IAsyncEnumerable Handle(object request, ServiceFactory serviceFactory, [EnumeratorCancellation] CancellationToken cancellationToken) + public override async IAsyncEnumerable Handle(object request, IServiceProvider serviceProvider, [EnumeratorCancellation] CancellationToken cancellationToken) { - await foreach (var item in Handle((IStreamRequest) request, serviceFactory, cancellationToken)) + await foreach (var item in Handle((IStreamRequest) request, serviceProvider, cancellationToken)) { yield return item; } } - public override async IAsyncEnumerable Handle(IStreamRequest request, ServiceFactory serviceFactory, [EnumeratorCancellation] CancellationToken cancellationToken) + public override async IAsyncEnumerable Handle(IStreamRequest request, + IServiceProvider serviceProvider, + [EnumeratorCancellation] CancellationToken cancellationToken) { - IAsyncEnumerable Handler() => GetHandler>(serviceFactory).Handle((TRequest) request, cancellationToken); + IAsyncEnumerable Handler() => serviceProvider + .GetRequiredService>() + .Handle((TRequest) request, cancellationToken); - var items = serviceFactory - .GetInstances>() + var items = serviceProvider + .GetServices>() .Reverse() .Aggregate( (StreamHandlerDelegate) Handler, diff --git a/test/MediatR.Benchmarks/Benchmarks.cs b/test/MediatR.Benchmarks/Benchmarks.cs index 26f6de81..01c8fb45 100644 --- a/test/MediatR.Benchmarks/Benchmarks.cs +++ b/test/MediatR.Benchmarks/Benchmarks.cs @@ -1,7 +1,6 @@ using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; -using MediatR.Examples; using MediatR.Pipeline; using Microsoft.Extensions.DependencyInjection; diff --git a/test/MediatR.Benchmarks/GenericPipelineBehavior.cs b/test/MediatR.Benchmarks/GenericPipelineBehavior.cs new file mode 100644 index 00000000..26ffd59e --- /dev/null +++ b/test/MediatR.Benchmarks/GenericPipelineBehavior.cs @@ -0,0 +1,25 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediatR.Benchmarks +{ + public class GenericPipelineBehavior : IPipelineBehavior + where TRequest : IRequest + { + private readonly TextWriter _writer; + + public GenericPipelineBehavior(TextWriter writer) + { + _writer = writer; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + await _writer.WriteLineAsync("-- Handling Request"); + var response = await next(); + await _writer.WriteLineAsync("-- Finished Request"); + return response; + } + } +} \ No newline at end of file diff --git a/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs b/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs new file mode 100644 index 00000000..f8ae0f53 --- /dev/null +++ b/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediatR.Pipeline; + +namespace MediatR.Benchmarks +{ + public class GenericRequestPostProcessor : IRequestPostProcessor + where TRequest : IRequest + { + private readonly TextWriter _writer; + + public GenericRequestPostProcessor(TextWriter writer) + { + _writer = writer; + } + + public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) + { + return _writer.WriteLineAsync("- All Done"); + } + } +} \ No newline at end of file diff --git a/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs b/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs new file mode 100644 index 00000000..a2079c4b --- /dev/null +++ b/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs @@ -0,0 +1,22 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediatR.Pipeline; + +namespace MediatR.Benchmarks +{ + public class GenericRequestPreProcessor : IRequestPreProcessor + { + private readonly TextWriter _writer; + + public GenericRequestPreProcessor(TextWriter writer) + { + _writer = writer; + } + + public Task Process(TRequest request, CancellationToken cancellationToken) + { + return _writer.WriteLineAsync("- Starting Up"); + } + } +} \ No newline at end of file diff --git a/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj b/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj index 92133dc5..0232c140 100644 --- a/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj +++ b/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj @@ -16,10 +16,9 @@ - - + \ No newline at end of file diff --git a/test/MediatR.Benchmarks/Ping.cs b/test/MediatR.Benchmarks/Ping.cs new file mode 100644 index 00000000..c1f40b3f --- /dev/null +++ b/test/MediatR.Benchmarks/Ping.cs @@ -0,0 +1,7 @@ +namespace MediatR.Benchmarks +{ + public class Ping : IRequest + { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/test/MediatR.Benchmarks/Pinged.cs b/test/MediatR.Benchmarks/Pinged.cs new file mode 100644 index 00000000..09dada08 --- /dev/null +++ b/test/MediatR.Benchmarks/Pinged.cs @@ -0,0 +1,7 @@ +namespace MediatR.Benchmarks +{ + public class Pinged : INotification + { + + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/CreateStreamTests.cs b/test/MediatR.Tests/CreateStreamTests.cs index 36cee662..bbad3e3b 100644 --- a/test/MediatR.Tests/CreateStreamTests.cs +++ b/test/MediatR.Tests/CreateStreamTests.cs @@ -7,7 +7,7 @@ namespace MediatR.Tests; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class CreateStreamTests @@ -15,12 +15,12 @@ public class CreateStreamTests public class Ping : IStreamRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingStreamHandler : IStreamRequestHandler @@ -43,7 +43,6 @@ public async Task Should_resolve_main_handler() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -76,7 +75,6 @@ public async Task Should_resolve_main_handler_via_dynamic_dispatch() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Use(ctx => ctx.GetInstance); cfg.For().Use(); }); @@ -85,11 +83,11 @@ public async Task Should_resolve_main_handler_via_dynamic_dispatch() object request = new Ping { Message = "Ping" }; var response = mediator.CreateStream(request); int i = 0; - await foreach (Pong result in response) + await foreach (Pong? result in response) { if (i == 0) { - result.Message.ShouldBe("Ping Pang"); + result!.Message.ShouldBe("Ping Pang"); } i++; @@ -110,7 +108,6 @@ public async Task Should_resolve_main_handler_by_specific_interface() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -135,13 +132,12 @@ public void Should_raise_execption_on_null_request() { var container = new Container(cfg => { - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - Should.Throw(() => mediator.CreateStream((Ping) null)); + Should.Throw(() => mediator.CreateStream((Ping) null!)); } [Fact] @@ -149,12 +145,11 @@ public void Should_raise_execption_on_null_request_via_dynamic_dispatch() { var container = new Container(cfg => { - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - Should.Throw(() => mediator.CreateStream((object) null)); + Should.Throw(() => mediator.CreateStream((object) null!)); } } \ No newline at end of file diff --git a/test/MediatR.Tests/ExceptionTests.cs b/test/MediatR.Tests/ExceptionTests.cs index 358cac67..30878713 100644 --- a/test/MediatR.Tests/ExceptionTests.cs +++ b/test/MediatR.Tests/ExceptionTests.cs @@ -5,8 +5,9 @@ namespace MediatR.Tests; using System; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; +using Lamar.IoC; public class ExceptionTests { @@ -72,7 +73,6 @@ public ExceptionTests() { var container = new Container(cfg => { - cfg.For().Use(ctx => ctx.GetInstance); cfg.For().Use(); }); _mediator = container.GetInstance(); @@ -81,19 +81,19 @@ public ExceptionTests() [Fact] public async Task Should_throw_for_send() { - await Should.ThrowAsync(async () => await _mediator.Send(new Ping())); + await Should.ThrowAsync(async () => await _mediator.Send(new Ping())); } [Fact] public async Task Should_throw_for_void_send() { - await Should.ThrowAsync(async () => await _mediator.Send(new VoidPing())); + await Should.ThrowAsync(async () => await _mediator.Send(new VoidPing())); } [Fact] public async Task Should_not_throw_for_publish() { - Exception ex = null; + Exception ex = null!; try { await _mediator.Publish(new Pinged()); @@ -108,19 +108,19 @@ public async Task Should_not_throw_for_publish() [Fact] public async Task Should_throw_for_async_send() { - await Should.ThrowAsync(async () => await _mediator.Send(new AsyncPing())); + await Should.ThrowAsync(async () => await _mediator.Send(new AsyncPing())); } [Fact] public async Task Should_throw_for_async_void_send() { - await Should.ThrowAsync(async () => await _mediator.Send(new AsyncVoidPing())); + await Should.ThrowAsync(async () => await _mediator.Send(new AsyncVoidPing())); } [Fact] public async Task Should_not_throw_for_async_publish() { - Exception ex = null; + Exception ex = null!; try { await _mediator.Publish(new AsyncPinged()); @@ -144,12 +144,11 @@ public async Task Should_throw_argument_exception_for_send_when_request_is_null( scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - NullPing request = null; + NullPing request = null!; await Should.ThrowAsync(async () => await mediator.Send(request)); } @@ -166,12 +165,11 @@ public async Task Should_throw_argument_exception_for_void_send_when_request_is_ scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - VoidNullPing request = null; + VoidNullPing request = null!; await Should.ThrowAsync(async () => await mediator.Send(request)); } @@ -188,12 +186,11 @@ public async Task Should_throw_argument_exception_for_publish_when_request_is_nu scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - NullPinged notification = null; + NullPinged notification = null!; await Should.ThrowAsync(async () => await mediator.Publish(notification)); } @@ -210,12 +207,11 @@ public async Task Should_throw_argument_exception_for_publish_when_request_is_nu scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - object notification = null; + object notification = null!; await Should.ThrowAsync(async () => await mediator.Publish(notification)); } @@ -232,7 +228,6 @@ public async Task Should_throw_argument_exception_for_publish_when_request_is_no scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); @@ -267,7 +262,6 @@ public async Task Should_throw_exception_for_non_generic_send_when_exception_occ scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); @@ -289,7 +283,6 @@ public async Task Should_throw_exception_for_non_request_send() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); @@ -317,7 +310,6 @@ public async Task Should_throw_exception_for_generic_send_when_exception_occurs( scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); diff --git a/test/MediatR.Tests/GenericTypeConstraintsTests.cs b/test/MediatR.Tests/GenericTypeConstraintsTests.cs index e61d36a8..254b4756 100644 --- a/test/MediatR.Tests/GenericTypeConstraintsTests.cs +++ b/test/MediatR.Tests/GenericTypeConstraintsTests.cs @@ -6,7 +6,7 @@ namespace MediatR.Tests; using System; using System.Linq; using Shouldly; -using StructureMap; +using Lamar; using System.Threading.Tasks; using Xunit; @@ -55,7 +55,7 @@ public class GenericTypeConstraintJing : GenericTypeRequestHandlerTestClass @@ -69,12 +69,12 @@ public Task Handle(Jing request, CancellationToken cancellationToken) public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : IRequestHandler @@ -99,7 +99,6 @@ public GenericTypeConstraintsTests() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => ctx.GetInstance); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/MediatR.Tests.csproj b/test/MediatR.Tests/MediatR.Tests.csproj index e83c46ce..446f83f5 100644 --- a/test/MediatR.Tests/MediatR.Tests.csproj +++ b/test/MediatR.Tests/MediatR.Tests.csproj @@ -2,6 +2,7 @@ net6.0 + enable @@ -9,11 +10,12 @@ + - + all runtime; build; native; contentfiles; analyzers diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs new file mode 100644 index 00000000..520c9213 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs @@ -0,0 +1,62 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System; +using System.Linq; +using System.Reflection; +using Shouldly; +using Xunit; + +public class AssemblyResolutionTests +{ + private readonly IServiceProvider _provider; + + public AssemblyResolutionTests() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + _provider = services.BuildServiceProvider(); + } + + [Fact] + public void ShouldResolveMediator() + { + _provider.GetService().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveRequestHandler() + { + _provider.GetService>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveInternalHandler() + { + _provider.GetService>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveNotificationHandlers() + { + _provider.GetServices>().Count().ShouldBe(3); + } + + [Fact] + public void ShouldResolveStreamHandlers() + { + _provider.GetService>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldRequireAtLeastOneAssembly() + { + var services = new ServiceCollection(); + + Action registration = () => services.AddMediatR(new Type[0]); + + registration.ShouldThrow(); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/CustomMediatorTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/CustomMediatorTests.cs new file mode 100644 index 00000000..7859afd2 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/CustomMediatorTests.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System; +using System.Linq; +using Shouldly; +using Xunit; + +public class CustomMediatorTests +{ + private readonly IServiceProvider _provider; + + public CustomMediatorTests() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(cfg => cfg.Using(), typeof(CustomMediatorTests)); + _provider = services.BuildServiceProvider(); + } + + [Fact] + public void ShouldResolveMediator() + { + _provider.GetService().ShouldNotBeNull(); + _provider.GetRequiredService().GetType().ShouldBe(typeof(MyCustomMediator)); + } + + [Fact] + public void ShouldResolveRequestHandler() + { + _provider.GetService>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveNotificationHandlers() + { + _provider.GetServices>().Count().ShouldBe(3); + } + + [Fact] + public void Can_Call_AddMediatr_multiple_times() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(cfg => cfg.Using(), typeof(CustomMediatorTests)); + + // Call AddMediatr again, this should NOT override our custom mediatr (With MS DI, last registration wins) + services.AddMediatR(typeof(CustomMediatorTests)); + + var provider = services.BuildServiceProvider(); + var mediator = provider.GetRequiredService(); + mediator.GetType().ShouldBe(typeof(MyCustomMediator)); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/DerivingRequestsTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/DerivingRequestsTests.cs new file mode 100644 index 00000000..6ff2f261 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/DerivingRequestsTests.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +public class DerivingRequestsTests +{ + private readonly IServiceProvider _provider; + private readonly IMediator _mediator; + + public DerivingRequestsTests() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(typeof(Ping)); + _provider = services.BuildServiceProvider(); + _mediator = _provider.GetRequiredService(); + } + + [Fact] + public async Task ShouldReturnPingPong() + { + Pong pong = await _mediator.Send(new Ping() { Message = "Ping" }); + pong.Message.ShouldBe("Ping Pong"); + } + + [Fact] + public async Task ShouldReturnDerivedPingPong() + { + Pong pong = await _mediator.Send(new DerivedPing() { Message = "Ping" }); + pong.Message.ShouldBe("DerivedPing Pong"); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/DuplicateAssemblyResolutionTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/DuplicateAssemblyResolutionTests.cs new file mode 100644 index 00000000..684a7099 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/DuplicateAssemblyResolutionTests.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System; +using System.Linq; +using Shouldly; +using Xunit; + +public class DuplicateAssemblyResolutionTests +{ + private readonly IServiceProvider _provider; + + public DuplicateAssemblyResolutionTests() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(typeof(Ping), typeof(Ping)); + _provider = services.BuildServiceProvider(); + } + + [Fact] + public void ShouldResolveNotificationHandlersOnlyOnce() + { + _provider.GetServices>().Count().ShouldBe(3); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs new file mode 100644 index 00000000..4e241846 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs @@ -0,0 +1,240 @@ +using System; +using System.Runtime.CompilerServices; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests +{ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public class Ping : IRequest + { + public string? Message { get; init; } + public Action? ThrowAction { get; init; } + } + + public class DerivedPing : Ping + { + } + + public class Pong + { + public string? Message { get; init; } + } + + public class Zing : IRequest + { + public string? Message { get; init; } + } + + public class Zong + { + public string? Message { get; init; } + } + + public class Ding : IRequest + { + public string? Message { get; init; } + } + + public class Pinged : INotification + { + + } + + class InternalPing : IRequest { } + + public class StreamPing : IStreamRequest + { + public string? Message { get; init; } + } + + public class GenericHandler : INotificationHandler + { + public Task Handle(INotification notification, CancellationToken cancellationToken) + { + return Task.FromResult(0); + } + } + + public class DingAsyncHandler : IRequestHandler + { + public Task Handle(Ding message, CancellationToken cancellationToken) => Unit.Task; + } + + public class PingedHandler : INotificationHandler + { + public Task Handle(Pinged notification, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } + + public class PingedAlsoHandler : INotificationHandler + { + public Task Handle(Pinged notification, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } + + public class Logger + { + public IList Messages { get; } = new List(); + } + + public class PingHandler : IRequestHandler + { + private readonly Logger _logger; + + public PingHandler(Logger logger) + { + _logger = logger; + } + public Task Handle(Ping message, CancellationToken cancellationToken) + { + _logger.Messages.Add("Handler"); + + message.ThrowAction?.Invoke(message); + + return Task.FromResult(new Pong { Message = message.Message + " Pong" }); + } + } + + public class DerivedPingHandler : IRequestHandler + { + private readonly Logger _logger; + + public DerivedPingHandler(Logger logger) + { + _logger = logger; + } + public Task Handle(DerivedPing message, CancellationToken cancellationToken) + { + _logger.Messages.Add("Handler"); + return Task.FromResult(new Pong { Message = $"Derived{message.Message} Pong" }); + } + } + + public class ZingHandler : IRequestHandler + { + private readonly Logger _output; + + public ZingHandler(Logger output) + { + _output = output; + } + public Task Handle(Zing message, CancellationToken cancellationToken) + { + _output.Messages.Add("Handler"); + return Task.FromResult(new Zong { Message = message.Message + " Zong" }); + } + } + + public class PingStreamHandler : IStreamRequestHandler + { + private readonly Logger _output; + + public PingStreamHandler(Logger output) + { + _output = output; + } + public async IAsyncEnumerable Handle(StreamPing request, [EnumeratorCancellation] CancellationToken cancellationToken) + { + _output.Messages.Add("Handler"); + yield return await Task.Run(() => new Pong { Message = request.Message + " Pang" }, cancellationToken); + } + } + + + public class DuplicateTest : IRequest { } + public class DuplicateHandler1 : IRequestHandler + { + public Task Handle(DuplicateTest message, CancellationToken cancellationToken) + { + return Task.FromResult(nameof(DuplicateHandler1)); + } + } + + public class DuplicateHandler2 : IRequestHandler + { + public Task Handle(DuplicateTest message, CancellationToken cancellationToken) + { + return Task.FromResult(nameof(DuplicateHandler2)); + } + } + + class InternalPingHandler : IRequestHandler + { + public Task Handle(InternalPing request, CancellationToken cancellationToken) => Unit.Task; + } + + class MyCustomMediator : IMediator + { + public Task Send(object request, CancellationToken cancellationToken = new()) + { + throw new System.NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(IStreamRequest request, + CancellationToken cancellationToken = new()) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(object request, CancellationToken cancellationToken = new()) + { + throw new NotImplementedException(); + } + + public Task Publish(object notification, CancellationToken cancellationToken = new()) + { + throw new System.NotImplementedException(); + } + + public Task Publish(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification + { + throw new System.NotImplementedException(); + } + + public Task Send(IRequest request, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } + } +} + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests.Included +{ + using System.Threading; + using System.Threading.Tasks; + + public class Foo : IRequest + { + public string? Message { get; init; } + public Action? ThrowAction { get; init; } + } + + public class Bar + { + public string? Message { get; init; } + } + + public class FooHandler : IRequestHandler + { + private readonly Logger _logger; + + public FooHandler(Logger logger) + { + _logger = logger; + } + public Task Handle(Foo message, CancellationToken cancellationToken) + { + _logger.Messages.Add("Handler"); + + message.ThrowAction?.Invoke(message); + + return Task.FromResult(new Bar { Message = message.Message + " Bar" }); + } + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/PipeLineMultiCallToConstructorTest.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/PipeLineMultiCallToConstructorTest.cs new file mode 100644 index 00000000..d664e248 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/PipeLineMultiCallToConstructorTest.cs @@ -0,0 +1,105 @@ +using System.Threading; +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System.Reflection; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +public class PipelineMultiCallToConstructorTests +{ + public class ConstructorTestBehavior : IPipelineBehavior + where TRequest : IRequest + { + private readonly Logger _output; + + public ConstructorTestBehavior(Logger output) => _output = output; + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + _output.Messages.Add("ConstructorTestBehavior before"); + var response = await next(); + _output.Messages.Add("ConstructorTestBehavior after"); + + return response; + } + } + + public class ConstructorTestRequest : IRequest + { + public string? Message { get; set; } + } + + public class ConstructorTestResponse + { + public string? Message { get; set; } + } + + public class ConstructorTestHandler : IRequestHandler + { + + private static volatile object _lockObject = new(); + private readonly Logger _logger; + private static int _constructorCallCount; + + public static int ConstructorCallCount => _constructorCallCount; + + public static void ResetCallCount() + { + lock (_lockObject) + { + _constructorCallCount = 0; + } + } + + public ConstructorTestHandler(Logger logger) + { + _logger = logger; + lock (_lockObject) + { + _constructorCallCount++; + } + } + + public Task Handle(ConstructorTestRequest request, CancellationToken cancellationToken) + { + _logger.Messages.Add("Handler"); + return Task.FromResult(new ConstructorTestResponse { Message = request.Message + " ConstructorPong" }); + } + } + + [Fact] + public async Task Should_not_call_constructor_multiple_times_when_using_a_pipeline() + { + ConstructorTestHandler.ResetCallCount(); + ConstructorTestHandler.ConstructorCallCount.ShouldBe(0); + + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + + services.AddSingleton(output); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ConstructorTestBehavior<,>)); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var response = await mediator.Send(new ConstructorTestRequest { Message = "ConstructorPing" }); + + response.Message.ShouldBe("ConstructorPing ConstructorPong"); + + output.Messages.ShouldBe(new[] + { + "ConstructorTestBehavior before", + "First pre processor", + "Next pre processor", + "Handler", + "First post processor", + "Next post processor", + "ConstructorTestBehavior after" + }); + ConstructorTestHandler.ConstructorCallCount.ShouldBe(1); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/PipelineTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/PipelineTests.cs new file mode 100644 index 00000000..42836a7b --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/PipelineTests.cs @@ -0,0 +1,526 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Pipeline; +using Shouldly; +using Xunit; + +public class PipelineTests +{ + public class OuterBehavior : IPipelineBehavior + { + private readonly Logger _output; + + public OuterBehavior(Logger output) + { + _output = output; + } + + public async Task Handle(Ping request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + _output.Messages.Add("Outer before"); + var response = await next(); + _output.Messages.Add("Outer after"); + + return response; + } + } + + public class InnerBehavior : IPipelineBehavior + { + private readonly Logger _output; + + public InnerBehavior(Logger output) + { + _output = output; + } + + public async Task Handle(Ping request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + _output.Messages.Add("Inner before"); + var response = await next(); + _output.Messages.Add("Inner after"); + + return response; + } + } + + public class InnerBehavior : IPipelineBehavior + where TRequest : IRequest + { + private readonly Logger _output; + + public InnerBehavior(Logger output) + { + _output = output; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + _output.Messages.Add("Inner generic before"); + var response = await next(); + _output.Messages.Add("Inner generic after"); + + return response; + } + } + + public class OuterBehavior : IPipelineBehavior + where TRequest : IRequest + { + private readonly Logger _output; + + public OuterBehavior(Logger output) + { + _output = output; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + _output.Messages.Add("Outer generic before"); + var response = await next(); + _output.Messages.Add("Outer generic after"); + + return response; + } + } + + public class ConstrainedBehavior : IPipelineBehavior + where TRequest : Ping, IRequest + where TResponse : Pong + { + private readonly Logger _output; + + public ConstrainedBehavior(Logger output) + { + _output = output; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + _output.Messages.Add("Constrained before"); + var response = await next(); + _output.Messages.Add("Constrained after"); + + return response; + } + } + + public class FirstPreProcessor : IRequestPreProcessor where TRequest : notnull + { + private readonly Logger _output; + + public FirstPreProcessor(Logger output) + { + _output = output; + } + public Task Process(TRequest request, CancellationToken cancellationToken) + { + _output.Messages.Add("First pre processor"); + return Task.FromResult(0); + } + } + + public class FirstConcretePreProcessor : IRequestPreProcessor + { + private readonly Logger _output; + + public FirstConcretePreProcessor(Logger output) + { + _output = output; + } + public Task Process(Ping request, CancellationToken cancellationToken) + { + _output.Messages.Add("First concrete pre processor"); + return Task.FromResult(0); + } + } + + public class NextPreProcessor : IRequestPreProcessor where TRequest : notnull + { + private readonly Logger _output; + + public NextPreProcessor(Logger output) + { + _output = output; + } + public Task Process(TRequest request, CancellationToken cancellationToken) + { + _output.Messages.Add("Next pre processor"); + return Task.FromResult(0); + } + } + + public class NextConcretePreProcessor : IRequestPreProcessor + { + private readonly Logger _output; + + public NextConcretePreProcessor(Logger output) + { + _output = output; + } + public Task Process(Ping request, CancellationToken cancellationToken) + { + _output.Messages.Add("Next concrete pre processor"); + return Task.FromResult(0); + } + } + + public class FirstPostProcessor : IRequestPostProcessor + where TRequest : IRequest + { + private readonly Logger _output; + + public FirstPostProcessor(Logger output) + { + _output = output; + } + public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) + { + _output.Messages.Add("First post processor"); + return Task.FromResult(0); + } + } + + public class FirstConcretePostProcessor : IRequestPostProcessor + { + private readonly Logger _output; + + public FirstConcretePostProcessor(Logger output) + { + _output = output; + } + public Task Process(Ping request, Pong response, CancellationToken cancellationToken) + { + _output.Messages.Add("First concrete post processor"); + return Task.FromResult(0); + } + } + + public class NextPostProcessor : IRequestPostProcessor + where TRequest : IRequest + { + private readonly Logger _output; + + public NextPostProcessor(Logger output) + { + _output = output; + } + public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) + { + _output.Messages.Add("Next post processor"); + return Task.FromResult(0); + } + } + + public class NextConcretePostProcessor : IRequestPostProcessor + { + private readonly Logger _output; + + public NextConcretePostProcessor(Logger output) + { + _output = output; + } + public Task Process(Ping request, Pong response, CancellationToken cancellationToken) + { + _output.Messages.Add("Next concrete post processor"); + return Task.FromResult(0); + } + } + + public class PingPongGenericExceptionAction : IRequestExceptionAction + { + private readonly Logger _output; + + public PingPongGenericExceptionAction(Logger output) => _output = output; + + public Task Execute(Ping request, Exception exception, CancellationToken cancellationToken) + { + _output.Messages.Add("Logging generic exception"); + + return Task.CompletedTask; + } + } + + public class PingPongApplicationExceptionAction : IRequestExceptionAction + { + private readonly Logger _output; + + public PingPongApplicationExceptionAction(Logger output) => _output = output; + + public Task Execute(Ping request, ApplicationException exception, CancellationToken cancellationToken) + { + _output.Messages.Add("Logging ApplicationException exception"); + + return Task.CompletedTask; + } + } + + public class PingPongExceptionActionForType1 : IRequestExceptionAction + { + private readonly Logger _output; + + public PingPongExceptionActionForType1(Logger output) => _output = output; + + public Task Execute(Ping request, SystemException exception, CancellationToken cancellationToken) + { + _output.Messages.Add("Logging exception 1"); + + return Task.CompletedTask; + } + } + + public class PingPongExceptionActionForType2 : IRequestExceptionAction + { + private readonly Logger _output; + + public PingPongExceptionActionForType2(Logger output) => _output = output; + + public Task Execute(Ping request, SystemException exception, CancellationToken cancellationToken) + { + _output.Messages.Add("Logging exception 2"); + + return Task.CompletedTask; + } + } + + public class PingPongExceptionHandlerForType : IRequestExceptionHandler + { + public Task Handle(Ping request, ApplicationException exception, RequestExceptionHandlerState state, CancellationToken cancellationToken) + { + state.SetHandled(new Pong { Message = exception.Message + " Handled by Specific Type" }); + + return Task.CompletedTask; + } + } + + public class PingPongGenericExceptionHandler : IRequestExceptionHandler + { + private readonly Logger _output; + + public PingPongGenericExceptionHandler(Logger output) => _output = output; + + public Task Handle(Ping request, Exception exception, RequestExceptionHandlerState state, CancellationToken cancellationToken) + { + _output.Messages.Add(exception.Message + " Logged by Generic Type"); + + return Task.CompletedTask; + } + } + + [Fact] + public async Task Should_wrap_with_behavior() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddTransient, OuterBehavior>(); + services.AddTransient, InnerBehavior>(); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var response = await mediator.Send(new Ping { Message = "Ping" }); + + response.Message.ShouldBe("Ping Pong"); + + output.Messages.ShouldBe(new[] + { + "Outer before", + "Inner before", + "First concrete pre processor", + "Next concrete pre processor", + "First pre processor", + "Next pre processor", + "Handler", + "First concrete post processor", + "Next concrete post processor", + "First post processor", + "Next post processor", + "Inner after", + "Outer after" + }); + } + + + [Fact] + public async Task Should_wrap_generics_with_behavior() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(OuterBehavior<,>)); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(InnerBehavior<,>)); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var response = await mediator.Send(new Ping { Message = "Ping" }); + + response.Message.ShouldBe("Ping Pong"); + + output.Messages.ShouldBe(new[] + { + "Outer generic before", + "Inner generic before", + "First concrete pre processor", + "Next concrete pre processor", + "First pre processor", + "Next pre processor", + "Handler", + "First concrete post processor", + "Next concrete post processor", + "First post processor", + "Next post processor", + "Inner generic after", + "Outer generic after", + }); + } + + [Fact] + public async Task Should_pick_up_pre_and_post_processors() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var response = await mediator.Send(new Ping { Message = "Ping" }); + + response.Message.ShouldBe("Ping Pong"); + + output.Messages.ShouldBe(new[] + { + "First concrete pre processor", + "Next concrete pre processor", + "First pre processor", + "Next pre processor", + "Handler", + "First concrete post processor", + "Next concrete post processor", + "First post processor", + "Next post processor", + }); + } + + [Fact] + public async Task Should_pick_up_specific_exception_behaviors() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var response = await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new ApplicationException(msg.Message + " Thrown")}); + + response.Message.ShouldBe("Ping Thrown Handled by Specific Type"); + output.Messages.ShouldNotContain("Logging ApplicationException exception"); + } + + [Fact] + public void Should_pick_up_base_exception_behaviors() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + Should.Throw(async () => await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new Exception(msg.Message + " Thrown")})); + + output.Messages.ShouldContain("Ping Thrown Logged by Generic Type"); + output.Messages.ShouldContain("Logging generic exception"); + } + + [Fact] + public void Should_pick_up_exception_actions() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + Should.Throw(async () => await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new SystemException(msg.Message + " Thrown")})); + + output.Messages.ShouldContain("Logging exception 1"); + output.Messages.ShouldContain("Logging exception 2"); + } + + [Fact] + public async Task Should_handle_constrained_generics() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(OuterBehavior<,>)); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(InnerBehavior<,>)); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ConstrainedBehavior<,>)); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var response = await mediator.Send(new Ping { Message = "Ping" }); + + response.Message.ShouldBe("Ping Pong"); + + output.Messages.ShouldBe(new[] + { + "Outer generic before", + "Inner generic before", + "Constrained before", + "First concrete pre processor", + "Next concrete pre processor", + "First pre processor", + "Next pre processor", + "Handler", + "First concrete post processor", + "Next concrete post processor", + "First post processor", + "Next post processor", + "Constrained after", + "Inner generic after", + "Outer generic after" + }); + + output.Messages.Clear(); + + var zingResponse = await mediator.Send(new Zing { Message = "Zing" }); + + zingResponse.Message.ShouldBe("Zing Zong"); + + output.Messages.ShouldBe(new[] + { + "Outer generic before", + "Inner generic before", + "First pre processor", + "Next pre processor", + "Handler", + "First post processor", + "Next post processor", + "Inner generic after", + "Outer generic after" + }); + } + +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/StreamPipelineTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/StreamPipelineTests.cs new file mode 100644 index 00000000..f55f3010 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/StreamPipelineTests.cs @@ -0,0 +1,87 @@ +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Pipeline; +using Shouldly; +using Xunit; + +public class StreamPipelineTests +{ + public class OuterBehavior : IStreamPipelineBehavior + { + private readonly Logger _output; + + public OuterBehavior(Logger output) + { + _output = output; + } + + public async IAsyncEnumerable Handle(StreamPing request, StreamHandlerDelegate next, [EnumeratorCancellation] CancellationToken cancellationToken) + { + _output.Messages.Add("Outer before"); + await foreach (var response in next().WithCancellation(cancellationToken)) + { + yield return response; + } + _output.Messages.Add("Outer after"); + } + } + + public class InnerBehavior : IStreamPipelineBehavior + { + private readonly Logger _output; + + public InnerBehavior(Logger output) + { + _output = output; + } + + public async IAsyncEnumerable Handle(StreamPing request, StreamHandlerDelegate next, [EnumeratorCancellation] CancellationToken cancellationToken) + { + _output.Messages.Add("Inner before"); + await foreach (var response in next().WithCancellation(cancellationToken)) + { + yield return response; + } + _output.Messages.Add("Inner after"); + } + } + + [Fact] + public async Task Should_wrap_with_behavior() + { + var output = new Logger(); + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(output); + services.AddTransient, OuterBehavior>(); + services.AddTransient, InnerBehavior>(); + services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly); + var provider = services.BuildServiceProvider(); + + var mediator = provider.GetRequiredService(); + + var stream = mediator.CreateStream(new StreamPing { Message = "Ping" }); + + await foreach (var response in stream) + { + response.Message.ShouldBe("Ping Pang"); + } + + output.Messages.ShouldBe(new[] + { + "Outer before", + "Inner before", + "Handler", + "Inner after", + "Outer after" + }); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/TypeEvaluatorTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/TypeEvaluatorTests.cs new file mode 100644 index 00000000..ff90107d --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/TypeEvaluatorTests.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using MediatR.Extensions.Microsoft.DependencyInjection.Tests.Included; +using Shouldly; +using System; +using System.Reflection; +using Xunit; + +public class TypeEvaluatorTests +{ + private readonly IServiceProvider _provider; + + public TypeEvaluatorTests() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(new[] { typeof(Ping).GetTypeInfo().Assembly }, cfg => + { + cfg.WithEvaluator(t => t.Namespace == "MediatR.Extensions.Microsoft.DependencyInjection.Tests.Included"); + }); + _provider = services.BuildServiceProvider(); + } + + [Fact] + public void ShouldResolveMediator() + { + _provider.GetService().ShouldNotBeNull(); + } + + [Fact] + public void ShouldOnlyResolveIncludedRequestHandlers() + { + _provider.GetService>().ShouldNotBeNull(); + _provider.GetService>().ShouldBeNull(); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/TypeResolutionTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/TypeResolutionTests.cs new file mode 100644 index 00000000..7fe90803 --- /dev/null +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/TypeResolutionTests.cs @@ -0,0 +1,72 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; + +using System; +using System.Linq; +using System.Reflection; +using Shouldly; +using Xunit; + +public class TypeResolutionTests +{ + private readonly IServiceProvider _provider; + + public TypeResolutionTests() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new Logger()); + services.AddMediatR(typeof(Ping)); + _provider = services.BuildServiceProvider(); + } + + [Fact] + public void ShouldResolveMediator() + { + _provider.GetService().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveSender() + { + _provider.GetService().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolvePublisher() + { + _provider.GetService().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveRequestHandler() + { + _provider.GetService>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveVoidRequestHandler() + { + _provider.GetService>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveNotificationHandlers() + { + _provider.GetServices>().Count().ShouldBe(3); + } + + [Fact] + public void ShouldResolveFirstDuplicateHandler() + { + _provider.GetService>().ShouldNotBeNull(); + _provider.GetService>() + .ShouldBeAssignableTo(); + } + + [Fact] + public void ShouldResolveIgnoreSecondDuplicateHandler() + { + _provider.GetServices>().Count().ShouldBe(1); + } +} \ No newline at end of file diff --git a/test/MediatR.Tests/NotificationHandlerTests.cs b/test/MediatR.Tests/NotificationHandlerTests.cs index 3e0ce76b..a8a3f788 100644 --- a/test/MediatR.Tests/NotificationHandlerTests.cs +++ b/test/MediatR.Tests/NotificationHandlerTests.cs @@ -10,7 +10,7 @@ public class NotificationHandlerTests { public class Ping : INotification { - public string Message { get; set; } + public string? Message { get; set; } } public class PongChildHandler : NotificationHandler diff --git a/test/MediatR.Tests/Pipeline/RequestExceptionActionTests.cs b/test/MediatR.Tests/Pipeline/RequestExceptionActionTests.cs index 854ac30a..6cc9281b 100644 --- a/test/MediatR.Tests/Pipeline/RequestExceptionActionTests.cs +++ b/test/MediatR.Tests/Pipeline/RequestExceptionActionTests.cs @@ -5,31 +5,31 @@ namespace MediatR.Tests.Pipeline; using System.Threading.Tasks; using MediatR.Pipeline; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class RequestExceptionActionTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public abstract class PingPongException : Exception { - protected PingPongException(string message) : base(message + " Thrown") + protected PingPongException(string? message) : base(message + " Thrown") { } } public class PingException : PingPongException { - public PingException(string message) : base(message) + public PingException(string? message) : base(message) { } } @@ -49,7 +49,7 @@ public Task Handle(Ping request, CancellationToken cancellationToken) } } - public class GenericExceptionAction : IRequestExceptionAction + public class GenericExceptionAction : IRequestExceptionAction where TRequest : notnull { public int ExecutionCount { get; private set; } @@ -60,7 +60,7 @@ public Task Execute(TRequest request, Exception exception, CancellationToken can } } - public class PingPongExceptionAction : IRequestExceptionAction + public class PingPongExceptionAction : IRequestExceptionAction where TRequest : notnull { public bool Executed { get; private set; } @@ -106,7 +106,6 @@ public async Task Should_run_all_exception_actions_that_match_base_type() cfg.For>().Use(_ => pingPongExceptionAction); cfg.For>().Use(_ => pongExceptionAction); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionActionProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -129,7 +128,6 @@ public async Task Should_run_matching_exception_actions_only_once() cfg.For>().Use(); cfg.For>().Use(_ => genericExceptionAction); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionActionProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/Pipeline/RequestExceptionHandlerTests.cs b/test/MediatR.Tests/Pipeline/RequestExceptionHandlerTests.cs index b12137fe..732bafed 100644 --- a/test/MediatR.Tests/Pipeline/RequestExceptionHandlerTests.cs +++ b/test/MediatR.Tests/Pipeline/RequestExceptionHandlerTests.cs @@ -5,24 +5,24 @@ namespace MediatR.Tests.Pipeline; using System.Threading.Tasks; using MediatR.Pipeline; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class RequestExceptionHandlerTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingException : Exception { - public PingException(string message) : base(message + " Thrown") + public PingException(string? message) : base(message + " Thrown") { } } @@ -89,7 +89,6 @@ public async Task Should_run_exception_handler_and_allow_for_exception_not_to_th cfg.For>().Use(); cfg.For>().Use(); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -108,7 +107,6 @@ public async Task Should_run_exception_handler_and_allow_for_exception_to_be_sti cfg.For>().Use(); cfg.For>().Use(); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -131,7 +129,6 @@ public async Task Should_run_exception_handler_and_unwrap_expections_thrown_in_t cfg.For>().Use(); cfg.For>().Use(); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -153,7 +150,6 @@ public async Task Should_run_matching_exception_handlers_only_once() cfg.For>().Use(); cfg.For>().Use(genericPingExceptionHandler); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/Pipeline/RequestPostProcessorTests.cs b/test/MediatR.Tests/Pipeline/RequestPostProcessorTests.cs index a3d4ae01..f237bfc3 100644 --- a/test/MediatR.Tests/Pipeline/RequestPostProcessorTests.cs +++ b/test/MediatR.Tests/Pipeline/RequestPostProcessorTests.cs @@ -5,19 +5,19 @@ namespace MediatR.Tests.Pipeline; using System.Threading.Tasks; using MediatR.Pipeline; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class RequestPostProcessorTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : IRequestHandler @@ -52,7 +52,6 @@ public async Task Should_run_postprocessors() scanner.AddAllTypesOf(typeof(IRequestPostProcessor<,>)); }); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPostProcessorBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/Pipeline/RequestPreProcessorTests.cs b/test/MediatR.Tests/Pipeline/RequestPreProcessorTests.cs index 6dc8fbe0..880d676b 100644 --- a/test/MediatR.Tests/Pipeline/RequestPreProcessorTests.cs +++ b/test/MediatR.Tests/Pipeline/RequestPreProcessorTests.cs @@ -5,19 +5,19 @@ namespace MediatR.Tests.Pipeline; using System.Threading.Tasks; using MediatR.Pipeline; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class RequestPreProcessorTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : IRequestHandler @@ -52,7 +52,6 @@ public async Task Should_run_preprocessors() scanner.AddAllTypesOf(typeof(IRequestPreProcessor<>)); }); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPreProcessorBehavior<,>)); - cfg.For().Use(ctx => ctx.GetInstance); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/Pipeline/Streams/StreamPipelineBehaviorTests.cs b/test/MediatR.Tests/Pipeline/Streams/StreamPipelineBehaviorTests.cs index c336b3a4..15a8145d 100644 --- a/test/MediatR.Tests/Pipeline/Streams/StreamPipelineBehaviorTests.cs +++ b/test/MediatR.Tests/Pipeline/Streams/StreamPipelineBehaviorTests.cs @@ -3,7 +3,7 @@ namespace MediatR.Tests.Pipeline.Streams; using Shouldly; -using StructureMap; +using Lamar; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -13,12 +13,12 @@ public class StreamPipelineBehaviorTests { public class Sing : IStreamRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Song { - public string Message { get; set; } + public string? Message { get; set; } } public class SingHandler : IStreamRequestHandler @@ -60,7 +60,6 @@ public async Task Should_run_pipeline_behavior() scanner.AddAllTypesOf(typeof(IStreamPipelineBehavior<,>)); }); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(SingSongPipelineBehavior)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/PipelineTests.cs b/test/MediatR.Tests/PipelineTests.cs index c20c3b85..a7147995 100644 --- a/test/MediatR.Tests/PipelineTests.cs +++ b/test/MediatR.Tests/PipelineTests.cs @@ -5,29 +5,29 @@ namespace MediatR.Tests; using System.Collections.Generic; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class PipelineTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class Zing : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Zong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : IRequestHandler @@ -196,10 +196,9 @@ public async Task Should_wrap_with_behavior() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For>().Add(); cfg.For>().Add(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -232,12 +231,11 @@ public async Task Should_wrap_generics_with_behavior() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>)); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -272,13 +270,12 @@ public async Task Should_handle_constrained_generics() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>)); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>)); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(ConstrainedBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -317,7 +314,7 @@ public async Task Should_handle_constrained_generics() }); } - [Fact(Skip = "StructureMap does not mix concrete and open generics. Use constraints instead.")] + [Fact(Skip = "Lamar does not mix concrete and open generics. Use constraints instead.")] public async Task Should_handle_concrete_and_open_generics() { var output = new Logger(); @@ -330,13 +327,12 @@ public async Task Should_handle_concrete_and_open_generics() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>)); cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>)); cfg.For(typeof(IPipelineBehavior)).Add(typeof(ConcreteBehavior)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/PublishTests.cs b/test/MediatR.Tests/PublishTests.cs index 66eac681..d23ce788 100644 --- a/test/MediatR.Tests/PublishTests.cs +++ b/test/MediatR.Tests/PublishTests.cs @@ -8,14 +8,14 @@ namespace MediatR.Tests; using System.Text; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class PublishTests { public class Ping : INotification { - public string Message { get; set; } + public string? Message { get; set; } } public class PongHandler : INotificationHandler @@ -65,7 +65,6 @@ public async Task Should_resolve_main_handler() }); cfg.For().Use(writer); cfg.For().Use(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); }); var mediator = container.GetInstance(); @@ -94,7 +93,6 @@ public async Task Should_resolve_main_handler_when_object_is_passed() }); cfg.For().Use(writer); cfg.For().Use(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); }); var mediator = container.GetInstance(); @@ -109,8 +107,8 @@ public async Task Should_resolve_main_handler_when_object_is_passed() public class SequentialMediator : Mediator { - public SequentialMediator(ServiceFactory serviceFactory) - : base(serviceFactory) + public SequentialMediator(IServiceProvider serviceProvider) + : base(serviceProvider) { } @@ -140,7 +138,6 @@ public async Task Should_override_with_sequential_firing() }); cfg.For().Use(writer); cfg.For().Use(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); }); var mediator = container.GetInstance(); @@ -169,7 +166,6 @@ public async Task Should_resolve_handlers_given_interface() }); cfg.For().Use(writer); cfg.For().Use(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); }); var mediator = container.GetInstance(); @@ -199,7 +195,6 @@ public async Task Should_resolve_main_handler_by_specific_interface() }); cfg.For().Use(writer); cfg.For().Use(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); }); var mediator = container.GetInstance(); diff --git a/test/MediatR.Tests/RequestHandlerTests.cs b/test/MediatR.Tests/RequestHandlerTests.cs index c9b2c05d..b7474c51 100644 --- a/test/MediatR.Tests/RequestHandlerTests.cs +++ b/test/MediatR.Tests/RequestHandlerTests.cs @@ -8,12 +8,12 @@ public class RequestHandlerTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : RequestHandler diff --git a/test/MediatR.Tests/RequestHandlerUnitTests.cs b/test/MediatR.Tests/RequestHandlerUnitTests.cs index a2c15114..b9428399 100644 --- a/test/MediatR.Tests/RequestHandlerUnitTests.cs +++ b/test/MediatR.Tests/RequestHandlerUnitTests.cs @@ -10,7 +10,7 @@ public class RequestHandlerUnitTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : RequestHandler diff --git a/test/MediatR.Tests/SendTests.cs b/test/MediatR.Tests/SendTests.cs index ae93337f..3e2af0d5 100644 --- a/test/MediatR.Tests/SendTests.cs +++ b/test/MediatR.Tests/SendTests.cs @@ -5,7 +5,7 @@ namespace MediatR.Tests; using System; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class SendTests @@ -13,12 +13,12 @@ public class SendTests public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : IRequestHandler @@ -41,7 +41,6 @@ public async Task Should_resolve_main_handler() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -64,7 +63,6 @@ public async Task Should_resolve_main_handler_via_dynamic_dispatch() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => ctx.GetInstance); cfg.For().Use(); }); @@ -89,7 +87,6 @@ public async Task Should_resolve_main_handler_by_specific_interface() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); }); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -106,12 +103,11 @@ public async Task Should_raise_execption_on_null_request() { var container = new Container(cfg => { - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); var mediator = container.GetInstance(); - await Should.ThrowAsync(async () => await mediator.Send(null)); + await Should.ThrowAsync(async () => await mediator.Send(default!)); } } \ No newline at end of file diff --git a/test/MediatR.Tests/SendVoidInterfaceTests.cs b/test/MediatR.Tests/SendVoidInterfaceTests.cs index 27e58b3e..2f7f9056 100644 --- a/test/MediatR.Tests/SendVoidInterfaceTests.cs +++ b/test/MediatR.Tests/SendVoidInterfaceTests.cs @@ -6,14 +6,14 @@ namespace MediatR.Tests; using System.Text; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class SendVoidInterfaceTests { public class Ping : IRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : AsyncRequestHandler @@ -41,7 +41,6 @@ public async Task Should_resolve_main_void_handler() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof (IRequestHandler<,>)); }); - cfg.For().Use(ctx => ctx.GetInstance); cfg.For().Use(writer); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/ServiceFactoryTests.cs b/test/MediatR.Tests/ServiceFactoryTests.cs index c040c456..69d75542 100644 --- a/test/MediatR.Tests/ServiceFactoryTests.cs +++ b/test/MediatR.Tests/ServiceFactoryTests.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace MediatR.Tests; @@ -15,23 +16,19 @@ public class Ping : IRequest public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } [Fact] public async Task Should_throw_given_no_handler() { - var serviceFactory = new ServiceFactory(type => - typeof(IEnumerable).IsAssignableFrom(type) - ? Array.CreateInstance(type.GetGenericArguments().First(), 0) - : null); + var serviceCollection = new ServiceCollection(); + var serviceProvider = serviceCollection.BuildServiceProvider(); - var mediator = new Mediator(serviceFactory); + var mediator = new Mediator(serviceProvider); - var exception = await Assert.ThrowsAsync( + await Assert.ThrowsAsync( () => mediator.Send(new Ping()) ); - - Assert.StartsWith("Handler was not found for request", exception.Message); } } \ No newline at end of file diff --git a/test/MediatR.Tests/StreamPipelineTests.cs b/test/MediatR.Tests/StreamPipelineTests.cs index 46f2ecb5..878ca18d 100644 --- a/test/MediatR.Tests/StreamPipelineTests.cs +++ b/test/MediatR.Tests/StreamPipelineTests.cs @@ -6,29 +6,29 @@ namespace MediatR.Tests; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Shouldly; -using StructureMap; +using Lamar; using Xunit; public class StreamPipelineTests { public class Ping : IStreamRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Pong { - public string Message { get; set; } + public string? Message { get; set; } } public class Zing : IStreamRequest { - public string Message { get; set; } + public string? Message { get; set; } } public class Zong { - public string Message { get; set; } + public string? Message { get; set; } } public class PingHandler : IStreamRequestHandler @@ -204,10 +204,9 @@ public async Task Should_wrap_with_behavior() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For>().Add(); cfg.For>().Add(); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -241,12 +240,11 @@ public async Task Should_wrap_generics_with_behavior() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>)); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -280,13 +278,12 @@ public async Task Should_handle_constrained_generics() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>)); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>)); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(ConstrainedBehavior<,>)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); @@ -325,7 +322,7 @@ public async Task Should_handle_constrained_generics() }); } - [Fact(Skip = "StructureMap does not mix concrete and open generics. Use constraints instead.")] + [Fact(Skip = "Lamar does not mix concrete and open generics. Use constraints instead.")] public async Task Should_handle_concrete_and_open_generics() { var output = new Logger(); @@ -338,13 +335,12 @@ public async Task Should_handle_concrete_and_open_generics() scanner.WithDefaultConventions(); scanner.AddAllTypesOf(typeof(IStreamRequestHandler<,>)); }); - cfg.For().Singleton().Use(output); + cfg.For().Use(output); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>)); cfg.For(typeof(IStreamPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>)); cfg.For(typeof(IStreamPipelineBehavior)).Add(typeof(ConcreteBehavior)); - cfg.For().Use(ctx => t => ctx.GetInstance(t)); cfg.For().Use(); }); diff --git a/test/MediatR.Tests/UnitTests.cs b/test/MediatR.Tests/UnitTests.cs index 46549224..8a3c8503 100644 --- a/test/MediatR.Tests/UnitTests.cs +++ b/test/MediatR.Tests/UnitTests.cs @@ -53,7 +53,7 @@ public static object[][] ValueData() new object[] {new object(), false}, new object[] {"", false}, new object[] {"()", false}, - new object[] {null, false}, + new object[] {null!, false}, new object[] {new Uri("https://www.google.com"), false}, new object[] {new Unit(), true}, new object[] {Unit.Value, true},