1- ![ Icon] ( https://raw.githubusercontent.com/devlooped/DependencyInjection.Attributed /main/assets/img/icon-32.png ) .NET DependencyInjection via [ Service] Attribute
1+ ![ Icon] ( https://raw.githubusercontent.com/devlooped/DependencyInjection/main/assets/img/icon-32.png ) .NET DependencyInjection via [ Service] Attribute
22============
33
4- [ ![ Version] ( https://img.shields.io/nuget/vpre/Devlooped.Extensions.DependencyInjection.Attributed. svg )] ( https://www.nuget.org/packages/Devlooped.Extensions.DependencyInjection.Attributed )
5- [ ![ Downloads] ( https://img.shields.io/nuget/dt/Devlooped.Extensions.DependencyInjection.Attributed. svg )] ( https://www.nuget.org/packages/Devlooped.Extensions.DependencyInjection.Attributed )
6- [ ![ License] ( https://img.shields.io/github/license/devlooped/DependencyInjection.Attributed. svg?color=blue )] ( https://github.com//devlooped/DependencyInjection.Attributed /blob/main/license.txt )
7- [ ![ Build] ( https://github.com/devlooped/DependencyInjection.Attributed/ workflows/build/badge.svg?branch=main )] ( https://github.com/devlooped/DependencyInjection.Attributed /actions )
4+ [ ![ Version] ( https://img.shields.io/nuget/vpre/Devlooped.Extensions.DependencyInjection.svg )] ( https://www.nuget.org/packages/Devlooped.Extensions.DependencyInjection )
5+ [ ![ Downloads] ( https://img.shields.io/nuget/dt/Devlooped.Extensions.DependencyInjection.svg )] ( https://www.nuget.org/packages/Devlooped.Extensions.DependencyInjection )
6+ [ ![ License] ( https://img.shields.io/github/license/devlooped/DependencyInjection.svg?color=blue )] ( https://github.com//devlooped/DependencyInjection/blob/main/license.txt )
7+ [ ![ Build] ( https://github.com/devlooped/DependencyInjection/actions/ workflows/build.yml /badge.svg )] ( https://github.com/devlooped/DependencyInjection/actions/workflows/build.yml )
88
99<!-- include https://github.com/devlooped/.github/raw/main/sponsorlinkr.md -->
1010
@@ -15,8 +15,15 @@ from conventions or attributes.
1515
1616## Usage
1717
18- After [ installing the nuget package] ( https://www.nuget.org/packages/Devlooped.Extensions.DependencyInjection.Attributed ) ,
19- a new ` [Service(ServiceLifetime)] ` attribute will be available to annotate your types:
18+ The package supports two complementary ways to register services in the DI container, both of which are source-generated at compile-time
19+ and therefore have no run-time dependencies or reflection overhead:
20+
21+ - ** Attribute-based** : annotate your services with ` [Service] ` or ` [Service<TKey>] ` attributes to register them in the DI container.
22+ - ** Convention-based** : register services by type or name using a convention-based approach.
23+
24+ ### Attribute-based
25+
26+ The ` [Service(ServiceLifetime)] ` attribute is available to explicitly annotate types for registration:
2027
2128``` csharp
2229[Service (ServiceLifetime .Scoped )]
@@ -66,6 +73,8 @@ And that's it. The source generator will discover annotated types in the current
6673project and all its references too. Since the registration code is generated at
6774compile-time, there is no run-time reflection (or dependencies) whatsoever.
6875
76+ ### Convention-based
77+
6978You can also avoid attributes entirely by using a convention-based approach, which
7079is nevertheless still compile-time checked and source-generated. This allows
7180registering services for which you don't even have the source code to annotate:
@@ -77,6 +86,9 @@ builder.Services.AddServices(typeof(IRepository), ServiceLifetime.Scoped);
7786// ...
7887```
7988
89+ This will register all types in the current project and its references that are
90+ assignable to ` IRepository ` , with the specified lifetime.
91+
8092You can also use a regular expression to match services by name instead:
8193
8294``` csharp
@@ -86,30 +98,30 @@ builder.Services.AddServices(".*Service$"); // defaults to ServiceLifetime.Sing
8698// ...
8799```
88100
89- Or a combination of both, as needed. In all cases, NO run-time reflection is
90- ever performed, and the compile-time source generator will evaluate the types
91- that are assignable to the given type or matching full type names and emit
92- the typed registrations as needed.
101+ You can use a combination of both, as needed. In all cases, NO run-time reflection is
102+ ever performed, and the compile-time source generator will evaluate the types that are
103+ assignable to the given type or matching full type names and emit the typed registrations
104+ as needed.
93105
94106### Keyed Services
95107
96108[ Keyed services] ( https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#keyed-services )
97- are also supported by a separate generic ` [Service] ` attribute, like :
109+ are also supported by providing a key with the ` [Service] ` attribute. For example :
98110
99111``` csharp
100112public interface INotificationService
101113{
102114 string Notify (string message );
103115}
104116
105- [Service < string > (" sms" )]
117+ [Service (" sms" )]
106118public class SmsNotificationService : INotificationService
107119{
108120 public string Notify (string message ) => $" [SMS] {message }" ;
109121}
110122
111- [Service < string > (" email" )]
112- [Service < string > (" default" )]
123+ [Service (" email" )]
124+ [Service (" default" )]
113125public class EmailNotificationService : INotificationService
114126{
115127 public string Notify (string message ) => $" [Email] {message }" ;
@@ -137,7 +149,7 @@ Note you can also register the same service using multiple keys, as shown in the
137149
138150## How It Works
139151
140- The generated code that implements the registration looks like the following:
152+ In all cases, the generated code that implements the registration looks like the following:
141153
142154``` csharp
143155static partial class AddServicesExtension
@@ -153,7 +165,7 @@ static partial class AddServicesExtension
153165```
154166
155167Note how the service is registered as scoped with its own type first , and the
156- other two registrations just retrieve the same (according to its defined
168+ other two registrations just retrieve the same service (according to its defined
157169lifetime ). This means the instance is reused and properly registered under
158170all implemented interfaces automatically .
159171
@@ -167,6 +179,8 @@ provider by the implementation factory too, like:
167179services .AddScoped (s => new MyService (s .GetRequiredService <IMyDependency >(), ...));
168180```
169181
182+ Keyed services will emit AddKeyedXXX methods instead.
183+
170184## MEF Compatibility
171185
172186Given the (more or less broad ?) adoption of
@@ -233,16 +247,7 @@ package from your library projects, you can just declare it like so:
233247public class ServiceAttribute : Attribute
234248{
235249 public ServiceAttribute (ServiceLifetime lifetime = ServiceLifetime .Singleton ) { }
236- }
237- ```
238-
239- Likewise for the keyed service version :
240-
241- ```csharp
242- [AttributeUsage (AttributeTargets .Class )]
243- public class ServiceAttribute <TKey > : Attribute
244- {
245- public ServiceAttribute (TKey key , ServiceLifetime lifetime = ServiceLifetime .Singleton ) { }
250+ public ServiceAttribute (object key , ServiceLifetime lifetime = ServiceLifetime .Singleton ) { }
246251}
247252```
248253
@@ -256,14 +261,23 @@ that is adding the services to the collection!
256261The attribute is matched by simple name , so it can exist in any namespace .
257262
258263If you want to avoid adding the attribute to the project referencing this package ,
259- set the `$(AddServiceAttribute )` to `true ` via MSBuild :
264+ set the `$(AddServiceAttribute )` to `false ` via MSBuild :
260265
261266```xml
262267<PropertyGroup >
263268 <AddServiceAttribute >false </AddServiceAttribute >
264269</PropertyGroup >
265270```
266271
272+ If you want to avoid generating the `AddServices ` extension method to the project referencing
273+ this package , set the `$(AddServicesExtension )` to `false ` via MSBuild :
274+
275+ ```xml
276+ <PropertyGroup >
277+ <AddServicesExtension >false </AddServicesExtension >
278+ </PropertyGroup >
279+ ```
280+
267281### Choose Constructor
268282
269283If you want to choose a specific constructor to be used for the service implementation
@@ -292,7 +306,7 @@ respectively.
292306# Dogfooding
293307
294308[](https://pkg.kzu.app/index.json)
295- [](https://github.com/devlooped/DependencyInjection/actions)
309+ [](https://github.com/devlooped/DependencyInjection/actions/workflows/build.yml )
296310
297311We also produce CI packages from branches and pull requests so you can dogfood builds as quickly as they are produced .
298312
0 commit comments