Dependency Injection in .NET Core Main() Not Working - c#

I have a library with a class that I instantiate in the Main() method of my .NET Core Web API (netcoreapp2.2) for retrieving the Web API's configuration:
public static void Main(string[] args)
{
var configBuilder = new ConfigBuilder("configuration-v1.json").Build();
WebHost.CreateDefaultBuilder(args)
.UseConfiguration(configuration)
.UseStartup<Startup>()
.Build()
.Run();
}
The constructor ConfigBuilder is:
public AwsConfigurationBuilder(string configKey, IAmazonS3ClientAdapter s3Client)
My ConfigureServices method in Startup.cs is:
public void ConfigureServices(IServiceCollection services)
{
services
.AddScoped<IAmazonS3ClientAdapter, AmazonS3ClientAdapter>()
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
And I am getting the following error concerning ConfigBuilder instantiation:
There is no argument given that corresponds to the required formal parameter 's3Client' of 'ConfigBuilder.ConfigBuilder(string, IAmazonS3ClientAdapter)' [Foo.BarService]csharp(CS7036)
Is it just not possible to do dependency injection in the service's Main method?

You can do this, assuming you injected your service properly, and not sure if this is a good idea or not but...
public static void Main(string[] args)
{
var builder = CreateWebHostBuilder(args);
var host = builder.Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var aws = services.GetService<AwsConfigurationBuilder>();
}

The starting point of any Dependency Injection application is called the Composition Root. The composition root is where we define all the mappings of the application. Loosely put mappings between abstraction and concretion.
Once this mapping is defined the object creation is handed over to the DI framework. DI framework then traverses through these mappings and creates objects for us.
A very good analogy that #Mark Seemann gave in his seminal book on DI is that imagine your code as a little kid. That kid is not allowed to open the fridge. Now if the kid wants to eat something he asks his mom about it. DI framework is the mom who knows what's there in the fridge and how to serve it to the kid.

No, you cannot apply it within the Main method as that is the entrypoint to your application (and its a static method, not a constructor).
You are also instantiating the class by creating a new object (new ConfigBuilder), that's not how you are supposed to use dependency injection.
You are using ASP .NET core. The wiring for your dependencies is done within ConfigureServices(IServiceCollection services) after which you can use the dependencies. For example, by specifying the needed dependencies (interfaces and/or classes) in your Controller constructor.
You can find a more in-depth explanation here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection.

Related

ASP.NET Core : Why do we need IDesignTimeDbContextFactory?

I have an ASP.NET Core application and all I have is the DataContext, I don't have IDesignTimeDbContextFactory implemented.
public class DataContext : DbContext, IUnitOfWork
{...}
With that I can do Add-Migration, Update-Database & Script-Migration.
However, I came across an another project where they have implemented IDesignTimeDbContextFactory, mentioned that this is to generate migration classes.
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<CodingBlastDbContext>
{
public CodingBlastDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<CodingBlastDbContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new CodingBlastDbContext(builder.Options);
}
}
I wonder why this is needed? especially the first project works without implementing the IDesignTimeDbContextFactory..
Docs have some explanation on when you can leverage the design-time factory:
A design-time factory can be especially useful if you need to configure the DbContext differently for design time than at run time, if the DbContext constructor takes additional parameters are not registered in DI, if you are not using DI at all, or if for some reason you prefer not to have a CreateHostBuilder method in your ASP.NET Core application's Main class.
The only use case I personally encountered was when DbContext was moved into a separate library and we did not want to run CreateHostBuilder for context designing purposes (startup involved some relatively heavy stuff and we didn't want to invoke that). Like for example here.

Is it possible to pass the ServiceProvider into a constructor parameter?

Problem:
We have a .NET 5 WPF application that has an EntityFramework Core entities class file DbEntities, which implements the DbContext. We use constructor injection when instantiating it. One of the options that we use is AddInterceptors in order to append an Access Token to the SqlConnection. The interceptor is called AzureAuthenticationInterceptor. When registering the service, we would like to pass in the ServiceProvider so that it is available in the interceptors constructor, which can be used to get a service that implements Access Token in-memory caching.
The reason for it is that we have a project with 50+ classes that all use the same DbEntities file, which takes 0 arguments in the constructor. This was upgraded to .NET 5 where Dependency Injection was avoided due to the work it would take to apply it to all of the forms. So, the DbEntities is instantiated in the forms with new DbEntities();.
But, in this case, we are implementing an access token cache, which needs to be registered as a service. Otherwise, if we just instantiate the cache every time we create a new DbContext, then the cache will be wiped out.
The access token in-memory cache is implemented using this method https://mderriey.com/2020/09/12/resolve-ef-core-interceptors-with-dependency-injection/
We only want to use dependency injection for the in-memory token cache. The only way we think of as a shortcut is to pass the ServiceProvider in the interceptor's constructor, but it does not appear available in the ConfigureServices method.
Question:
Is it possible to pass in the ServiceProvider? If not, is there any other way we can implement dependency injection on the interceptor without having to change 50 class files?
Program.cs
Public static void Main()
{
...
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, builder) =>
{
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
Configuration = context.Configuration;
ConfigureServices(Configuration, services);
})
.Build();
...
}
private static void ConfigureServices(IConfiguration objConfiguration, IServiceCollection objServices)
{
objServices.AddMemoryCache()
.AddSingleton<IAzureSqlTokenProvider, AzureIdentityAzureSqlTokenProvider>()
.Decorate<IAzureSqlTokenProvider, CacheAzureSqlTokenProvider>()
.AddSingleton(new AzureAuthenticationInterceptor(IServiceProvider_NeededHere))
;
}
DbEntities.cs
public DbEntities() :
base(new DbContextOptionsBuilder<DbEntities>()
.UseSqlServer(ConfigurationManager.ConnectionStrings["DbEntities"].ConnectionString)
.AddInterceptors(new AzureAuthenticationInterceptor())
.Options)
{ }
AzureAuthenticationInterceptor.cs
public AzureAuthenticationInterceptor(IServiceProvider objServiceProvider)
{
this.IAzureSqlTokenProvider = (IAzureSqlTokenProvider)objServiceProvider.GetService(typeof(IAzureSqlTokenProvider));
}
First, avoid injecting IServiceProvider, it is a code smell and leads to poor design.
Refactor AzureAuthenticationInterceptor.cs
public AzureAuthenticationInterceptor(IAzureSqlTokenProvider tokenProvider) {
this.IAzureSqlTokenProvider = tokenProvider;
}
So that way explicit dependencies can be injected as needed
//...
.AddSingleton<AzureAuthenticationInterceptor>()
//...
When resolving the interceptor while configuring the DbEntities
//...
services.AddDbContext<DbEntities>((provider, options) => {
options.UseSqlServer(Configuration.GetConnectionString("<connection-string-name>"));
options.AddInterceptors(provider.GetRequiredService<AzureAuthenticationInterceptor>());
});
//...
Note that if you are manually initializing the context using the default constructor, ie:new DbEntities(); Then this bypasses the opportunity to apply dependency injection via constructor injection.

