.Net Core Dependency Injection in Console App - c#

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.

Related

Configure a logging provider for a different service collection

This is an ASP.NET application in .NET 6. There's a wizard interface where the user inputs some data and then based on the input, I set up a new dependency injection container with the services that are required to complete the task. My problem is that the ILogger<> instances coming out of this second container don't use the custom ILoggingProvider that I set up.
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddDebug();
builder.Logging.AddSignalRLogging();
public static class ILoggingBuilderExtensions
{
public static ILoggingBuilder AddSignalRLogging(this ILoggingBuilder builder)
=> builder.AddProvider(new SignalRLoggerProvider(builder.Services))
.AddFilter<SignalRLoggerProvider>("MyNamespace", LogLevel.Information);
}
The SignalRLoggerProvider comes from How to implement an ILogger to send messages to a SignalR Hub?.
Controller:
IServiceCollection services = new ServiceCollection();
services.AddLogging();
services.AddSignalR();
services.AddSingleton(_sheetsClient); // this was injected into the controller
services.AddSingleton<ITeamReaderService>(new PostedTeamReaderService(model.Divisions));
string[] divisionNames = model.Divisions.Keys.ToArray();
foreach (string divisionName in divisionNames)
{
services.AddSingleton<IDivisionSheetService>(provider => new DivisionSheetService(divisionName,
provider.GetRequiredService<StandingsRequestCreatorFactory>(),
provider.GetRequiredService<ISheetsClient>(),
provider.GetRequiredService<IOptionsMonitor<ScoreSheetConfiguration>>(),
provider.GetRequiredService<ILogger<DivisionSheetService>>())
);
}
I know my provider works because when I log things in a controller whose dependencies were injected from the HostBuilder's service collection (_sheetsClient), those messages work correctly. In classes that come from this other container (DivisionSheetService), those log messages go nowhere and when I view the ILogger instance in the debugger, it shows that it has no logger that it's writing to.
So it must be the case that my custom logging provider is unknown to the second container, but I can't figure out how to register it there.
Thanks in advance.
Since you're creating a new ServiceCollection from scratch, you also need to add the logging infrastructure from scratch:
IServiceCollection services = new ServiceCollection();
services.AddLogging(builder => builder.AddDebug().AddSignalRLogging());

Dependency Injection in .NET Core Main() Not Working

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.

Accessing Unity-specific functionality in .NET Core dependency injection

I have a .net core 2.1 console app. I'm building the container as below:
public IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Register services here.
// Unity container interface:
var containerFactory = new ServiceProviderFactory(null);
IUnityContainer container = containerFactory.CreateBuilder(services);
IServiceProvider serviceProvider = containerFactory.CreateServiceProvider(container);
return serviceProvider;
}
I need to provide constructor arguments to some of the services registered in the container. However, I do not want to use something like options pattern. Instead, I would like to access the Unity functionality with something like below:
var container = (UnityContainer)serviceProvider;
var foo = container.Resolve<IFoo>(new ParameterOverrides<Foo> { "name": "myFoo" });
The above approach is not working and throw the error:
Unable to cast object of type 'Unity.Microsoft.DependencyInjection.ServiceProvider' to type 'Unity.UnityContainer'.
Is there a way to do this or am I restricted to IServiceProvider interface even though I'm using Unity implementation?
EDIT:
So, I can't find a way to switch from IServiceProvider to IUnityContainer with something like a cast. So, I changed the above code a bit to return Unity container instead:
public IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Register services here.
// Unity container interface:
var containerFactory = new ServiceProviderFactory(null);
IUnityContainer container = containerFactory.CreateBuilder(services);
return container;
}
Now, I can use the usual Unity DI in the .Net Core App.
You should use NugetPackage which integrates Unity with ServiceProvider provided by .NET Core.
https://github.com/unitycontainer/microsoft-dependency-injection

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^^

Dependency Injection for Azure chat bot middleware?

I am working on a new chat bot using Azure Bot Service and QnAMaker. We are using BotBuilder middleware, including custom middleware, to tailor the bot behavior.
One of the middlewares will be calling an Azure function and I would like to use the new HttpClientFactory feature with the custom middleware - but this requires dependency injection.
How can I use dependency injection in BotBuilder middleware like you do with regular .NET Core middleware?
When you look at the bot configuration in the Startup.cs, you can see how it requires you to new up all of the bot dependencies:
services.AddHttpClient<MyFunctionClient>(client =>
{
client.BaseAddress = new Uri(mySettings.GetValue<string>("myFunctionUrl"));
client.DefaultRequestHeaders.Add("x-functions-key", mySettings.GetValue<string>("myFunctionKey"));
});
services.AddBot<QnAMakerBot>(options =>
{
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
options.ConnectorClientRetryPolicy = new RetryPolicy(
new BotFrameworkHttpStatusCodeErrorDetectionStrategy(),
3,
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(20),
TimeSpan.FromSeconds(1));
var middleware = options.Middleware;
middleware.Add(new ConversationState<ChatLog>(new MemoryStorage()));
middleware.Add(new MyCustomMiddleware()); // <- I want to inject a typed HttpClient here
//... etc. ....
Is there a different way to configure the bot that allows for dependency injection?
If MyCustomMiddleware requires a typed HttpClient in its constructor, I have to create a new instance right here, so I don't get the benefit of the DI and the configuration I just set up.
While I am not a fan of service locator pattern, the current design of the bot configuration is not very dependency injection friendly.
Using the nature of how the bot middleware are setup but having to provide a new instance during startup, I came up with the following work around.
public class BotMiddlewareAdapter<TMiddleware> : IMiddleware
where TMiddleware : IMiddleware {
private readonly Lazy<TMiddleware> middleware;
public BotMiddlewareAdapter(IServiceCollection services) {
middleware = new Lazy<TMiddleware>(() =>
services.BuildServiceProvider().GetRequiredService<TMiddleware>());
}
public Task OnTurn(ITurnContext context, MiddlewareSet.NextDelegate next) {
return middleware.Value.OnTurn(context, next);
}
}
It takes the IServiceCollection as an explicit dependency and defers the creation of the service provider and eventual resolution of the actual middleware in a factory delegate.
It can then be implemented using
middleware.Add(new BotMiddlewareAdapter<MyCustomMiddleware>(services));
When the adapter is invoked it will lazy resolve the intended middleware on initial call and then invoke it.
In fact you can take this another step further and convert it to an extension method
public static class BotBuilderMiddlewareExtension {
public static void Add<TMiddleware>(this IList<IMiddleware> middleware, IServiceCollection services)
where TMiddleware : IMiddleware {
middleware.Add(new BotMiddlewareAdapter<TMiddleware>(services));
}
}
Which simplifies the setup to
middleware.Add<MyCustomMiddleware>(services);

Categories

Resources