I see in many MVC examples how to register your interfaces in the ConfigurationServices method of the Startup Class. This is fine when you have your code all written within the MVC app, but in the 'Real World' this wouldn't necessarily be the case.
I have a class library project in the form
public class MyService : IMyService
{
private readonly IMyRepository _myRepository;
public MyService(IMyRepository myRepository)
{
_myRepository = myRepository;
}
.......
Now in my controller I have a constructor of the form:
public HomeController(IConfigurationRoot config, IMyServcie myService)
{
......
The problem is, the MyService interfaces have not been registered with the DI Container and I don't really want to bloat the ConfigurationServices method with loads of services.AddScoped<interface,class>() lines of code for my other layers.
What do I need to do in my other layers (Repository and Service) to first register them here (both are .NET Core class library projects) and then wire those containers into the parent container?
ConfigurationServices is your composition root so that's where you register you services. The bloat has to go somewhere. You can create an extension method in your other layers and targets IServiceCollection and then populate as needed. They technically are not first registered there. they are registered in the composition root when you apply the extension method against IServiceColection
Your other layers would have to reference Microsoft.Extensions.DependencyInjection.Abstractions in order to have access to the IServiceCollection interface.
IMO I don't think these extension methods need to be in your service or repository layers. Those layers don't need to know anything about how they are composed. You can just as easily put them in your composition root in another class and call them just as shown above if the end goal is to make the startup class cleaner. Or put in a separate extension project which is specifically for targeting .net core's DI framework.
Service Extension Layer
public static IServiceCollection AddMyServices(this IServiceCollection services) {
services.AddScoped<IMyService, MyService>();
//...add other services
}
Repository Extension Layer
public static IServiceCollection AddMyRepositories(this IServiceCollection services) {
services.AddScoped<IMyRepository, MyRepository >();
//...add other services
}
And then in your composition root ConfigureServices
public void ConfigureServices(IServiceCollection services) {
//...other code
services
.AddMyServices()
.AddMyRepositories();
//...other code
}
UPDATE
based on comment you could just as easily call the services.AddMyRepositories() in the AddMyServies extension method as opposed to the main project itself
public static IServiceCollection AddMyServices(this IServiceCollection services) {
services.AddMyRepositories();
services.AddScoped<IMyService, MyService>();
//...add other services
}
And then in your composition root, ConfigureServices will only need to call the AddMyServices
public void ConfigureServices(IServiceCollection services) {
//...other code
services.AddMyServices();
//...other code
}
Related
I started a new class library project and I want to use Dependency Injection for services and DbContext, etc.
However there is no Program.cs file.
Where should I configure the DI interfaces and classes? Do I need to add an empty Program.cs?
for example, if you have a class library as a business access layer. you can add a class in the root with the name DependencyInjection as the following
public static class DependencyInjection
{
public static void AddApplication(this IServiceCollection service)
{
service.AddScoped<IOrgSettingsService, OrgSettingsService>();
service.AddScoped<IIdentity, IdentityService>();
service.AddScoped<ILdapAuthenticationService, LdapAuthenticationService>();
service.AddScoped<IOrgAuthenticationService, OrgAuthenticationService>();
service.AddScoped<IVacanciesService, VacancyService>();
// assemblers
service.AddScoped<IVacancyAssembler, VacancyAssembler>();
}
}
and register it in startup class as the following
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddApplication();
}
dependency injection used for whole application.Main application injects services to all assemblies. for example, dbcontext inject from main application to your assembly.You should not define dependency instance for each assembly separately and locally
In my Asp.net Core 5 API Project
I have a serviceLayer that the controller uses, to get data from a third layer called dataLayer.
I want to use the service layer as a DLL in different projects.
This ServiceLayer Contain dependency Injections like that :
namespace ServiceLayer
{
public class UserService : IUserService
{
IUserRepository userRepository; // (From DataLayer)
public UserService(IUserRepository repository) : base(repository)
{
this.userRepository = repository;
}
public Users GetAllPersonsById(int id)
{
return userRepository.GetById(id);
}
}
public interface IUserService : IService<Users>
{
Users GetAllPersonsById(int id);
}
How can I use the method GetAllPersonsById with the DLL ServiceLayer
can I use it because the dependency Injections
As soon as you reference the DLL / project you can use all classes the same ways as if they were in the project.
To use a class as a service:
Provide the service
Inject the service
There's a lot of documentation available, so I'll keep this short:
// provide in startup.cs
services.AddTransient<IUserService, UserService>();
// Inject where you need it
MyConstructor(IUserService userService) {}
See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0
Provide Extension Method
If we take a look at other libs, most of them provide a method to setup the services.
Example: Entity framework core
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(...));
}
So you could:
In your lib, create an extension method for IServicesCollection that adds all services of your lib.
In the consuming project, call services.AddMyLibServices().
This could look like so:
public static class ServicesConfiguration
{
public static void AddDataLayer(this IServiceCollection services)
{
services.AddTransient<IUserService, UserService>();
// ... same for all services of your lib
}
}
Here's a tutorial with more details:
https://dotnetcoretutorials.com/2017/01/24/servicecollection-extension-pattern/
Lamar service registries
An optional and alternative approach are service registries. It's very similar to the extension methods but uses a class to do the setup. See https://jasperfx.github.io/lamar/documentation/ioc/registration/registry-dsl/
Composition Root
You may want to read about the composition root pattern, e.g. What is a composition root in the context of dependency injection?
In a simple app, your startup.cs is your composition root. In more complex apps, you could create a separate project to have a single place to configure your apps services.
Create the DLL
There are two ways to create the DLL:
As a project in your solution (so your solution has multiple projects, each will result in a separate DLL)
As a separate solution and as nuget package
I have an ASP.NET Core 3.1 project. Typically, I register any dependency using the ConfigureServices() method in the Startup.cs class.
But, I find myself having to register lots of dependencies and the ConfigureServices() looks huge! I know I can probably create an extension method of a static method and call it from the ConfigureService()` class, but wondering if there is a better way.
If there a way to register dependencies in the IoC container without having to define them one at a time like this
services.AddScoped<Interface, Class>();
.... 200 lines later
services.AddScoped<ISettings, Settings>()
Grouping related dependencies into custom extension methods is a very common way to do this. ASP.NET Core already does this for many of the internal services, and you can easily expand on top of that and set them up the way you need for your application. For example to set up authentication and authorization:
public IServiceCollection AddSecurity(this IServiceCollection services)
{
services.AddAuthentication()
.AddCookie();
service.AddAuthorization(options =>
{
options.DefaultPolicy = …;
});
return services;
}
You can do the same for your application-specific services and group them logically in separate extension methods.
If you have a lot of service registrations that are very similar, you can also employ a convention-based registration e.g. using Scrutor. For example, this registers all services within a certain namespace as transient for their respective interface:
services.Scan(scan => scan
.FromAssemblyOf<Startup>()
.AddClasses(c => c.InNamespaces("MyApp.Services"))
.AsImplementedInterfaces()
.WithTransientLifetime()
);
Scrutor allows for very complex rules to scan for services, so if your services do follow some pattern, you will likely be able to come up with a rule for that.
Create a custom attribute (called AutoBindAttribute)
public class AutoBindAttribute : Attribute
{
}
Use it like bellow (Decorate all the implementations that you want to automatically bind with [AutroBind])
public interface IMyClass {}
[AutoBind]
public class MyClass : IMyClass {}
Now create an extention method for IServiceCollection
public static class ServiceCollectionExtensions
{
public static void AutoBind(this IServiceCollection source, params Assembly[] assemblies)
{
source.Scan(scan => scan.FromAssemblies(assemblies)
.AddClasses(classes => classes.WithAttribute<AutoBindAttribute>())
.AsImplementedInterfaces()
.WithTransientLifetime();
}
}
Now call it in Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AutoBind(typeof(Startup).Assembly);
}
}
Note: You can improve the ServiceCollectionExtentions class to support all scopes such as singleton, etc. This example shows only for Transient lifetime.
Enjoy!!!
I think I implemented a poor architecture. Try to follow me on this please.
I have project1 that is a simple scraper. Whenever it receive a response from its scraping process, it want to save the scraped data into the database. It has its own DI container.
I have project2 which is the one responsible for executing DB migrations and I placed also the logic to access and modify the database in here. I wanted to keep all the logic to manage the DB in one place only. It has its own DI container.
Finally, I have project3 which is an API that whenever asked, serves the data stored in the DB. It has its own DI container.
As you can see project2 is in the middle, it gets used both by the scraper project when saving data and the API project for serving the data.
In project2 my DI registration looks like this:
private static IServiceCollection ConfigureServices(IConfiguration Configuration)
{
IServiceCollection services = new ServiceCollection();
// Inject IOptions<T>
services.AddOptions();
var dbSettings = Configuration.GetSection("Settings");
services.Configure<DbSettings>(dbSettings);
return services;
}
What it does is that looks into the appsettings (there is only a connectionString towards the database saved) and prepares IOption to be injected where needed.
So in project2 there is a for example a method to check if the DB is empty.
public class DbClient : IDbClient
{
private readonly IOptions<DbSettings> _dbSettings;
public DbClient(IOptions<DbSettings> dbSettings)
{
_dbSettings = dbSettings;
}
public bool IsDbEmpty()
{
using (SqlConnection connection = new SqlConnection(_dbSettings.Value.ConnectionString))
{
var isEmpty = connection.QueryFirst<string>("SELECT COUNT(1) WHERE EXISTS (SELECT * FROM Test)");
Console.ReadLine();
}
}
}
Guess where the problem is? Whenever I try to call this method from either project1 (the scraper) or project3 (the API) the IOptions is not injected, because I haven't called the container of project2. How do I solve this? Thanks in advance!
One approach is to have your shared project ("project2") expose some functionality for configuration that can be used by consuming applications ("project1" and "project3").
With .NET Core, you can do this by defining IServiceCollection extension methods in your shared project ("project2"):
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddProject2(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
var dbSettings = configuration.GetSection("Settings");
services.Configure<DbSettings>(dbSettings);
return services;
}
}
And then calling this from the startup of the consuming projects ("project1" and "project3"):
public void ConfigureServices(IServiceCollection services)
{
...
services.AddProject2(Configuration);
...
}
Note that you can then define the configuration required for "project2" within any project that is consuming it.
I have a Web API implementation in ASP.NET Core and I'd like to use the included Dependency Injection.
In addition I have late binded assemblies, where I have to load a Type and create an instance of it, which can have dependencies to the main application.
I am trying to load dynamic resources from Assemblies I do not know while startup. So I am using Assembly.Load("name") and look up factory types, that give me the resource reader implementation of the Assembly.
So I know the type I need to create an instance of, but I cannot register it to the IServiceCollection and therefore cannot create instances with the ServiceProvider.
So I tried to register my found types to the IServiceCollection which was provided by the framework. For what I registered the ServiceCollection within itself.
// Within Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IServiceCollection>(services);
}
// A service to register new dependencies later on
public class ServiceRegistrationService : IServiceRegistrationService
{
public IServiceCollection Services { get; }
public IServiceRegistrationService RegisterSelfTransient(Type type)
{
Services.AddTransient(type);
return this;
}
}
After calling this method like:
ServiceRegistrationService.RegisterSelfTransient(typeof(MyConcreteType));
I'd expect the IServiceProvider to resolve a new instance of my type.
Is there a way to register services after leaving the ConfigureServices(IServiceCollection services) method?
After execution of WebApplicationBuilder.Build() in Program.cs's top-level statements, the IServiceCollection turns to read-only one:
public IHost Build()
{
...
// Prevent further modification of the service collection now that the provider is built.
_serviceCollection.MakeReadOnly();
...
}
So it seems no further changes could be introduced after that.