Microsoft Dependency Injection Documentation

In the documentation for dependency injection I notice the following line.
The MVC framework will automatically look at the service provider to
register our dependency in the Controller.
They then provide a basic example with constructor injection, not their example but in essence this.
public class Example
{
private IFooFactory foo;
public Example(IFooFactory foo) => this.foo = foo;
public void SampleUse()
{
using(var context = foo.Create())
context.DoSomething();
}
}
If you have a console application, by default it will not look at the service provider to register your dependency with the concrete implementation. Is there a way to simulate that? Otherwise the console application will require you to do something along these lines:
public static Main(string[] args)
{
// Stuff to prepare the application and build service provider.
var service = serviceProvider.GetService<IFooFactory>();
using(var context = service.Create())
context.DoSomething();
// OR
var fooFactory = serviceProvider.GetService<IFooFactory>();
new Example(fooFactory).SampleUse();
}
Which creates the problem of having to pass IFooFactory or pulling things into the main that you may wanted separated for structure. How can I make the console application look at the provider when a new class is created with a defined interface?
You have to create everything manually as the framework is not there to automagically do it for you.
var services = new ServiceCollection();
services.AddTransient<IFooFactory, FooFactory>();
services.AddTransient<Example>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
Example example = serviceProvider.GetService<Example>();
example.SampleUse();
While not ideal, it is usually the way shown in most examples where DI is configured manually.
When you inspect the framework DI integration, behind the scenes it does the exact same thing during startup.
You could probably write your own code to inspect available types, but that is a very broad task to tackle on your own.
Reference Dependency injection in ASP.NET Core
Default service container replacement
The built-in service container is meant to serve the needs of the
framework and most consumer apps. We recommend using the built-in
container unless you need a specific feature that it doesn't support.
Some of the features supported in 3rd party containers not found in
the built-in container:
Property injection
Injection based on name
Child containers
Custom lifetime management
Func<T> support for lazy initialization

.Net Core Dependency Injection in Console App

