diff --git a/README.md b/README.md
index 3a0c62f..1ccfe65 100644
--- a/README.md
+++ b/README.md
@@ -164,4 +164,4 @@ public static partial class ModelBuilderExtensions
| **ExcludeByTypeName** | Sets this value to exclude types from being registered by their full name. You can use '*' wildcards. You can also use ',' to separate multiple filters. |
| **ExcludeByAttribute** | Excludes matching types by the specified attribute type being present. |
| **KeySelector** | Sets this property to add types as keyed services. This property should point to one of the following: - The name of a static method in the current type with a string return type. The method should be either generic or have a single parameter of type `Type`. - A constant field or static property in the implementation type. |
-| **CustomHandler** | Sets this property to invoke a custom method for each type found instead of regular registration logic. This property should point to one of the following: - Name of a generic method in the current type. - Static method name in found types. This property is incompatible with `Lifetime`, `AsImplementedInterfaces`, `AsSelf`, and `KeySelector` properties. |
\ No newline at end of file
+| **CustomHandler** | Sets this property to invoke a custom method for each type found instead of regular registration logic. This property should point to one of the following: - Name of a generic method in the current type. - Static method name in found types. This property is incompatible with `Lifetime`, `AsImplementedInterfaces`, `AsSelf`, and `KeySelector` properties. **Note:** When using a generic `CustomHandler` method, types are automatically filtered by the generic constraints defined on the method's type parameters (e.g., `class`, `struct`, `new()`, interface constraints). |
\ No newline at end of file
diff --git a/ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs b/ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs
index 01bdd1f..3fb9e01 100644
--- a/ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs
+++ b/ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs
@@ -105,6 +105,51 @@ public static partial void ProcessServices( string value, decimal number)
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
}
+ [Fact]
+ public void CustomHandler_NoTypesFound()
+ {
+ var source = $$"""
+ using ServiceScan.SourceGenerator;
+
+ namespace GeneratorTests;
+
+ public static partial class ServicesExtensions
+ {
+ [GenerateServiceRegistrations(AssignableTo = typeof(IService), CustomHandler = nameof(HandleType))]
+ public static partial void ProcessServices();
+
+ private static void HandleType() => System.Console.WriteLine(typeof(T).Name);
+ }
+ """;
+
+ var services =
+ """
+ namespace GeneratorTests;
+
+ public interface IService { }
+ """;
+
+ var compilation = CreateCompilation(source, services);
+
+ var results = CSharpGeneratorDriver
+ .Create(_generator)
+ .RunGenerators(compilation)
+ .GetRunResult();
+
+ var expected = $$"""
+ namespace GeneratorTests;
+
+ public static partial class ServicesExtensions
+ {
+ public static partial void ProcessServices()
+ {
+
+ }
+ }
+ """;
+ Assert.Equal(expected, results.GeneratedTrees[1].ToString());
+ }
+
[Fact]
public void CustomHandlerExtensionMethod()
{
@@ -663,6 +708,380 @@ public static partial class ServiceCollectionExtensions
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
}
+ [Fact]
+ public void CustomHandler_FiltersByNewConstraint()
+ {
+ var source = """
+ using ServiceScan.SourceGenerator;
+
+ namespace GeneratorTests;
+
+ public static partial class ServicesExtensions
+ {
+ [GenerateServiceRegistrations(AssignableTo = typeof(IService), CustomHandler = nameof(HandleType))]
+ public static partial void ProcessServices();
+
+ private static void HandleType() where T : IService, new() => System.Console.WriteLine(typeof(T).Name);
+ }
+ """;
+
+ var services = """
+ namespace GeneratorTests;
+
+ public interface IService { }
+ public class ServiceWithParameterlessConstructor : IService { }
+ public class ServiceWithoutParameterlessConstructor : IService
+ {
+ public ServiceWithoutParameterlessConstructor(int value) { }
+ }
+ public class ServiceWithPrivateConstructor : IService
+ {
+ private ServiceWithPrivateConstructor() { }
+ }
+ """;
+
+ var compilation = CreateCompilation(source, services);
+
+ var results = CSharpGeneratorDriver
+ .Create(_generator)
+ .RunGenerators(compilation)
+ .GetRunResult();
+
+ var expected = """
+ namespace GeneratorTests;
+
+ public static partial class ServicesExtensions
+ {
+ public static partial void ProcessServices()
+ {
+ HandleType();
+ }
+ }
+ """;
+ Assert.Equal(expected, results.GeneratedTrees[1].ToString());
+ }
+
+ [Fact]
+ public void CustomHandler_FiltersByClassConstraint()
+ {
+ var source = """
+ using ServiceScan.SourceGenerator;
+
+ namespace GeneratorTests;
+
+ public static partial class ServicesExtensions
+ {
+ [GenerateServiceRegistrations(TypeNameFilter = "*Service", CustomHandler = nameof(HandleType))]
+ public static partial void ProcessServices();
+
+ private static void HandleType() where T : class => System.Console.WriteLine(typeof(T).Name);
+ }
+ """;
+
+ var services = """
+ namespace GeneratorTests;
+
+ public class ClassService { }
+ public struct StructService { }
+ """;
+
+ var compilation = CreateCompilation(source, services);
+
+ var results = CSharpGeneratorDriver
+ .Create(_generator)
+ .RunGenerators(compilation)
+ .GetRunResult();
+
+ var expected = """
+ namespace GeneratorTests;
+
+ public static partial class ServicesExtensions
+ {
+ public static partial void ProcessServices()
+ {
+ HandleType();
+ }
+ }
+ """;
+ Assert.Equal(expected, results.GeneratedTrees[1].ToString());
+ }
+
+ [Fact]
+ public void CustomHandler_FiltersByNestedTypeParameterConstraints()
+ {
+ var source = """
+ using ServiceScan.SourceGenerator;
+
+ namespace GeneratorTests;
+
+ public static partial class ServiceCollectionExtensions
+ {
+ [GenerateServiceRegistrations(AssignableTo = typeof(ICommandHandler<>), CustomHandler = nameof(AddHandler))]
+ public static partial void AddHandlers();
+
+ private static void AddHandler()
+ where THandler : class, ICommandHandler
+ where TCommand : class, ICommand
+ {
+ }
+ }
+ """;
+
+ var services = """
+ namespace GeneratorTests;
+
+ public interface ICommand { }
+ public interface ICommandHandler where T : ICommand { }
+
+ public class ValidCommand : ICommand { }
+ public class InvalidCommand { }
+
+ public class ValidHandler : ICommandHandler { }
+ public class InvalidHandler : ICommandHandler { }
+ """;
+
+ var compilation = CreateCompilation(source, services);
+
+ var results = CSharpGeneratorDriver
+ .Create(_generator)
+ .RunGenerators(compilation)
+ .GetRunResult();
+
+ var expected = """
+ namespace GeneratorTests;
+
+ public static partial class ServiceCollectionExtensions
+ {
+ public static partial void AddHandlers()
+ {
+ AddHandler();
+ }
+ }
+ """;
+ Assert.Equal(expected, results.GeneratedTrees[1].ToString());
+ }
+
+ [Fact]
+ public void CustomHandler_FiltersByMultipleInterfacesWithDifferentTypeArguments()
+ {
+ var source = """
+ using ServiceScan.SourceGenerator;
+
+ namespace GeneratorTests;
+
+ public static partial class ServiceCollectionExtensions
+ {
+ [GenerateServiceRegistrations(AssignableTo = typeof(IHandler<>), CustomHandler = nameof(AddHandler))]
+ public static partial void AddHandlers();
+
+ private static void AddHandler()
+ where THandler : class, IHandler
+ where TArg : class
+ {
+ }
+ }
+ """;
+
+ var services = """
+ namespace GeneratorTests;
+
+ public interface IHandler { }
+
+ public class Handler1 : IHandler { }
+ public class Handler2 : IHandler