How to call another project's method where DI is implemented - c#

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.

Related

Class library has no Program.cs - where to configure DI?

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

How do I use dll of .net 5 class library that contain in its class Dependency injection?

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

Late registration of services with asp.net core web api

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.

How can I keep my (.NET Core) Dependency Injection configuration maintainable?

I am using Microsoft.Extensions.DependencyInjection for Dependency Injection in .NET Core Console Application.
public class Program
{
public IConfiguration Configuration { get; }
public static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
Startup startup = new Startup();
startup.ConfigureServices(services);
IServiceProvider serviceProvider = services.BuildServiceProvider();
var etlService = serviceProvider.GetService<IETLService>();
}
}
public class Startup
{
IConfigurationRoot Configuration { get; }
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
var sqlServerConnectionString = Configuration.GetConnectionString("SqlServerConnection");
services.AddDbContext<ETLSqlContext>(options =>
options.UseSqlServer(sqlServerConnectionString), ServiceLifetime.Scoped);
services.AddSingleton(Configuration);
services.AddTransient<ISqlRepository, SqlRepository>();
services.AddTransient<IAzureSqlRepository, AzureSqlRepository>();
services.AddTransient<IExtractService, ExtractService>();
services.AddTransient<ILoadService, LoadService>();
}
}
public class ExtractService : IExtractService
{
public ISqlRepository SqlRepository { get; set; }
public IAzureSqlRepository AzureSqlRepository { get; set; }
public ExtractService(ISqlRepository sqlRepository, IAzureSqlRepository azureSqlRepository)
{
SqlRepository = sqlRepository;
AzureSqlRepository = azureSqlRepository;
}
}
As solution grows there will be more services for example 50+ and each service will require registering its Interface and Implementation class in Startup.cs for Dependency Injection. I need to know is there any better way to implement Dependency Injection which does not require manually adding new service Interface and Implementation class in registration code
In chapter 12 of Dependency Injection, Principles, Practices, and Patterns, Mark Seemann and I describe that there are multiple configuration options when working with a DI Container, namely:
Configuration files–Mapping are specified in configuration files (typically in XML or JSON format)
Configuration as Code–Code explicitly determines mappings
Auto-Registration–Rules are used to locate suitable components using reflection and to build the mappings.
You are currently applying Configuration as Code. With Auto-Registration, however, you apply Convention over Configuration to register your application components using reflection, based on a specified convention.
In section 12.3 we describe in detail when you should use a DI Container, and how you should use it. In summary we state that:
The use of Convention over Configuration using Auto-Registration can minimize the amount of maintenance on the Composition Root to almost zero.
We, therefore, advice:
a Composition Root should either be focused around Pure DI with, perhaps a few late-bound types, or around Auto-Registration with, optionally, a limited amount of Configuration as Code and configuration files. A Composition Root that focuses around Configuration as Code is pointless and should therefore be avoided.
In chapters 6 and 10 of the book, we also describe the kinds of designs you can use that, among other things, maximize convention over configuration and, with it, minimizes the amount of maintenance on your DI configuration.
I've done this by adding a class to each of my projects "PluginConfiguration", which passes in the IServiceCollection, and registers its individual services in the constructor.. Then just call each "Plugin" in the ServicesCollection in my main project. It abstracts out the config into a class thats relevant to the individual project and keeps the main config clean with just a single line of code for each additional project

DI in .Net Core MVC 1.0

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
}

Categories