{"id":37044755,"url":"https://github.com/tretyakov-d/service-annotations","last_synced_at":"2026-01-14T05:10:39.417Z","repository":{"id":201818585,"uuid":"325784065","full_name":"tretyakov-d/service-annotations","owner":"tretyakov-d","description":"Attributes for automatic registration in ServiceCollection","archived":false,"fork":false,"pushed_at":"2021-01-28T19:05:37.000Z","size":24,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-11-12T18:21:23.757Z","etag":null,"topics":["c-sharp","ioc-container","ioc-framework","netcore","netstandard20"],"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/tretyakov-d.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-12-31T11:17:56.000Z","updated_at":"2021-11-11T11:31:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"140bee42-84e7-407f-867c-e0ffa9c23348","html_url":"https://github.com/tretyakov-d/service-annotations","commit_stats":null,"previous_names":["tretyakov-d/service-annotations"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/tretyakov-d/service-annotations","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tretyakov-d%2Fservice-annotations","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tretyakov-d%2Fservice-annotations/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tretyakov-d%2Fservice-annotations/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tretyakov-d%2Fservice-annotations/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tretyakov-d","download_url":"https://codeload.github.com/tretyakov-d/service-annotations/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tretyakov-d%2Fservice-annotations/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28410264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["c-sharp","ioc-container","ioc-framework","netcore","netstandard20"],"created_at":"2026-01-14T05:10:38.677Z","updated_at":"2026-01-14T05:10:39.410Z","avatar_url":"https://github.com/tretyakov-d.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# Service annotations\n\nAttributes for automatic registration in ServiceCollection\n\n##### dotnet add package [ServiceAnnotations](https://www.nuget.org/packages/ServiceAnnotations/)\n\n\n## Usage examples\n\n### ServiceAttribute\n\nThe attribute instructs assembly scanner to register class it is applied to \nas implementation type for given types or itself if not types specified.\n\nThe attribute can be applied on classes with any access modifier (public/internal/private/protected).\n\n```c#\n[Service(ServiceLifetime.Transient)]\nclass MyService\n{\n    // the class will be registered with Transient lifetime as MyService\n\n    public MyService(IMyServiceDependency repository) { }\n}\n\n[Service(ServiceLifetime.Scoped, typeof(IMyServiceDependency))]\nclass MyServiceDependency : IMyServiceDependency\n{ \n    // the class will be registered with Scoped lifetime as IMyServiceDependency\n}\n\npublic class StartUp\n{\n    public static void ConfigureServices(IServiceCollection serviceCollection)\n    {\n        // to add all services from calling assembly (the assembly where this code is written, typically the aspent app) \n        serviceCollection.AddAnnotatedServices();\n        \n        // too add all services from specific assembly\n        serviceCollection.AddAnnotatedServices(typeof(AnyTypeFromTargetAssemly).Assembly);\n    }\n}\n```\n\n### ServiceAttribute multiple types\n\n```c#\n[Service(ServiceLifetime.Scoped, typeof(ServiceWithManyHats), typeof(IRedHat), typeof(IGreenHat)]\nclass ServiceWithManyHats : IRedHat, IBlueHat, IGreenHat {\n}\n```\nthe type above will get registered in a following manner\n```c#\n// primary registration -\u003e the service will get registered as Type and as Implementation\nserviceCollection.AddScoped\u003cServiceWithManyHats, ServiceWithManyHats\u003e();\n\n// secondary registrations -\u003e all other types will get registered as resolves for primary registration\nserviceCollection.AddScoped\u003cIRedHat\u003e(serviceProvider =\u003e (IRedHat)serviceProvider.Resolve(typeof(ServiceWithManyHats)); \nserviceCollection.AddScoped\u003cIGreenHat\u003e(serviceProvider =\u003e (IGreenHat)serviceProvider.Resolve(typeof(ServiceWithManyHats));\n\n// The IBlueHat wasn't mentioned in attribute, therefore won't be registered\n```\n\u003e with Singleton or Scoped lifetime (withing single lifetime scope) \n\u003e resolving service by all three keys will result the same instance\n\n#### How primary type is selected\n\nIf type of a class is in the list of types it always becomes primary,\notherwise the first in list.\n\nIn the example above is first in a list of types `typeof(ServiceWithManyHats)`, making it last in a list would change nothing. \n\nHowever removing `typeof(ServiceWithManyHats)` from the list would change the registration manner to following:\n\n```c#\n// primary registration -\u003e the service will get registered as Type and as Implementation\nserviceCollection.AddScoped\u003cIRedHat, ServiceWithManyHats\u003e();\n\n// secondary registration -\u003e all other types will get registered as resolves for primary registration\nserviceCollection.AddScoped\u003cIGreenHat\u003e(serviceProvider =\u003e (IGreenHat)serviceProvider.Resolve(typeof(IRedHat)));\n```\n\n### ConfigureServicesAttribute\n\nConfigure services attribute instructs assembly scanner to invoke static method \nConfigureServices (or custom name if given in attribute) of a class it's applied on.\n\nThe attribute can point to method with any access modifier (public/private/internal), but method must be static.\nIt doesn't matter if class is static or not.\n\n*very basic example*\n```c#\n[ConfigureServices]\nstatic class MyModule {\n    static ConfigureServices(IServiceCollection serviceCollection) {\n        serviceCollection.AddTransient\u003cMyService\u003e();\n        serviceCollection.AddTransient\u003cMyOtherService\u003e();\n    }\n}\n```\n\n### ConfigureServicesAttribute with additional parameters on ConfigureServices methods\n\n*practical example with HttpClient*\n```c#\n[Service(ServiceLifetime.Transient), ConfigureServices(nameof(RegisterHttpClient))]\nclass MyService {\n    \n    public MyService(HttpClient myHttpClient) { }\n    \n    static void RegisterHttpClient(IServiceCollection serviceColleciton, IConfiguration configuration) {\n        serviceCollection.AddHttpClient\u003cMyService\u003e(httpClient =\u003e {\n            httpClient.BaseUrl = new Uri(configuration.GetConnectionString(\"myServiceEndpoint\"));\n        });\n    }\n    \n}\n\n// please note\n// an example passing IConfiguration or any other service to assembly scanner in classic aspnet core app\n\npublic class Startup\n{\n    readonly IConfiguration _configuration;\n    readonly IWebHostingEnvironment _environment;\n    \n    public Startup(IConfiguration configuration, IWebHostingEnvironment _environment)\n    {\n        _configuration = configuration;\n    }\n\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // Pass the objects that will be avialble as parameters in ConfigureServices method(s) invoked by ConfigureServicesAttribute \n        services.AddAnnotatedServices(context =\u003e context\n            .Add\u003cIConfiguration\u003e(_configuration)\n            .Add\u003cIWebHostingEnvironment\u003e(_environment));\n    }\n\n    public void Configure(IApplicationBuilder app)\n    {\n        // ...\n    }\n}\n```\n\n\n## Errors\n\n### SA2000\n\n#### Configure services method not found by name\n\n**Case #1**: ConfigureServicesAttribute by default looks by method named ConfigureServices, but method is not present in class\n```c#\n[ConfigureServices]\nclass MyClass {\n    static void RegisterServices(IServiceCollection serviceCollection) {}\n}\n```\n**Case #1 Solution #1**: Make sure the method is called ConfigureServices\n```c#\n[ConfigureServices]\nclass MyClass {\n    static void ConfigureServices(IServiceCollection serviceCollection) {}\n}\n```\n**Case #1 Solution #2**: Specify method name in ConfigureServicesAttribute\n```c#\n[ConfigureServices(nameof(RegisterServices))]\nclass MyClass {\n    static void RegisterServices(IServiceCollection serviceCollection) {}\n}\n```\n\n**Case #2**: ConfigureServicesAttribute looks for static method RegisterServices, but the method defined in class is not static.\n```c#\n[ConfigureServices(nameof(RegisterServices))]\nclass MyClass {\n    void RegisterServices(IServiceCollection serviceCollection) {}\n}\n```\n\n**Case #2 Solution**: Ensure method is static\n```c#\n[ConfigureServices(nameof(RegisterServices))]\nclass MyClass {\n    void RegisterServices(IServiceCollection serviceCollection) {}\n}\n```\n\n### SA2001\n\n#### Ambiguous configure services method name\n\n**Case:** ConfigureServiceAttribute looks for static \"ConfigureServices\" (or custom name if specified) method.\nThere are two methods matching description. ConfigureServiceAttribute cannot decide which one to use.\n\n```c#\n[ConfigureServices]\nclass MyClass {\n    static ConfigureServices(IServiceCollection serviceCollection) {}\n    static ConfigureServices(IServiceCollection serviceCollection, IConfiguration configuration) {}\n}\n```\n\n\u003e non-static methods doesn't affect resolution, in other words the class may have endless count of non-static methods matching the same name it wont cause a problem\n\n**Solution**: Ensure the method name referred by ConfigureServicesAttribute is unique.\n\n```c#\n[ConfigureServices]\npublic class MyClass {\n    public static RegisterServices(IServiceCollection serviceCollection) {}\n    public static ConfigureServices(IServiceCollection serviceCollection, IConfiguration configuration) {}\n}\n```\n\n### SA2002\n#### The instance of \u0026lt;Type\u0026gt; was not provided.\n\n**Case**: ConfigureServicesAttribute looks for static \"ConfigureServices\" (or custom name method if specified)\nthe method is present, the method is static, but has more parameters than just IServiceCollection.\n\n```c#\n[ConfigureServices]\npublic class MyClass {\n    public static ConfigureServices(IServiceCollection serviceCollection, IConfiguration configuration) {}\n}\n```\n\n**Solution**: Pass instances of objects required to register services when invoking AddAnnotatedServices method,\nlike on example below\n```c#\npublic class Startup\n{\n    readonly IConfiguration _configuration;\n    \n    public Startup(IConfiguration configuration)\n    {\n        _configuration = configuration;\n    }\n\n    public void ConfigureServices(IServiceCollection services)\n    {\n        services.AddMvc();\n        services.AddAnnotatedServices(context =\u003e context.Add\u003cIConfiguration\u003e(_configuration));\n    }\n\n    public void Configure(IApplicationBuilder app)\n    {\n        // ...\n    }\n}\n```\n\n## License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftretyakov-d%2Fservice-annotations","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftretyakov-d%2Fservice-annotations","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftretyakov-d%2Fservice-annotations/lists"}