Skip to content

Commit 0670eaa

Browse files
authored
fix: Filter out open generics when used with CustomHandler (#47)
1 parent de9bd2c commit 0670eaa

3 files changed

Lines changed: 67 additions & 3 deletions

File tree

ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,66 @@ public partial void ProcessServices( global::Microsoft.Extensions.DependencyInje
603603
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
604604
}
605605

606+
[Fact]
607+
public void AddServicesWithDecorator()
608+
{
609+
var services = """
610+
namespace GeneratorTests;
611+
612+
public interface ICommandHandler<T> { }
613+
public class CommandHandlerDecorator<T>(ICommandHandler<T> inner) : ICommandHandler<T>;
614+
615+
public class SpecificHandler1 : ICommandHandler<string>;
616+
public class SpecificHandler2 : ICommandHandler<long>;
617+
""";
618+
619+
var source = """
620+
using ServiceScan.SourceGenerator;
621+
using Microsoft.Extensions.DependencyInjection;
622+
623+
namespace GeneratorTests;
624+
625+
public static partial class ServiceCollectionExtensions
626+
{
627+
[GenerateServiceRegistrations(AssignableTo = typeof(ICommandHandler<>), CustomHandler = nameof(AddDecoratedHandler))]
628+
public static partial IServiceCollection AddHandlers(this IServiceCollection services);
629+
630+
private static void AddDecoratedHandler<THandler, TCommand>(this IServiceCollection services)
631+
where THandler : class, ICommandHandler<TCommand>
632+
{
633+
// Add handler itself to DI
634+
services.AddScoped<THandler>();
635+
636+
// Register decorated handler as ICommandHandler
637+
services.AddScoped<ICommandHandler<TCommand>>(s => new CommandHandlerDecorator<TCommand>(s.GetRequiredService<THandler>()));
638+
}
639+
}
640+
""";
641+
642+
643+
var compilation = CreateCompilation(source, services);
644+
645+
var results = CSharpGeneratorDriver
646+
.Create(_generator)
647+
.RunGenerators(compilation)
648+
.GetRunResult();
649+
650+
var expected = $$"""
651+
namespace GeneratorTests;
652+
653+
public static partial class ServiceCollectionExtensions
654+
{
655+
public static partial global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddHandlers(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
656+
{
657+
AddDecoratedHandler<global::GeneratorTests.SpecificHandler1, string>(services);
658+
AddDecoratedHandler<global::GeneratorTests.SpecificHandler2, long>(services);
659+
return services;
660+
}
661+
}
662+
""";
663+
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
664+
}
665+
606666
private static Compilation CreateCompilation(params string[] source)
607667
{
608668
var path = Path.GetDirectoryName(typeof(object).Assembly.Location)!;

ServiceScan.SourceGenerator/DependencyInjectionGenerator.FilterTypes.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public partial class DependencyInjectionGenerator
5858
if (type.IsStatic && attribute.CustomHandlerType != CustomHandlerType.TypeMethod)
5959
continue;
6060

61+
// Cannot use open generics with CustomHandler
62+
if (type.IsGenericType && attribute.CustomHandler != null)
63+
continue;
64+
6165
if (attributeFilterType != null)
6266
{
6367
if (!type.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeFilterType)))

ServiceScan.SourceGenerator/DependencyInjectionGenerator.FindServicesToRegister.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ .. matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.F
9090
attribute.Lifetime,
9191
serviceTypeName,
9292
implementationTypeName,
93-
false,
94-
true,
93+
ResolveImplementation: false,
94+
IsOpenGeneric: true,
9595
attribute.KeySelector,
9696
attribute.KeySelectorType);
9797

@@ -105,7 +105,7 @@ .. matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.F
105105
serviceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
106106
implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
107107
shouldResolve,
108-
false,
108+
IsOpenGeneric: false,
109109
attribute.KeySelector,
110110
attribute.KeySelectorType);
111111

0 commit comments

Comments
 (0)