{"id":13628743,"url":"https://github.com/Dreamescaper/ServiceScan.SourceGenerator","last_synced_at":"2025-04-17T04:32:14.086Z","repository":{"id":243953422,"uuid":"795012467","full_name":"Dreamescaper/ServiceScan.SourceGenerator","owner":"Dreamescaper","description":"Assembly scanning source generator for Microsoft.Extensions.DependencyInjection","archived":false,"fork":false,"pushed_at":"2025-03-25T09:12:15.000Z","size":79,"stargazers_count":56,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-13T16:40:34.973Z","etag":null,"topics":["assembly-scanning","dependency-injection","dotnet","source-generator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Dreamescaper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-05-02T12:13:07.000Z","updated_at":"2025-04-05T15:39:04.000Z","dependencies_parsed_at":"2024-06-29T08:40:34.563Z","dependency_job_id":"0e711761-cddb-4795-b75e-286c85327202","html_url":"https://github.com/Dreamescaper/ServiceScan.SourceGenerator","commit_stats":null,"previous_names":["dreamescaper/servicescan.sourcegenerator"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dreamescaper%2FServiceScan.SourceGenerator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dreamescaper%2FServiceScan.SourceGenerator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dreamescaper%2FServiceScan.SourceGenerator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dreamescaper%2FServiceScan.SourceGenerator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Dreamescaper","download_url":"https://codeload.github.com/Dreamescaper/ServiceScan.SourceGenerator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249315977,"owners_count":21249868,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembly-scanning","dependency-injection","dotnet","source-generator"],"created_at":"2024-08-01T22:00:56.847Z","updated_at":"2025-04-17T04:32:14.080Z","avatar_url":"https://github.com/Dreamescaper.png","language":"C#","readme":"# ServiceScan.SourceGenerator\n[![NuGet Version](https://img.shields.io/nuget/v/ServiceScan.SourceGenerator)](https://www.nuget.org/packages/ServiceScan.SourceGenerator/)\n\nSource generator for services registrations inspired by [Scrutor](https://github.com/khellang/Scrutor/).\nCode generation allows to have AOT-compatible code, without an additional hit on startup performance due to runtime assembly scanning.\n\n## Installation \nAdd the NuGet Package to your project:\n```\ndotnet add package ServiceScan.SourceGenerator\n```\n\n## Usage\n\n`ServiceScan` generates a partial method implementation based on `GenerateServiceRegistrations` attribute. This attribute can be added to a partial method with `IServiceCollection` parameter. \nFor example, based on the following partial method:\n```csharp\npublic static partial class ServicesExtensions\n{\n    [GenerateServiceRegistrations(AssignableTo = typeof(IMyService), Lifetime = ServiceLifetime.Scoped)]\n    public static partial IServiceCollection AddServices(this IServiceCollection services);\n}\n```\n\n`ServiceScan` will generate the following implementation:\n```csharp\npublic static partial class ServicesExtensions\n{\n    public static partial IServiceCollection AddServices(this IServiceCollection services)\n    {\n        return services\n            .AddScoped\u003cIMyService, ServiceImplementation1\u003e()\n            .AddScoped\u003cIMyService, ServiceImplementation2\u003e();\n    }\n}\n```\n\nThe only thing left is to invoke this method on your `IServiceCollection` instance\n```csharp\nservices.AddServices();\n```\n\n## Examples\n\n### Register all [FluentValidation](https://github.com/FluentValidation/FluentValidation) validators\nUnlike using `FluentValidation.DependencyInjectionExtensions` package, `ServiceScan` is AOT-compatible, and doesn't affect startup performance:\n```csharp\n[GenerateServiceRegistrations(AssignableTo = typeof(IValidator\u003c\u003e), Lifetime = ServiceLifetime.Singleton)]\npublic static partial IServiceCollection AddValidators(this IServiceCollection services);\n```\n\n### Add [MediatR](https://github.com/jbogard/MediatR) handlers\n```csharp\npublic static IServiceCollection AddMediatR(this IServiceCollection services)\n{\n    return services\n        .AddTransient\u003cIMediator, Mediator\u003e()\n        .AddMediatRHandlers();\n}\n\n[GenerateServiceRegistrations(AssignableTo = typeof(IRequestHandler\u003c\u003e), Lifetime = ServiceLifetime.Transient)]\n[GenerateServiceRegistrations(AssignableTo = typeof(IRequestHandler\u003c,\u003e), Lifetime = ServiceLifetime.Transient)]\nprivate static partial IServiceCollection AddMediatRHandlers(this IServiceCollection services);\n```\nIt adds MediatR requests handlers, although you might need to add other types like PipelineBehaviors or NotificationHandlers.\n\n### Add all repository types from your project based on name filter as their implemented interfaces:\n```csharp\n[GenerateServiceRegistrations(\n    TypeNameFilter = \"*Repository\",\n    AsImplementedInterfaces = true,\n    Lifetime = ServiceLifetime.Scoped)]\nprivate static partial IServiceCollection AddRepositories(this IServiceCollection services);\n```\n\n### Add AspNetCore Minimal API endpoints\nYou can add custom type handler, if you need to do something non-trivial with that type. For example, you can automatically discover\nand map Minimal API endpoints:\n```csharp\npublic interface IEndpoint\n{\n    abstract static void MapEndpoint(IEndpointRouteBuilder endpoints);\n}\n\npublic class HelloWorldEndpoint : IEndpoint\n{\n    public static void MapEndpoint(IEndpointRouteBuilder endpoints)\n    {\n        endpoints.MapGet(\"/\", () =\u003e \"Hello World!\");\n    }\n}\n\npublic static partial class ServiceCollectionExtensions\n{\n    [GenerateServiceRegistrations(AssignableTo = typeof(IEndpoint), CustomHandler = nameof(MapEndpoint))]\n    public static partial IEndpointRouteBuilder MapEndpoints(this IEndpointRouteBuilder endpoints);\n\n    private static void MapEndpoint\u003cT\u003e(IEndpointRouteBuilder endpoints) where T : IEndpoint\n    {\n        T.MapEndpoint(endpoints);\n    }\n}\n```\n\n### Register Options types\nAnother example of `CustomHandler` is to register Options types. We can define custom `OptionAttribute`, which allows to specify configuration section key.\nAnd then read that value in our `CustomHandler`:\n```csharp\n[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]\npublic class OptionAttribute(string? section = null) : Attribute\n{\n    public string? Section { get; } = section;\n}\n\n[Option]\npublic record RootSection { }\n\n[Option(\"SectionOption\")]\npublic record SectionOption { }\n\npublic static partial class ServiceCollectionExtensions\n{\n    [GenerateServiceRegistrations(AttributeFilter = typeof(OptionAttribute), CustomHandler = nameof(AddOption))]\n    public static partial IServiceCollection AddOptions(this IServiceCollection services, IConfiguration configuration);\n\n    private static void AddOption\u003cT\u003e(IServiceCollection services, IConfiguration configuration) where T : class\n    {\n        var sectionKey = typeof(T).GetCustomAttribute\u003cOptionAttribute\u003e()?.Section;\n        var section = sectionKey is null ? configuration : configuration.GetSection(sectionKey);\n        services.Configure\u003cT\u003e(section);\n    }\n}\n```\n\n\n## Parameters\n\n`GenerateServiceRegistrations` attribute has the following properties:\n| Property | Description |\n| --- | --- |\n| **FromAssemblyOf** |Set 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. |\n| **AssignableTo** | Set 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. |\n| **Lifetime** | Set the lifetime of the registered services. `ServiceLifetime.Transient` is used if not specified. |\n| **AsImplementedInterfaces** | If true, the registered types will be registered as implemented interfaces instead of their actual type. |\n| **AsSelf** | If true, types will be registered with their actual type. It can be combined with `AsImplementedInterfaces`. In that case implemented interfaces will be \"forwarded\" to an actual implementation type |\n| **TypeNameFilter** | Set this value to filter the types to register by their full name. You can use '*' wildcards. You can also use ',' to separate multiple filters. |\n| **AttributeFilter** | Filter types by specified attribute type present. |\n| **KeySelector** | Set this property to add types as keyed services. This property should point to one of the following: \u003cbr\u003e- Name of the static method in the current type with string return type. Method should be either generic, or have a single parameter of type `Type`. \u003cbr\u003e- Const field or static property in the implementation type. |\n| **CustomHandler** | Set this property to a static generic method name in the current class. This property is incompatible with `Lifetime`, `AsImplementedInterfaces`, `AsSelf`, `KeySelector` properties. |","funding_links":[],"categories":["Content","Source Generators"],"sub_categories":["152. [ServiceScan.SourceGenerator](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ServiceScan.SourceGenerator) , in the [DependencyInjection](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#dependencyinjection) category","Dependency Injection (IoC Container)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDreamescaper%2FServiceScan.SourceGenerator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDreamescaper%2FServiceScan.SourceGenerator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDreamescaper%2FServiceScan.SourceGenerator/lists"}