var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(configuration["ConnectionStrings:Default"]));
services.AddScoped<IApplicationDbContext>(provider =>
provider.GetService<ApplicationDbContext>()!);
})
.UseSerilog()
.Build();
Server.Bot.Initializer(host.Services);
BotOn.Initializer(host.Services);
I'm learning dependency injection and I can't figure out why any classes don't see dependencies without this piece of code
Server.Bot.Initializer(host.Services);
BotOn.Initializer(host.Services);
But this is not shown in different sources.
And I want to know how to do it.
A piece of code that I use in some classes to get a dependency
public static void Initializer(IServiceProvider provider) =>
_context = provider.GetService<IApplicationDbContext>()!;
I want IServiceProvider to be "embedded" in the class automatically like ASP.NET
without additional methods like "Initializer"
Dependencies mean services
I tried to pass as a parameter to the method
Related
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());
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.
I have a custom AuthenticationHandler<> implementation that depends on application service. Is there a way to resolve dependencies of AuthenticationHandler from Simple Injector? Or maybe cross-wire registration so that applications services can be resolved from IServiceCollection?
Sample implementation can look as follows for simplicity:
public class AuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly ITokenDecryptor tokenDecryptor;
public SecurityTokenAuthHandler(ITokenDecryptor tokenDecryptor,
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) :
base(options, logger, encoder, clock) =>
this.tokenDecryptor = tokenDecryptor;
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() =>
return tokenDecryptor.Decrypt(this);
}
...
services.AddAuthentication("Scheme")
.AddScheme<AuthenticationSchemeOptions, AuthHandler>("Scheme", options => { });
Current solution is to manually cross-wire application service which is not quite convenient:
services.AddTransient(provider => container.GetInstance<ITokenDecryptor>());
Tao's answer is right. The easiest way to implement this is to cross wire the AuthHandler to Simple Injector.
This can be done as follows:
// Your original configuration:
services.AddAuthentication("Scheme")
.AddScheme<AuthenticationSchemeOptions, AuthHandler>("Scheme", options => { });
// Cross wire AuthHandler; let Simple Injector create AuthHandler.
// Note: this must be done after the call to AddScheme. Otherwise it will
// be overridden by ASP.NET.
services.AddTransient(c => container.GetInstance<AuthHandler>());
// Register the handler with its dependencies (in your case ITokenDecryptor) with
// Simple Injector
container.Register<AuthHandler>();
container.Register<ITokenDecryptor, MyAwesomeTokenDecryptor>(Lifestyle.Singleton);
maybe cross-wire registration so that applications services can be
resolved from IServiceCollection?
No, it is impossible for .Net Core to resolve service from Simple Injector automatically.
Cross-wiring is a one-way process. By using
AutoCrossWireAspNetComponents, ASP.NET’s configuration system will not
automatically resolve its missing dependencies from Simple Injector.
When an application component, composed by Simple Injector, needs to
be injected into a framework or third-party component, this has to be
set up manually by adding a ServiceDescriptor to the
IServiceCollection that requests the dependency from Simple Injector.
This practice however should be quite rare.
Reference:Cross-wiring ASP.NET and third-party services.
As the suggestion from above, you need to register the service in IServiceCollection. Which you currently has implemented.
I was using .Net 5 and the original provided by Steven no longer worked for me after I upgraded from my older .net core app. I had to:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAuthentication("MyScheme")
.AddScheme<ApiKeyOptions, ApiKeyAuthenticationHandler>("MyScheme", o => { });
var authHandlerDescriptor = services
.First(s => s.ImplementationType == typeof(ApiKeyAuthenticationHandler));
services.Remove(authHandlerDescriptor);
services.AddTransient(c => container.GetInstance<ApiKeyAuthenticationHandler>());
}
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^^
(This question is similar to ASP.NET Core MvcOptions dependency injection without modified closure? However, I struggle to apply the solution of that question to this one.)
In my ASP.NET Core 1.1.3 project, I currently inject an ITraceWriter dependency into MvcJsonOptions in the ConfigureServices method in the following way:
public override IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddProjectSpecificStuff();
ITraceWriter traceWriter = null;
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.TraceWriter = traceWriter;
});
var provider = base.ConfigureServices(services);
traceWriter = provider.GetService<ITraceWriter>();
return provider;
}
This works, but causes code analyzers such as ReSharper to complain about access to modified closure.
Is there an alternative to achieve the same dependency injection without using modified closure?
You can likely postpone the assignment to .Configure method, it should work.
public override IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddProjectSpecificStuff();
ITraceWriter traceWriter = null;
services.AddMvc().AddJsonOptions(options =>
{
...
// other options here
});
return base.ConfigureServices(services);
}
public void Configure(IApplicationBuilder app, ITraceWriter traceWriter, IOptions<MvcJsonOptions> jsonOptions)
{
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.TraceWriter = provider.GetService<ITraceWriter>();
});
}
This will work for as long as your dependencies (like IOptions<MvcJsonOptions> and ITraceWriter are singletons.
Word of warning
However, this behavior may change in future, as some plans/ideas were on the ASP.NET Core GitHub issues, that the ASP.NET Core may change this to create an implicitly scoped context which is used during .Configure, it may or may not break the above code (if it ever comes).
But you may want consider to directly instantiate the ITraceWriter and pass it as instance to the IoC container, such as
ITraceWriter traceWriter = new TraceWriter(/* other dependencies */);
services.AddSingleton<ITraceWriter>(traceWriter);
It's okay to compose it in the composition root, as long as it's a singleton.
Or alternatively you choose this brute and somewhat ugly solution and instantiate MvcJsonOptions via factory method:
services.AddSingleton<IOptions<MvcJsonOptions>>(provider => Options.Create(new MvcJsonOptions
{
SerializerSettings.TraceWriter = provider.GetService<ITraceWriter>()
}));
Then it's pretty ugly (though you could hide that behind an extension method) and you may override defaults set by the mddleware/extension method.