Open NeoElitech opened 1 year ago
namespace DecoratorDemo
{
internal class Program
{
static void Main(string[] args)
{
var container = new Container();
var serviceProvider = new ServiceCollection()
.AddSimpleInjector(container)
.AddServices()
.BuildServiceProvider()
.UseSimpleInjector(container);
var command = serviceProvider.GetRequiredService<ICommandHandler<FooCommand>>();
command.Handle(new FooCommand());
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddServices(this IServiceCollection services)
{
// *********** Can NOT Decorate ***********
return services
.AddTransient(typeof(ICommandHandler<>), typeof(CommandHandler<>))
.Decorate(typeof(ICommandHandler<>), typeof(CommandHandlerDecorator<>));
// *********** Can Decorate ***********
//return services
// .AddTransient<ICommandHandler<FooCommand>, CommandHandler<FooCommand>>()
// .Decorate(typeof(ICommandHandler<>), typeof(CommandHandlerDecorator<>));
}
}
}
Hello @NeoElitech! 👋🏻
Sorry for the long reply time.
The issue with open generic decoration is that Scrutor needs to know which concrete services are registered ahead of time. It does this by inspecting the concrete/closed services already registered in the container. If you replace the first AddTransient
call with something like
services.Scan(scan => scan
.FromAssemblyOf(typeof(ICommandHandler<>))
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>)))
.AsImplementedInterfaces()
.WithTransientLifetime());
Scrutor will can the specified assembly and add all closed generic services that implements ICommandHandler<T>
.
When you then call Decorate
, it should be able to find the registrations and successfully decorate them 😄
I'm running into a similar problem, and the suggested solution doesn't provide any help. We have an assembly with an interface, a service and service decorator. They're all in the same assembly with their own extension method to register them:
public interface IGetJoinedDocumentsService<out TDocument, in TUniqueId>
{
/// <summary>
/// Retrieves data of type <see cref="TDocument"/> related to a specified <paramref name="uniqueId"/>.
/// </summary>
/// <param name="uniqueId">An instance of <see cref="TUniqueId"/> of a to retrieve related data for.</param>
/// <param name="cancellationToken">An instance of <see cref="CancellationToken"/> to stop enumeration.</param>
/// <returns>A collection of <see cref="TDocument"/> with data; empty collection if there are no data.</returns>
IAsyncEnumerable<TDocument> GetAsync(TUniqueId uniqueId, CancellationToken cancellationToken);
}
internal class GetJoinedDocumentsService<TDocument, TUniqueId> : IGetJoinedDocumentsService<TDocument, TUniqueId> {}
public class GetDocumentEnrichmentDecorator<TDocument, TUniqueId> : IGetDocument<TDocument, TUniqueId> {}
And this throws the error:
var assembly = typeof(IGetJoinedDocumentsService<,>).Assembly;
serviceCollection.Scan(scan => scan
.FromAssemblies(assembly)
.AddClasses(classes => classes.AssignableTo(typeof(IGetJoinedDocumentsService<,>)))
.AsImplementedInterfaces()
.WithTransientLifetime());
return serviceCollection
.AddScoped(typeof(IGetJoinedDocumentsService<,>), typeof(GetJoinedDocumentsService<,>))
.Decorate(typeof(IGetJoinedDocumentsService<,>), typeof(GetJoinedDocumentsServiceEnrichmentDecorator<,>))
Scrutor.DecorationException: Could not find any registered services for type 'BizMachine.Prospector.Modules.Filtering.Enrichment.IGetJoinedDocumentsService<TDocument, TUniqueId>'.
at Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.Decorate(IServiceCollection services, DecorationStrategy strategy)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.Decorate(IServiceCollection services, Type serviceType, Type decoratorType)
at Microsoft.Extensions.DependencyInjection.EnrichmentServiceCollectionExtensions.AddFilteringEnrichmentDecorators(IServiceCollection serviceCollection) in D:\src\bm-products-api\Modules.Filtering.Enrichment\EnrichmentServiceCollectionExtensions.cs:line 23
at Program.<>c.<<Main>$>b__0_0(HostBuilderContext context, IServiceCollection services) in D:\src\bm-products-api\Modules.Filtering.ElasticSearch.Console\Program.cs:line 21
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at Program.<Main>$(String[] args) in D:\src\bm-products-api\Modules.Filtering.ElasticSearch.Console\Program.cs:line 12
at Program.<Main>(String[] args)
I believe this is caused by #39 and can't be fixed as things are today were in 2017. IT seems by this SO thread that there are ways to do it with the MS DI.
Hi @khellang, I'm using Scrutor to decorate an open generic type but I receive the following exception.
Could not find any registered services for type 'DecoratorDemo.ICommandHandler<TCommand>
I found a similar issue. But it's fixed already. Here is the link to the repository I'm using .NET.4.8 and Scrutor.4.2.2