I am trying to use dependency injection in a .NET Core Console App.
There are a lot of articles about it, but did not find one that fixes my problem.
I am trying to use services original from an aspnet core web app, thats why i have the WebHost.
My main problem is to create an instance of my own class, all the dependency seems to work, and my console app starts up.
I have this code in my Program class:
static void Main(string[] args)
{
var host = WebHost.CreateDefaultBuilder(args)
.UseKestrel(options => options.AddServerHeader = false)
.UseStartup<Startup>()
.Build();
var services = new ServiceCollection().AddLogging();
var container = new Container();
var serviceProvider = container.GetInstance<IServiceProvider>();
This code will not compile due to this error:
'Container' does not contain a definition for 'GetInstance'
How can i create an instance of my custom class App which has this implementation:
public class App
{
private readonly IProductService _productService;
public App(IProductService productService)
{
_productService = productService;
}
}
You don't even need to create your own ServiceCollection or ServiceProvider in this scenario - You can just use the IWebHost's Services property that you already have:
var app = host.Services.GetService<App>();
WebHost.CreateDefaultBuilder already adds the logging services, so there's no need to do that either.
Note: I'm assuming that you've registered your App and IProductService types in Startup.ConfigureServices.
I have no idea what a "Container" is in your setting, but you normally create a service provider by calling BuildServiceProvider on the ServiceCollection.
var provider = services.BuildServiceProvider();
var instance = provider.GetService<App>();
You will need to register both App and whatever IProductService you want with the services collection first though.

How to pass dependencies to a custom .NET Core ILoggerProvider

I am creating a custom .NET Core ILoggerProvider that requires some dependencies to be passed into its constructor.
I believe I am using a fairly common pattern to initialize my logging implementation; it looks something like this:
var services = new ServiceCollection();
// Register some services here
services.AddLogging(builder =>
{
builder.AddProvider(new DebugLoggerProvider());
});
var provider = services.BuildServiceProvider();
I want to add my new provider within the AddLogging block, in the same way that the DebugLoggerProvider is currently added.
My custom provider requires some other services to be passed into its constructor and since these are already registered with the ServiceCollection, I assume that I should be able to reference them. However, unlike methods such as AddSingleton, which have an overload that exposes the IServiceProvider, AddLogging doesn't seem to offer an equivalent.
Is there a simple way to achieve this, or am I attempting to do something that contradicts the way .NET Core logging was designed to be deployed?
UPDATE:
After experimenting with the suggestions proposed by #Nkosi, I can confirm that it is possible to get this to work by bypassing AddLogging and directly implementing what it does internally, as follows:
var services = new ServiceCollection();
// Register some services
services.AddSingleton<IMyService, MyService>();
// Initialize logging
services.AddOptions();
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddSingleton<ILoggerProvider>(p => new DebugLoggerProvider());
services.AddSingleton<ILoggerProvider>(p => new MyLoggerProvider("Constant value", p.GetService<IMyService>()));
var provider = services.BuildServiceProvider();
Now I am not sure if an extension already exists to do this but I see potential here.
First this is how AddProvider is defined in the source code repo.
public static ILoggingBuilder AddProvider(this ILoggingBuilder builder, ILoggerProvider provider) {
builder.Services.AddSingleton(provider);
return builder;
}
You could build up on that by making your own generic version
public static class MyLoggingBuilderExtensions {
public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder)
where T: class, ILoggerProvider{
builder.Services.AddSingleton<ILoggerProvider, T>();
return builder;
}
}
which should allow the DI container to build up the object graph when resolved
services.AddLogging(builder =>
{
builder.AddProvider<CustomLoggerProvider>();
});
And there is room to extend this functionality, like adding your own overload that exposes the IServiceProvider and passing that on to the AddSingleton within the extension.
public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder, Func<IServiceProvider, T> factory)
where T: class, ILoggerProvider {
builder.Services.AddSingleton<ILoggerProvider, T>(factory);
return builder;
}
And used
services.AddLogging(builder => {
builder.AddProvider<CustomLoggerProvider>(p => new CustomLoggerProvider("Constant value", p.GetService<IMyService>()));
});
Apologies for being a bit late to the party on this one, but I ran into exactly the same problem after having searched high and low. Inspired by the excellent entries in this page, I ended up with the solution below.
services.AddTransient<IMyLogRepository, LogRepository>();
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole()
.AddDbLoggerProvider(services.BuildServiceProvider().GetService<IMyLogRepository>());
});
services.AddSingleton(loggerFactory.CreateLogger("MyLogging"));
The key to this being:
services.BuildServiceProvider().GetService<IMyLogRepository>())
Which allowed me to link my database repository to the dbLogger object I created in a single extra line. In essence, it gives me the ability to pluck my DI database object an send it to the Logging service via standard ILoggerProvider and ILogger interfaces
I got a simple solution to work which is kinda lighter.
serviceCollection.AddLogging(logBuilder =>
{
logBuilder.AddConfiguration(theConfigRoot.GetSection("Logging"));
});
serviceCollection.AddSingleton<ILoggerProvider, MyLogProvider>();
However.... Instanciating the Provider keeps you from running in circular dependency problems--> The service you may want to inject soon want´s a logger himself^^

Categories

Resources