### Install ServiceScan.SourceGenerator NuGet Package Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Add the ServiceScan.SourceGenerator NuGet package to your project using the .NET CLI. ```bash dotnet add package ServiceScan.SourceGenerator ``` -------------------------------- ### Add MediatR Handlers Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Register MediatR request handlers using GenerateServiceRegistrations. This example registers both IRequestHandler<> and IRequestHandler<,> types. ```csharp public static IServiceCollection AddMediatR(this IServiceCollection services) { return services .AddTransient() .AddMediatRHandlers(); } [GenerateServiceRegistrations(AssignableTo = typeof(IRequestHandler<>), Lifetime = ServiceLifetime.Transient)] [GenerateServiceRegistrations(AssignableTo = typeof(IRequestHandler<,>), Lifetime = ServiceLifetime.Transient)] private static partial IServiceCollection AddMediatRHandlers(this IServiceCollection services); ``` -------------------------------- ### Get All Matched Types as a Collection Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md When no handler is specified and the method returns a collection of types, this generator returns all types that match the specified criteria. ```csharp public static partial class TypeDiscovery { [ScanForTypes(AssignableTo = typeof(IService))] public static partial Type[] GetAllServiceTypes(); } ``` -------------------------------- ### Apply Entity Framework Core Configurations with ScanForTypes Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Automates the registration of IEntityTypeConfiguration implementations by scanning for types and invoking a handler method. Requires a partial method decorated with the ScanForTypes attribute. ```csharp using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using ServiceScan.SourceGenerator; public class User { public int Id { get; set; } public string Name { get; set; } } public class Order { public int Id { get; set; } public decimal Total { get; set; } } public class UserConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.HasKey(u => u.Id); builder.Property(u => u.Name).HasMaxLength(100); } } public class OrderConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.HasKey(o => o.Id); builder.Property(o => o.Total).HasPrecision(18, 2); } } public static partial class ModelBuilderExtensions { // Handler with two type parameters resolved from interface [ScanForTypes(AssignableTo = typeof(IEntityTypeConfiguration<>), Handler = nameof(ApplyConfiguration))] public static partial ModelBuilder ApplyEntityConfigurations(this ModelBuilder modelBuilder); private static void ApplyConfiguration(ModelBuilder modelBuilder) where TConfiguration : IEntityTypeConfiguration, new() where TEntity : class { modelBuilder.ApplyConfiguration(new TConfiguration()); } } // Generated code: // public static partial ModelBuilder ApplyEntityConfigurations(this ModelBuilder modelBuilder) // { // ApplyConfiguration(modelBuilder); // ApplyConfiguration(modelBuilder); // return modelBuilder; // } // Usage in DbContext: protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyEntityConfigurations(); } ``` -------------------------------- ### Register keyed services Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Register services with keys for keyed dependency injection using constants or methods. Requires .NET 8 or higher. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface IPaymentProcessor { } public class StripeProcessor : IPaymentProcessor { public const string Key = "stripe"; } public class PayPalProcessor : IPaymentProcessor { public const string Key = "paypal"; } public static partial class ServiceRegistrations { // Using a constant field in each type as the key [GenerateServiceRegistrations( AssignableTo = typeof(IPaymentProcessor), KeySelector = "Key", Lifetime = ServiceLifetime.Singleton)] public static partial IServiceCollection AddPaymentProcessorsWithConstKey(this IServiceCollection services); // Using a generic method to compute the key private static string GetProcessorKey() => typeof(T).Name.Replace("Processor", "").ToLower(); [GenerateServiceRegistrations( AssignableTo = typeof(IPaymentProcessor), KeySelector = nameof(GetProcessorKey), Lifetime = ServiceLifetime.Singleton)] public static partial IServiceCollection AddPaymentProcessorsWithMethodKey(this IServiceCollection services); } // Generated for AddPaymentProcessorsWithConstKey: // return services // .AddKeyedSingleton(StripeProcessor.Key) // .AddKeyedSingleton(PayPalProcessor.Key); // Usage: var provider = services.BuildServiceProvider(); var stripe = provider.GetRequiredKeyedService("stripe"); var paypal = provider.GetRequiredKeyedService("paypal"); ``` -------------------------------- ### Register as Self with Interface Forwarding Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use `AsSelf = true` along with `AsImplementedInterfaces = true` to register types as themselves while also forwarding interface resolutions to the concrete type. This ensures singleton instances are shared. Services are registered with a singleton lifetime. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface IServiceA { } public interface IServiceB { } public class MyService : IServiceA, IServiceB { } public static partial class ServiceRegistrations { // Register as self AND forward interfaces to the self registration [GenerateServiceRegistrations( TypeNameFilter = "*Service", AsImplementedInterfaces = true, AsSelf = true, Lifetime = ServiceLifetime.Singleton)] public static partial IServiceCollection AddServices(this IServiceCollection services); } // Generated code: // return services // .AddSingleton() // .AddSingleton(s => s.GetRequiredService()) // .AddSingleton(s => s.GetRequiredService()); // Usage - all resolve to the same singleton instance: var provider = services.BuildServiceProvider(); var concrete = provider.GetRequiredService(); var interfaceA = provider.GetRequiredService(); // Same instance var interfaceB = provider.GetRequiredService(); // Same instance ``` -------------------------------- ### Register Services with Decorator Pattern Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use the `ScanForTypes` attribute with a custom handler to register services and apply decorators. The handler wraps implementations to add custom logic before and after the original handler execution. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface ICommandHandler { Task HandleAsync(T command); } public class CommandHandlerDecorator : ICommandHandler { private readonly ICommandHandler _inner; public CommandHandlerDecorator(ICommandHandler inner) => _inner = inner; public async Task HandleAsync(T command) { Console.WriteLine($"Before handling {typeof(T).Name}"); await _inner.HandleAsync(command); Console.WriteLine($"After handling {typeof(T).Name}"); } } public record CreateOrderCommand(string ProductId); public record SendEmailCommand(string To, string Subject); public class CreateOrderHandler : ICommandHandler { public Task HandleAsync(CreateOrderCommand cmd) => Task.CompletedTask; } public class SendEmailHandler : ICommandHandler { public Task HandleAsync(SendEmailCommand cmd) => Task.CompletedTask; } public static partial class HandlerRegistrations { [ScanForTypes(AssignableTo = typeof(ICommandHandler<>), Handler = nameof(AddDecoratedHandler))] public static partial IServiceCollection AddCommandHandlers(this IServiceCollection services); private static void AddDecoratedHandler(IServiceCollection services) where THandler : class, ICommandHandler { services.AddScoped(); services.AddScoped>(sp => new CommandHandlerDecorator(sp.GetRequiredService())); } } ``` ```csharp // Generated code: // public static partial IServiceCollection AddCommandHandlers(this IServiceCollection services) // { // AddDecoratedHandler(services); // AddDecoratedHandler(services); // return services; // } ``` -------------------------------- ### Basic Service Registration with GenerateServiceRegistrations Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Use the GenerateServiceRegistrations attribute on a partial method to automatically register services. Specify the base type and service lifetime. ```csharp public static partial class ServicesExtensions { [GenerateServiceRegistrations(AssignableTo = typeof(IMyService), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddServices(this IServiceCollection services); } ``` ```csharp public static partial class ServicesExtensions { public static partial IServiceCollection AddServices(this IServiceCollection services) { return services .AddScoped() .AddScoped(); } } ``` -------------------------------- ### Register Repositories by Name Filter Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Register repository types based on a name filter (e.g., '*Repository') and automatically implement their interfaces. Services are registered as Scoped. ```csharp [GenerateServiceRegistrations( TypeNameFilter = "*Repository", AsImplementedInterfaces = true, Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddRepositories(this IServiceCollection services); ``` -------------------------------- ### Register Options with Custom Attributes and ScanForTypes Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Registers configuration options by scanning for a specific attribute and binding them to configuration sections. The handler method is responsible for reading the attribute metadata and configuring the service. ```csharp using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; using System; using System.Reflection; // Define custom attribute for options [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class OptionAttribute : Attribute { public string? Section { get; } public OptionAttribute(string? section = null) => Section = section; } // Define options classes [Option] // Uses type name as section key public record DatabaseOptions { public string ConnectionString { get; init; } = ""; } [Option("Email")] // Explicit section name public record EmailOptions { public string SmtpServer { get; init; } = ""; public int Port { get; init; } = 587; } public static partial class OptionsRegistrations { [ScanForTypes(AttributeFilter = typeof(OptionAttribute), Handler = nameof(AddOption))] public static partial IServiceCollection AddAllOptions(this IServiceCollection services, IConfiguration configuration); private static void AddOption(IServiceCollection services, IConfiguration configuration) where T : class { var attr = typeof(T).GetCustomAttribute(); var sectionKey = attr?.Section ?? typeof(T).Name; services.Configure(configuration.GetSection(sectionKey)); } } // Generated code: // public static partial IServiceCollection AddAllOptions(this IServiceCollection services, IConfiguration configuration) // { // AddOption(services, configuration); // AddOption(services, configuration); // return services; // } // appsettings.json: // { // "DatabaseOptions": { "ConnectionString": "..." }, // "Email": { "SmtpServer": "smtp.example.com", "Port": 587 } // } ``` -------------------------------- ### Basic Service Registration with GenerateServiceRegistrations Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Apply the GenerateServiceRegistrations attribute to a partial method to automatically register services assignable to a specified interface with the DI container. The default lifetime is Transient. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; // Define your service interface and implementations public interface IMyService { } public class ServiceImplementation1 : IMyService { } public class ServiceImplementation2 : IMyService { } // Create a partial class with the registration method public static partial class ServicesExtensions { [GenerateServiceRegistrations(AssignableTo = typeof(IMyService), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddServices(this IServiceCollection services); } // Usage in Program.cs or Startup.cs var services = new ServiceCollection(); services.AddServices(); ``` -------------------------------- ### Invoke Generated Service Registration Method Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md After the source generator runs, invoke the generated method on your IServiceCollection instance to apply the registrations. ```csharp services.AddServices(); ``` -------------------------------- ### Use HandlerTemplate to Build Descriptor Objects Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Demonstrates using HandlerTemplate to construct descriptor objects, incorporating 'typeof(T)' and additional context. This is useful for registering types with specific metadata. ```csharp public static partial class HandlerRegistry { [ScanForTypes(AssignableTo = typeof(ICommandHandler), HandlerTemplate = "new HandlerDescriptor(typeof(T), category)")] public static partial HandlerDescriptor[] GetDescriptors(string category); } ``` -------------------------------- ### ScanForTypes for ASP.NET Core Minimal API Endpoints Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Automatically map ASP.NET Core Minimal API endpoints by discovering types that implement a specific interface with a static method. This simplifies endpoint registration. ```csharp using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; using ServiceScan.SourceGenerator; // Define endpoint interface with static abstract method public interface IEndpoint { static abstract void MapEndpoint(IEndpointRouteBuilder endpoints); } // Implement endpoints public class GetUsersEndpoint : IEndpoint { public static void MapEndpoint(IEndpointRouteBuilder endpoints) { endpoints.MapGet("/api/users", () => new[] { "Alice", "Bob" }); } } public class CreateUserEndpoint : IEndpoint { public static void MapEndpoint(IEndpointRouteBuilder endpoints) { endpoints.MapPost("/api/users", (CreateUserRequest req) => Results.Created($"/api/users/{1}", req)); } } public record CreateUserRequest(string Name); public static partial class EndpointExtensions { // Call the static MapEndpoint method on each endpoint type [ScanForTypes(AssignableTo = typeof(IEndpoint), Handler = nameof(IEndpoint.MapEndpoint))] public static partial IEndpointRouteBuilder MapEndpoints(this IEndpointRouteBuilder endpoints); } ``` -------------------------------- ### ScanForTypes with Custom Handler for ServiceDescriptors Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use a custom handler to map discovered types to `ServiceDescriptor` objects. Ensure the handler method is static and accessible. ```csharp using ServiceScan.SourceGenerator; using Microsoft.Extensions.DependencyInjection; using System.Collections.Generic; public interface IService { } public class ServiceA : IService { } public class ServiceB : IService { } public static partial class ServiceDescriptorFactory { // Map each type to a ServiceDescriptor [ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(CreateDescriptor))] public static partial ServiceDescriptor[] GetServiceDescriptors(); private static ServiceDescriptor CreateDescriptor() where T : class, IService => ServiceDescriptor.Transient(typeof(IService), typeof(T)); // Map to custom info objects [ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(CreateInfo))] public static partial IEnumerable GetServiceInfos(); private static ServiceInfo CreateInfo() where T : IService => new ServiceInfo(typeof(T).Name, typeof(T).FullName); } public record ServiceInfo(string Name, string FullName); ``` -------------------------------- ### Use HandlerTemplate with Void Methods for Statements Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Shows how HandlerTemplate can be used with void methods, where each expanded expression becomes a statement. This is effective for performing actions like registration or logging for each matched type. ```csharp public static partial class PluginLoader { [ScanForTypes(AssignableTo = typeof(IPlugin), HandlerTemplate = "registry.Add(new T(options))")] public static partial void RegisterPlugins(PluginRegistry registry, PluginOptions options); } ``` -------------------------------- ### Map AspNetCore Minimal API Endpoints Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Automatically discover and map Minimal API endpoints using the ScanForTypes attribute and a custom handler. The handler specifies the method to call for mapping each endpoint. ```csharp public interface IEndpoint { abstract static void MapEndpoint(IEndpointRouteBuilder endpoints); } public class HelloWorldEndpoint : IEndpoint { public static void MapEndpoint(IEndpointRouteBuilder endpoints) { endpoints.MapGet("/", () => "Hello World!"); } } public static partial class ServiceCollectionExtensions { [ScanForTypes(AssignableTo = typeof(IEndpoint), Handler = nameof(IEndpoint.MapEndpoint))] public static partial IEndpointRouteBuilder MapEndpoints(this IEndpointRouteBuilder endpoints); } ``` -------------------------------- ### Discover Types with ScanForTypes Return Collections Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Uses ScanForTypes to return an array or IEnumerable of Type objects for all types matching the specified interface. ```csharp using ServiceScan.SourceGenerator; using System; using System.Collections.Generic; public interface IPlugin { } public class PluginA : IPlugin { } public class PluginB : IPlugin { } public class PluginC : IPlugin { } public static partial class TypeDiscovery { // Return Type[] of all matched types [ScanForTypes(AssignableTo = typeof(IPlugin))] public static partial Type[] GetAllPluginTypes(); // Return IEnumerable [ScanForTypes(AssignableTo = typeof(IPlugin))] public static partial IEnumerable EnumeratePluginTypes(); } // Generated code: // public static partial Type[] GetAllPluginTypes() // { // return [ // typeof(PluginA), // typeof(PluginB), // typeof(PluginC) // ]; // } // Usage: var pluginTypes = TypeDiscovery.GetAllPluginTypes(); foreach (var type in pluginTypes) { var plugin = (IPlugin)Activator.CreateInstance(type)!; plugin.Initialize(); } ``` -------------------------------- ### Filter Assemblies by Name with ServiceScan Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use `AssemblyNameFilter` with a pattern to scan multiple assemblies for types to register. This is beneficial for modular applications where services are distributed across various assemblies. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; // Assemblies: MyProduct.Core, MyProduct.Module1, MyProduct.Module2 // In MyProduct.Core: namespace Core; public interface IService { } // In MyProduct.Module1: namespace Module1; public class Service1 : Core.IService { } // In MyProduct.Module2: namespace Module2; public class Service2 : Core.IService { } // In your main application: public static partial class ServiceRegistrations { // Scan all MyProduct.* assemblies [GenerateServiceRegistrations( AssignableTo = typeof(Core.IService), AssemblyNameFilter = "MyProduct.*", Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddAllModuleServices(this IServiceCollection services); } ``` ```csharp // Generated code: // return services // .AddScoped() // .AddScoped(); ``` -------------------------------- ### Configure Service Lifetimes with GenerateServiceRegistrations Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Configure the lifetime of registered services (Transient, Scoped, Singleton) using the Lifetime property of the GenerateServiceRegistrations attribute. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface ITransientService { } public interface IScopedService { } public interface ISingletonService { } public class MyTransientService : ITransientService { } public class MyScopedService : IScopedService { } public class MySingletonService : ISingletonService { } public static partial class ServiceRegistrations { // Register as Transient (default if not specified) [GenerateServiceRegistrations(AssignableTo = typeof(ITransientService), Lifetime = ServiceLifetime.Transient)] public static partial IServiceCollection AddTransientServices(this IServiceCollection services); // Register as Scoped [GenerateServiceRegistrations(AssignableTo = typeof(IScopedService), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddScopedServices(this IServiceCollection services); // Register as Singleton [GenerateServiceRegistrations(AssignableTo = typeof(ISingletonService), Lifetime = ServiceLifetime.Singleton)] public static partial IServiceCollection AddSingletonServices(this IServiceCollection services); } // Usage var services = new ServiceCollection(); services.AddTransientServices() .AddScopedServices() .AddSingletonServices(); ``` -------------------------------- ### Use HandlerTemplate for Type Instantiation with Constructor Arguments Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Utilizes HandlerTemplate to generate code for instantiating types with constructor arguments. The 'T' placeholder is replaced with the fully-qualified name of the matched type. ```csharp public static partial class Factory { [ScanForTypes(AssignableTo = typeof(IPlugin), HandlerTemplate = "new T(options)")] public static partial IPlugin[] CreatePlugins(PluginOptions options); } ``` -------------------------------- ### Filter Services Using Generic Constraints Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Employ generic constraints within custom handlers to automatically filter types during source generation. Only types satisfying these constraints will be included. ```csharp using ServiceScan.SourceGenerator; public interface IService { } public class ServiceWithDefaultCtor : IService { } public class ServiceWithParamCtor : IService { public ServiceWithParamCtor(int value) { } } public class ServiceWithPrivateCtor : IService { private ServiceWithPrivateCtor() { } } public static partial class ConstrainedHandlers { // Only types with public parameterless constructor (new() constraint) [ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(HandleNewable))] public static partial void ProcessNewableServices(); private static void HandleNewable() where T : IService, new() { var instance = new T(); Console.WriteLine($"Created: {instance.GetType().Name}"); } // Only reference types (class constraint) [ScanForTypes(TypeNameFilter = "*Type", Handler = nameof(HandleClass))] public static partial void ProcessClassTypes(); private static void HandleClass() where T : class { Console.WriteLine($"Class type: {typeof(T).Name}"); } } ``` ```csharp // Generated for ProcessNewableServices (ServiceWithParamCtor and ServiceWithPrivateCtor excluded): // public static partial void ProcessNewableServices() // { // HandleNewable(); // } ``` -------------------------------- ### Use HandlerTemplate to Call Static Factory Method Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Employs HandlerTemplate to generate code for calling a static factory method that accepts multiple parameters. This allows for more complex object creation logic. ```csharp public static partial class PipelineBuilder { [ScanForTypes(AssignableTo = typeof(IPipelineStep), HandlerTemplate = "T.Create(context, logger)")] public static partial IPipelineStep[] BuildSteps(PipelineContext context, ILogger logger); } ``` -------------------------------- ### Scan External Assembly with ServiceScan Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use `FromAssemblyOf` to specify a type from an external assembly to scan for registrations. This is useful for registering services defined in separate DLLs. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; // In External.Assembly.dll: namespace External; public interface IExternalService { } public class ExternalService1 : IExternalService { } public class ExternalService2 : IExternalService { } // In your application: public static partial class ServiceRegistrations { [GenerateServiceRegistrations( FromAssemblyOf = typeof(External.IExternalService), AssignableTo = typeof(External.IExternalService), Lifetime = ServiceLifetime.Transient)] public static partial IServiceCollection AddExternalServices(this IServiceCollection services); } ``` ```csharp // Generated code: // return services // .AddTransient() // .AddTransient(); ``` -------------------------------- ### Apply EF Core IEntityTypeConfiguration Types Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Scans for types that implement IEntityTypeConfiguration and applies them to the ModelBuilder. The handler automatically instantiates and applies each found configuration. ```csharp public static partial class ModelBuilderExtensions { [ScanForTypes(AssignableTo = typeof(IEntityTypeConfiguration<>), Handler = nameof(ApplyConfiguration))] public static partial ModelBuilder ApplyEntityConfigurations(this ModelBuilder modelBuilder); private static void ApplyConfiguration(ModelBuilder modelBuilder) where T : IEntityTypeConfiguration, new() where TEntity : class { modelBuilder.ApplyConfiguration(new T()); } } ``` -------------------------------- ### ScanForTypes with HandlerTemplate for Inline Expressions Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Employ `HandlerTemplate` for concise, inline expressions where `T` is substituted with the matched type. This is useful for simple instantiation or method calls. ```csharp using ServiceScan.SourceGenerator; public interface IPlugin { void Initialize(); } public class PluginA : IPlugin { public PluginA(PluginOptions opts) { } public void Initialize() { } } public class PluginB : IPlugin { public PluginB(PluginOptions opts) { } public void Initialize() { } } public record PluginOptions(string Config); public static partial class PluginFactory { // Instantiate types with constructor arguments [ScanForTypes(AssignableTo = typeof(IPlugin), HandlerTemplate = "new T(options)")] public static partial IPlugin[] CreatePlugins(PluginOptions options); // Call static factory methods [ScanForTypes(AssignableTo = typeof(IPipelineStep), HandlerTemplate = "T.Create(context, logger)")] public static partial IPipelineStep[] BuildSteps(PipelineContext context, ILogger logger); // Build descriptors using typeof(T) [ScanForTypes(AssignableTo = typeof(IHandler), HandlerTemplate = "new HandlerDescriptor(typeof(T), category)")] public static partial HandlerDescriptor[] GetDescriptors(string category); // Void methods - each expression becomes a statement [ScanForTypes(AssignableTo = typeof(IPlugin), HandlerTemplate = "registry.Add(new T(options))")] public static partial void RegisterPlugins(PluginRegistry registry, PluginOptions options); } ``` -------------------------------- ### Invoke Custom Handlers with ScanForTypes Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Uses ScanForTypes to execute a custom generic method for every type matching the specified interface, optionally passing parameters. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface IService { } public class ServiceA : IService { } public class ServiceB : IService { } public static partial class TypeScanner { // Invoke a custom handler for each type found [ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(ProcessType))] public static partial void ProcessServices(); private static void ProcessType() where T : IService { Console.WriteLine($"Found service type: {typeof(T).Name}"); } // With parameters passed through [ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(ProcessTypeWithParams))] public static partial void ProcessServicesWithContext(string context, int count); private static void ProcessTypeWithParams(string context, int count) where T : IService { Console.WriteLine($"[{context}] Found {typeof(T).Name}, count: {count}"); } } // Generated code for ProcessServices: // public static partial void ProcessServices() // { // ProcessType(); // ProcessType(); // } ``` -------------------------------- ### Register Options Types with Custom Attribute Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Defines a custom OptionAttribute to specify configuration section keys and registers options types using this attribute. The handler reads the section key from the attribute and configures the options with the corresponding configuration section. ```csharp [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class OptionAttribute(string? section = null) : Attribute { public string? Section { get; } = section; } [Option] public record RootSection { } [Option("SectionOption")] public record SectionOption { } public static partial class ServiceCollectionExtensions { [ScanForTypes(AttributeFilter = typeof(OptionAttribute), Handler = nameof(AddOption))] public static partial IServiceCollection AddOptions(this IServiceCollection services, IConfiguration configuration); private static void AddOption(IServiceCollection services, IConfiguration configuration) where T : class { var sectionKey = typeof(T).GetCustomAttribute()?.Section; var section = sectionKey is null ? configuration : configuration.GetSection(sectionKey); services.Configure(section); } } ``` -------------------------------- ### Filter Types by Wildcard Name Patterns Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use `TypeNameFilter` with wildcards like `*` to match types ending with 'Repository'. Supports comma-separated values for multiple patterns. Services are registered as their implemented interfaces. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public class UserRepository { } public class OrderRepository { } public class ProductRepository { } public class SomeOtherClass { } public static partial class RepositoryRegistrations { // Register all types ending with "Repository" [GenerateServiceRegistrations( TypeNameFilter = "*Repository", AsImplementedInterfaces = true, Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddRepositories(this IServiceCollection services); // Multiple filters: types containing "User" OR "Order" [GenerateServiceRegistrations( TypeNameFilter = "*User*,*Order*", Lifetime = ServiceLifetime.Transient)] public static partial IServiceCollection AddUserAndOrderServices(this IServiceCollection services); } ``` -------------------------------- ### ScanForTypes Attribute Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Configuration options for the `ScanForTypes` attribute, used for custom type scanning and handling. ```APIDOC ## ScanForTypes Attribute ### Description This attribute is used to invoke a custom method for each matched type, offering more flexibility than direct registration. ### Parameters #### Attribute Properties - **Handler** (string) - Optional - Sets this property to invoke a custom method for each type found. Should point to a generic method name in the current type or a static method name in found types. Types are filtered by generic constraints. - **HandlerTemplate** (string) - Optional - Sets an expression template to evaluate for each type found. Use `T` as a placeholder for the fully-qualified name of each matched type. Incompatible with `Handler`. - **FromAssemblyOf** (Type) - Optional - Sets the assembly containing the given type as the source of types to scan. If not specified, the assembly containing the method with this attribute will be used. - **AssemblyNameFilter** (string) - Optional - Filters scanned assemblies by assembly name. Supports '*' wildcards and ',' for multiple filters. Incompatible with `FromAssemblyOf`. - **AssignableTo** (Type) - Optional - Sets the type that the scanned types must be assignable to. - **ExcludeAssignableTo** (Type) - Optional - Sets the type that the scanned types must *not* be assignable to. - **TypeNameFilter** (string) - Optional - Filters the types by their full name. Supports '*' wildcards and ',' for multiple filters. - **AttributeFilter** (Type) - Optional - Filters types by the specified attribute type being present. - **ExcludeByTypeName** (string) - Optional - Excludes types from being registered by their full name. Supports '*' wildcards and ',' for multiple filters. - **ExcludeByAttribute** (Type) - Optional - Excludes matching types by the specified attribute type being present. ``` -------------------------------- ### Register Types as Implemented Interfaces Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Set `AsImplementedInterfaces = true` to register types based on their implemented interfaces, suitable for DI patterns. Services are registered with a scoped lifetime. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface IUserService { } public interface IEmailService { } public interface ILoggable { } public class UserService : IUserService, ILoggable { } public class EmailService : IEmailService, ILoggable { } public static partial class ServiceRegistrations { // Register types as their implemented interfaces [GenerateServiceRegistrations( TypeNameFilter = "*Service", AsImplementedInterfaces = true, Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddServices(this IServiceCollection services); } ``` -------------------------------- ### Exclude types from DI registration Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use exclusion filters based on type name patterns, attributes, or interface/base class constraints to prevent specific services from being registered. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; using System; [AttributeUsage(AttributeTargets.Class)] public sealed class ExcludeFromDIAttribute : Attribute { } public interface IExcludable { } public class IncludedService { } public class ExcludedByNameService { } [ExcludeFromDI] public class ExcludedByAttributeService { } public class ExcludedByInterfaceService : IExcludable { } public static partial class ServiceRegistrations { // Exclude by type name pattern [GenerateServiceRegistrations( TypeNameFilter = "*Service", ExcludeByTypeName = "*ByName*", Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddServicesExcludeByName(this IServiceCollection services); // Exclude by attribute [GenerateServiceRegistrations( TypeNameFilter = "*Service", ExcludeByAttribute = typeof(ExcludeFromDIAttribute), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddServicesExcludeByAttribute(this IServiceCollection services); // Exclude by interface/base class [GenerateServiceRegistrations( TypeNameFilter = "*Service", ExcludeAssignableTo = typeof(IExcludable), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddServicesExcludeByInterface(this IServiceCollection services); } ``` -------------------------------- ### Register Open Generic Types with ServiceScan Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use the `GenerateServiceRegistrations` attribute with `AssignableTo = typeof(Interface<>)` to register open generic implementations. The generator handles the `typeof()` syntax for open generics. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public interface IRepository { } public class Repository : IRepository { } public class CachedRepository : IRepository { } public interface IHandler { } public class StringHandler : IHandler { } public class ObjectHandler : IHandler { } public static partial class ServiceRegistrations { // Register open generic implementations [GenerateServiceRegistrations( AssignableTo = typeof(IRepository<>), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddRepositories(this IServiceCollection services); // Register closed generic implementations [GenerateServiceRegistrations( AssignableTo = typeof(IHandler<,>), Lifetime = ServiceLifetime.Transient)] public static partial IServiceCollection AddHandlers(this IServiceCollection services); } ``` ```csharp // Generated for AddRepositories (open generics): // return services // .AddScoped(typeof(IRepository<>), typeof(Repository<>)) // .AddScoped(typeof(IRepository<>), typeof(CachedRepository<>)); ``` ```csharp // Generated for AddHandlers (closed generics): // return services // .AddTransient, StringHandler>() // .AddTransient, ObjectHandler>(); ``` -------------------------------- ### GenerateServiceRegistrations Attribute Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Configuration options for the `GenerateServiceRegistrations` attribute, used for automatic service registration. ```APIDOC ## GenerateServiceRegistrations Attribute ### Description This attribute is used to automatically register services based on specified criteria. ### Parameters #### Attribute Properties - **FromAssemblyOf** (Type) - Optional - Sets the assembly containing the given type as the source of types to register. If not specified, the assembly containing the method with this attribute will be used. - **AssemblyNameFilter** (string) - Optional - Filters scanned assemblies by assembly name. Supports '*' wildcards and ',' for multiple filters. Incompatible with `FromAssemblyOf`. - **AssignableTo** (Type) - Optional - Sets the type that the registered types must be assignable to. Types will be registered with this type as the service type, unless `AsImplementedInterfaces` or `AsSelf` is set. - **ExcludeAssignableTo** (Type) - Optional - Sets the type that the registered types must *not* be assignable to. - **Lifetime** (ServiceLifetime) - Optional - Sets the lifetime of the registered services. Defaults to `ServiceLifetime.Transient`. - **AsImplementedInterfaces** (bool) - Optional - If true, types will be registered as their implemented interfaces instead of their actual type. - **AsSelf** (bool) - Optional - If true, types will be registered with their actual type. Can be combined with `AsImplementedInterfaces`. - **TypeNameFilter** (string) - Optional - Filters the types to register by their full name. Supports '*' wildcards and ',' for multiple filters. - **AttributeFilter** (Type) - Optional - Filters types by the specified attribute type being present. - **ExcludeByTypeName** (string) - Optional - Excludes types from being registered by their full name. Supports '*' wildcards and ',' for multiple filters. - **ExcludeByAttribute** (Type) - Optional - Excludes matching types by the specified attribute type being present. - **KeySelector** (string) - Optional - Adds types as keyed services. Should point to a static method name (string return type, generic or single `Type` parameter) or a constant field/static property in the implementation type. - **CustomHandler** (Delegate) - Obsolete - Use `ScanForTypes` instead. Invokes a custom method for each type found instead of regular registration logic. ``` -------------------------------- ### Register MediatR Handlers with GenerateServiceRegistrations Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Uses GenerateServiceRegistrations to automatically register MediatR request and notification handlers with the dependency injection container. ```csharp using MediatR; using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public record GetUserQuery(int Id) : IRequest; public record CreateUserCommand(string Name) : IRequest; public record UserCreatedNotification(int UserId) : INotification; public class GetUserHandler : IRequestHandler { public Task Handle(GetUserQuery request, CancellationToken ct) => Task.FromResult(new User()); } public class CreateUserHandler : IRequestHandler { public Task Handle(CreateUserCommand request, CancellationToken ct) => Task.FromResult(1); } public class UserCreatedHandler : INotificationHandler { public Task Handle(UserCreatedNotification notification, CancellationToken ct) => Task.CompletedTask; } public static partial class MediatRRegistrations { public static IServiceCollection AddMediatR(this IServiceCollection services) { return services .AddTransient() .AddMediatRHandlers(); } // Register all request handlers (with single return type) [GenerateServiceRegistrations(AssignableTo = typeof(IRequestHandler<>), Lifetime = ServiceLifetime.Transient)] // Register all request handlers (with two type parameters) [GenerateServiceRegistrations(AssignableTo = typeof(IRequestHandler<,>), Lifetime = ServiceLifetime.Transient)] // Register all notification handlers [GenerateServiceRegistrations(AssignableTo = typeof(INotificationHandler<>), Lifetime = ServiceLifetime.Transient)] private static partial IServiceCollection AddMediatRHandlers(this IServiceCollection services); } ``` -------------------------------- ### Register FluentValidation Validators Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Register all FluentValidation validators by specifying IValidator<> as the base type. This approach is AOT-compatible and avoids startup performance impacts. ```csharp [GenerateServiceRegistrations(AssignableTo = typeof(IValidator<>), Lifetime = ServiceLifetime.Singleton)] public static partial IServiceCollection AddValidators(this IServiceCollection services); ``` -------------------------------- ### Filter Types by Attribute Presence Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Use `AttributeFilter` to register types that are marked with a specific attribute, enabling marker-based registration patterns. Services are registered with a scoped lifetime. ```csharp using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; using System; // Define a marker attribute [AttributeUsage(AttributeTargets.Class)] public sealed class ServiceAttribute : Attribute { } // Mark classes for registration [Service] public class AuthenticationService { } [Service] public class AuthorizationService { } public class NotAService { } // Not marked, won't be registered public static partial class ServiceRegistrations { [GenerateServiceRegistrations( AttributeFilter = typeof(ServiceAttribute), Lifetime = ServiceLifetime.Scoped)] public static partial IServiceCollection AddMarkedServices(this IServiceCollection services); } ``` -------------------------------- ### Map Matched Types to Custom Result Type Source: https://github.com/dreamescaper/servicescan.sourcegenerator/blob/main/README.md Maps discovered types to a custom result type using a specified handler. This is useful for creating descriptors or other custom objects based on the discovered types. ```csharp public static partial class TypeDiscovery { [ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(Describe))] public static partial ServiceDescriptor[] GetServiceDescriptors(); private static ServiceDescriptor Describe() where T : IService => ServiceDescriptor.Transient(typeof(IService), typeof(T)); } ``` -------------------------------- ### Register FluentValidation validators Source: https://context7.com/dreamescaper/servicescan.sourcegenerator/llms.txt Automatically register all IValidator implementations with AOT compatibility. ```csharp using FluentValidation; using Microsoft.Extensions.DependencyInjection; using ServiceScan.SourceGenerator; public class CreateUserCommand { public string Name { get; set; } } public class UpdateUserCommand { public int Id { get; set; } } public class CreateUserValidator : AbstractValidator { public CreateUserValidator() { RuleFor(x => x.Name).NotEmpty(); } } public class UpdateUserValidator : AbstractValidator { public UpdateUserValidator() { RuleFor(x => x.Id).GreaterThan(0); } } public static partial class ServiceRegistrations { // Register all IValidator implementations [GenerateServiceRegistrations( AssignableTo = typeof(IValidator<>), Lifetime = ServiceLifetime.Singleton)] public static partial IServiceCollection AddValidators(this IServiceCollection services); } // Generated code: // return services // .AddSingleton, CreateUserValidator>() // .AddSingleton, UpdateUserValidator>(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.