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.
Related
I have the code like this in my unit test setup:
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IDependency, Dependency>();
services.AddLogging();
services.RemoveAll<ILoggerProvider>();
services.AddSingleton<ILoggerProvider, MyCustomProvider>(provider =>
{
var myDependency = provider.GetService<IDependency>();
return new MyCustomProvider(myDependency );
});
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger>();
When I run this logger is null.
The lambda for the DI factory to create MyCustomProvider is never called. Nor is the constructor to MyCustomProvider nor the constructor of the class I made that implements ILogger.
I am guessing that I am missing a step to wire this up. But there is a lot of conflicting documentation out there between .Net Core 1, 2 and 3.
What do I need to do to wire up my provider the .Net Core 3 way? (Such that I can get an ILogger that uses it.)
NOTES:
I don't call LoggingBuilder.AddProvider. But if you look at the source code for that extension, it just calls AddSingleton, which is what I do, but with a factory. (Got the idea here.)
Similarly services.RemoveAll<ILoggerProvider>() is the same as calling ClearProviders.
My goal is to simulate an injection of an ILogger into a class constructor.
For Example:
public class SomeClass
{
public SomeClass(ILogger<SomeClass> logger)
{
// Do Stuff
}
}
Somehow this is done without indicating any provider. I would like to make it be the same in my unit test. (Just use my custom provider.)
The reason your logger variable is null in the example is because ILogger isn't actually registered as a service. If you look at the source of AddLogging you can see it only registers ILogger<> and ILoggerFactory. If you've ever tried to accept an ILogger instead of an ILogger<MyClass> via the .NET Core DI you will have run into the following exception*:
System.InvalidOperationException: 'Unable to resolve service for type
'Microsoft.Extensions.Logging.ILogger' while attempting to activate
'Your.Service'
Based on this, your testing code is flawed as you'd never have received an ILogger in the first place. To see that you're code is in fact working, modify your testing code so that it retrieves an ILogger<SomeClass> instead. The result of your variable will be non-null and a break point set in your provider's constructor will be hit:
// Get*Required*Service won't throw
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
If you want to be able to inject ILogger, you will need to register it separately with a default category name**:
services.AddSingleton<ILoggerProvider, MyCustomProvider>(); // no need for lambda
services.AddSingleton<ILogger>(sp =>
sp.GetService<ILoggerFactory>().CreateLogger("Default")
);
The following will both now work and use your custom provider:
var loggerA = serviceProvider.GetRequiredService<ILogger<Program>>();
var loggerB = serviceProvider.GetRequiredService<ILogger>();
I have my own custom provider/factory/logger that I use to inject xUnit's ITestOutputHelper into a custom logger following the same registration pattern as you, so I know from personal experience that this works. But I've also tested that your specific code is functional (mocking out my own IDependency)--the above code is executed and breakpoints set in the constructor of MyCustomProvider and the service registration are hit. Additionally, if I inject the logger into a class it's hit as well (as expected).
Your comment "Somehow this is done without indicating any provider" is misinformed, because you do in fact have a provider registered! But even in a scenario where all providers were cleared and no new ones were added, you'd still get a non-null logger. This is because LoggerFactory just loops through each provider to build up a new logger wrapper around them. If there's no providers then you essentially get a no-op logger.
* This is unfortunate as some tools (like R#) will suggest converting the parameter to the base type ILogger which then breaks DI!
** A default category name is required since there is no generic type argument to pass to ILoggerFactory.CreateLogger. For the generic ILogger<T> the category name is always a variation of T's name--but we obviously don't get that with the non-generic version. If I had to guess, this is probably why they don't register an implementation of ILogger by default.
NET CORE Testing I'm doing like this:
in the constructor of the test class (or where you do your DI mapping)
public class UnitTest1
{
public IConfigurationRoot Configuration { get; set; }
private readonly IDisponibilitaService _disponibilitaService;
public UnitTest1()
{
var services = new ServiceCollection();
Configuration = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Path.DirectorySeparatorChar.ToString(), "directory", "Kalliope_CTI", "backend", "KalliopeCTITestProject"))
.AddJsonFile("testconfig.json", optional: false, reloadOnChange: true)
.Build();
services.AddSingleton<ILoggerFactory, NullLoggerFactory>(); //< -- HERE TRY TO SET NullLoggerFactory
var serviceProvider = services
.AddOptions()
.BuildServiceProvider();
}
//// your test methods
}
Hope it helps you!!
It may be a shot in the dark, but you can try adding your ILoggerProvider to the ILoggerFactory:
var loggerFactory = serviceProvider.GetService<ILoggerFactory();
var loggerProvider = serviceProvider.GetService<ILoggerProvider>();
loggerFactory.AddProvider(loggerProvider);
In case it was constructed sooner than you have registered your provider, it might not be aware of it's existance.
Alternatively, would it work, if you also tell it, how to resolve the ILogger?:
...
services.AddSingleton<ILoggerProvider, MyCustomProvider>(provider =>
{
var myDependency = provider.GetService<IDependency>();
return new MyCustomProvider(myDependency );
});
services.AddSingleton(typeof(ILogger<>), provider =>
{
var loggerProvider = provider.GetService<ILoggerProvider>();
return loggerProvider.CreateLogger("TestLogger");
});
...
Finally... is there a reason, why you can't just inject ILoggerProvider into your tests, and call loggerProvider.CreateLogger(<test_class_name>) to get the logger?
I am writing a piece of middleware (maybe I want a scoped service??), I guess my plan is to have some kind of multi-tenant scenario.
If for example, I have 2 domains that respond on this service:
www.domain1.com
www.domain2.com
I want to capture the request when it starts, look at the host name that is being used and then set some other object to be available through Dependency Injection for everything further up the pipeline.
It seems that middleware should be the right way to achieve this, but not sure how to do the final step.
My options seem to be:
Middleware
Register Singleton service to access database
Register early to be the first item of middleware to capture the request.
Analyse Request Object and build custom configuration object
Add custom configuration as a scoped object to the DI container for use by other services
Service
Register Singleton service to access database
Register Singleton service for IHttpContextAccessor
Register Scoped? Service - to do equivalent of middleware
Analyse the request object and build custom configuration object
Register custom object as new scoped object in the DI container
My assumption is that the Service is able to register the custom scoped object as it is still within the ConfigureServices method of the startup.cs
However, with middleware it is initialised through the Configure method by which point the DI container has already been built?
You can use the factory-overload of AddScoped for the service you want to be different per tenant/request. Here's an example:
services.AddScoped<IServiceForTenant>(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenant = new ServiceForTenant();
// TODO: Use httpContextAcccessor.HttpContext to configure serviceForTenant.
return serviceForTenant;
});
For each request that comes in to your ASP.NET Core application, the code above will run when you first request IServiceForTenant in e.g. a controller. At this point, your code can read from IHttpContextAccessor.HttpContext and make whatever decisions it needs in order to create the implementation instance for IServiceForTenant. This same instance will then be used for the rest of the request (i.e. further up the pipeline).
The argument passed into AddScoped is Func<IServiceProvider, T>. All you need to provide here is a delegate of some kind, which could be done in one of many ways. Here's some examples:
You could just wrap the call into its own extension method, like this:
public static void AddServiceForTenant(this IServiceCollection services)
{
services.AddScoped<IServiceForTenant>(sp =>
{
// ...
});
}
In ConfigureServices:
services.AddServiceForTenant();
Use a class with a static method:
public static class ServiceForTenantFactory
{
public static ITenantForService Create(IServiceProvider sp)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(ServiceForTenantFactory.Create);
Use a class with an instance method:
public class ServiceForTenantFactory
{
public ITenantForService Create(HttpContext httpContext)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenantFactory = new ServiceForTenantFactory(); // Or use DI.
return serviceForTenantFactory.Create(httpContextAccessor.HttpContext);
});
This last option is the most flexible, as you could even resolve ServiceForTenantFactory itself from DI and it can have its own dependencies, etc. Note also that Create here takes the HttpContext directly (as an example).
As I've already said, there are yet more options than the three of shown, but this should be a good base to work with.
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.
I'm using ASP.NET Core and Autofac. Almost everything is registered as per lifetime scope ("per request"). So my database context DbContext is the same instance throughout a request.
However I have a singleton which also depends on DbContext. To avoid a captive dependency, it is injected as Func<Owned<DbContext>>, which means a new DbContext instance each time.
The problem is I need the same instance, as everywhere else during the request, not a new one.
I want to avoid a captive dependency bug, but I also want the same instance. Is that possible via tagging or a custom registration?
From the comments the least "architectural" painful approach may be by creating your own Scoped<T> class which will resolve the DbContext from current HttpContext
// Use an interface, so we don't have infrastructure dependencies in our domain
public interface IScoped<T> where T : class
{
T Instance { get; }
}
// Register as singleton too.
public sealed class Scoped<T> : IScoped<T> where T : class
{
private readonly IHttpContextAccessor contextAccessor;
private HttpContext HttpContext { get; } => contextAccessor.HttpContext;
public T Instance { get; } => HttpContext.RequestServices.GetService<T>();
public Scoped(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
}
Register it as
// Microsoft.Extensions.DependencyInjection
services.AddSingleton(typeof(IScoped<>), typeof(Scoped<>);
// Autofac
containerBuilder.RegisterType(typeof(Scoped<>))
.As(typeof(IScoped<>));
Then inject this into your validator service.
public class CustomerValidator: AbstractValidator<Customer>
{
private readonly IScoped<AppDbContext> scopedContext;
protected AppDbContext DbContext { get } => scopedContext.Instance;
public CustomValidator(IScoped<AppDbContext> scopedContext)
{
this.scopedContext = scopedContext ?? throw new ArgumentNullException(nameof(scopedContext));
// Access DbContext via this.DbContext
}
}
This way you can inject any scoped service w/o further registrations.
Additional notes
Autofac is considered a "conformer" (see docs) DI and integrates well with ASP.NET Core and Microsoft.Extensions.DependencyInjection.
From the documentation
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container. If you want
// to dispose of the container at the end of the app,
// be sure to keep a reference to it as a property or field.
builder.RegisterType<MyType>().As<IMyType>();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
There a few subtle differences to the default usage of Startup class and Microsoft.Extensions.DependencyInjection container.
ConfigureServices isn't void anymore, it returns IServiceProvider. This will tell ASP.NET Core to use the returned provider instead of DefaultServiceProvider from Microsoft.Extensions.DependencyInjection.
We return the Autofac container adapter: new AutofacServiceProvider(this.ApplicationContainer) which is the root container.
This is important to make ASP.NET Core use the container everywhere in ASP.NET Core, even inside middlewares which resolve per request dependencies via HttpContext.RequestedServices.
For that reasons you can't use .InstancePerRequest() lifetime in Autofac, because Autofac isn't in control of creating scopes and only ASP.NET Core can do it. So there is no easy way to make ASP.NET Core use Autofac's own Request lifetime.
Instead ASP.NET Core will create a new scope (using IServiceScopeFactory.CreateScope()) and use a scoped container of Autofac to resolve per-request dependencies.