I have written a simple version of AutoFac's RegisterAssemblyTypes
method that works directly with Microsoft's DI provider, i.e this library to scan an assemby (or assemblies) on your application and register all the public normal classes (i.e not generic classes) that have an interface into the Microsoft NET's Dependency injection provider (DI for short).
The NetCore.AutoRegisterDi is available on NuGet as NetCore.AutoRegisterDi and is an open-source library under the MIT license. The documenation is found in the README file and the ReleaseNotes contains the details of each release. There is also an actile called ASP.NET Core – fast and automatic dependency injection setup which give you more information on this library.
There are two reasons:
- I really hate having to hand-code each registering of my services - this extension method scans assembles and finds/registers classes with interfaces for you.
- I used to use AutoFac's assembly scanning
feature, but I then saw a tweet by @davidfowl about Dependency Injection container benchmark which showed the Microsoft's DI provider was much faster than AutoFac. I therefore implemented a similar (but not exactly the same) feature for the
Microsoft.Extensions.DependencyInjection
library.
NOTE: There is an article about this library which gives you an overview of this library. Useful if you haven't used this library before.
This example scans the assembly where you call the AutoRegisterDi's RegisterAssemblyPublicNonGenericClasses
method for all the classes which has one or more public interfaces and the Class's name ends with "Service" are registered with .NET's
public void ConfigureServices(IServiceCollection services)
{
//... other configure code removed
service.RegisterAssemblyPublicNonGenericClasses()
.Where(c => c.Name.EndsWith("Service"))
.AsPublicImplementedInterfaces();
This example scans the three assemblies and registers all the classes that have one or more public interfaces. That's because I have commented out the .Where(c => c.Name.EndsWith("Service"))
method.
public void ConfigureServices(IServiceCollection services)
{
//... other configure code removed
var assembliesToScan = new []
{
Assembly.GetExecutingAssembly(),
Assembly.GetAssembly(typeof(MyServiceInAssembly1)),
Assembly.GetAssembly(typeof(MyServiceInAssembly2))
};
service.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
//commenting the line below means it will scan all public classes
//.Where(c => c.Name.EndsWith("Service"))
.AsPublicImplementedInterfaces();
There are four parts:
RegisterAssemblyPublicNonGenericClasses
, which finds all the public classes that:- Aren't abstract
- Aren't a generic type, e.g. MyClass<AnotherClass>
- Isn't nested. e.g. It won't look at classes defined inside other classes
- An optional
Where
method, which allows you to filter the classes to be considered. - The
AsPublicImplementedInterfaces
method which finds ant interfaces on a class and registers those interfaces as pointing to the class. - There are two methods to allow you to say that a specific interface should be ignored:
- The
IgnoreThisInterface<TInterface>()
method to add an interface to the the ignored list. - The
IgnoreThisGenericInterface(Type interfaceType)
method to add an generic interface to the the ignored list.
- The
- Various attributes that you can add to your classes to tell
NetCore.AutoRegisterDi
what to do:- Set the
ServiceLifetime
of your class, e.g.[RegisterAsSingleton]
to apply aSingleton
lifetime to your class. - A
[DoNotAutoRegister]
attribute to stop library your class from being registered with the DI.
- Set the
The RegisterAssemblyPublicNonGenericClasses
method will find all the classes in
- If no assemblies are provided then it scans the assembly that called this method.
- You can provide one or more assemblies to be scanned. The easiest way to reference an assembly is to use something like this
Assembly.GetAssembly(typeof(MyService))
, which gets the assembly thatMyService
was defined in.
I only consider classes which match ALL of the criteria below:
- Public access
- Not nested, e.g. It won't look at classes defined inside other classes
- Not Generic, e.g. MyClass<T>
- Not Abstract
Pretty straightforward - you are provided with the Type
of each class and you can filter by any of the Type
properties etc. This allows you to do things like only registering certain classes, e.g Where(c => c.Name.EndsWith("Service"))
.
NOTE: Useful also if you want to register some classes with a different time scope - See next section.
The AsPublicImplementedInterfaces
method finds any public, non-nested interfaces (apart from IDisposable
and ISerializable
) that each class implements and registers each interface, known as service type, against the class, known as the implementation type. This means if you use an interface in a constructor (or other DI-enabled places) then the Microsoft DI resolver will provide an instance of the class that interface was linked to. See Microsoft DI Docs for more on this.
By default it will register the classes as having a lifetime of ServiceLifetime.Transient
, but the AsPublicImplementedInterfaces
method has an optional parameter called lifetime
which you can change to another lifetime. Note that all the classes will have the same lifetime, but you can use AutoRegisterDi's attributes to set different lifetime to a class (see section 5).
See this useful article on what lifetime (and other terms) means.
Some classes have interfaces that we don't really want to be registered to the DI provider - for instance IDisposable
and ISerializable
. Therefore AutoRegisterDi has two methods which allow you to define a interface that shouldn't be registered on any classes. They are:
This method allows you to add a interface to to a list of interfaces that you don't register to the DI provider. The example below adds the IMyInterface
in the list of interfaces to not register.
service.RegisterAssemblyPublicNonGenericClasses()
.IgnoreThisInterface<IMyInterface>()
.AsPublicImplementedInterfaces();
NOTES
- The list of interfaces to ignore has already got the
IDisposable
andISerializable
interfaces. - You can ignore many interfaces by including have many
IgnoreThisInterface<TInterface>
calls in the setup.
This method was asked for Lajos Marton (GitHub @martonx). He was using record
s and found that each record
has a IEquatable<RecordType>
and he didn't wanted the DI provider be up with interfaces that aren't used.
You could use the IgnoreThisInterface<IEquatable<RecordType>>
, but you would need to do that for every record. The other solution is to say that ALL IEquatable<T>
are ignored. The code below will do that.
service.RegisterAssemblyPublicNonGenericClasses()
.IgnoreThisGenericInterface(typeof(IEquatable<>))
.AsPublicImplementedInterfaces();
NOTES:
- I haven't
IEquatable<>
to the ignore interface list as there may be a valid use to register aIEquatable<SomeClass>
sometimes. In that case you would need to useIgnoreThisGenericInterface
to define all yourrecord
s separably. - The method works for any generic interface type as long that the generic interface has no arguments are filled in, e.g
IDictionary<,>
is fine, butIDictionary<,string>
won't work (you get an exception).
Fedor Zhekov, (GitHub @ZFi88) added attributes to allow you to define the ServiceLifetime
of your class, and also exclude your class from being registered with the DI.
Here are the attributes that sets the ServiceLifetime
to be used when NetCore.AutoRegisterDi
registers your class with the DI.
[RegisterAsSingleton]
- Singleton lifetime.[RegisterAsTransient]
- Transient lifetime.[RegisterAsScoped]
- Scoped lifetime.
The last attribute is [DoNotAutoRegister]
, which stops NetCore.AutoRegisterDi
registered that class with the DI.