I need to get a scoped instance from HttpContext.RequestServices (see: Using Scoped dependencies in Singletons in ASP.NET Core). The problem is that in Blazor-server rendering phase the instances injected to a constructor and returned from HttpContext.RequestServices.GetRequiredService are not the same.
Let's create a class of which we want to have a scoped instance
public class TestingClass {
public int MyProperty { get; set; }
}
And a consumer class
public class ResultClass {
public string result;
public ResultClass(TestingClass fromInjection, IHttpContextAccessor contextAccessor, IServiceProvider serviceProvider) {
var fromRequest = contextAccessor.HttpContext.RequestServices.GetRequiredService<TestingClass>();
var fromServiceProvider = serviceProvider.GetService<TestingClass>();
var requestXinjection = (fromRequest == fromInjection);
var serviceXinjection = (fromServiceProvider == fromInjection);
result = $"Instance in from request and injection is the same {requestXinjection}. Instance in from request and service provider is the same {serviceXinjection}";
}
}
And ConfigureServices
services.AddHttpContextAccessor();
services.AddScoped<TestingClass>();
services.AddTransient<ResultClass>();
Now let's inject the ResultClass instance to a Razor Page and Blazor Component (render-mode="ServerPrerendered")
On the Razor Page all three instances are the same:
Instance in from request and injection is the same True. Instance in from request and service provider is the same True
In blazor prerendering phase all three instances are the same:
Instance in from request and injection is the same True. Instance in from request and service provider is the same True
In blazor rendering phase the injected instance is different than the one from the request:
Instance in from request and injection is the same False. Instance in from request and service provider is the same True
By my opinion in all cases all three instances must be the same. Is it true?
Do I interpret DI behavior wrong way? Is it a bug?
Notes
When I set render-mode="Server" the behavior is the same as in rendering phase (False, True).
I use asp.net-core-3.1
Related
The InvalidOperationException Cannot consume scoped service from singleton is a well-known scenario described very well here
I am investigating a way to reproduce this exception (assuming it comes from the Dependency Injection framework) but I am not succeeding.
I've created a repo with a commit to illustrate it but basically I have the following test:
public class Given_Scoped_Repository_And_Singleton_Service_That_Uses_The_Repository_When_Getting_Service_From_Different_Scope_After_Disposing_First_Scope
: Given_When_Then_Test
{
private IServiceScope _scopeOne;
private IServiceScope _scopeTwo;
private ServiceSample _serviceSampleOne;
private ServiceSample _serviceSampleTwo;
protected override void Given()
{
var serviceCollection =
new ServiceCollection()
.AddScoped<RepositorySample>()
.AddSingleton<ServiceSample>()
.BuildServiceProvider();
_scopeOne = serviceCollection.CreateScope();
_scopeTwo = serviceCollection.CreateScope();
_serviceSampleOne = _scopeOne.ServiceProvider.GetService<ServiceSample>();
_scopeOne.Dispose();
}
protected override void When()
{
_serviceSampleTwo = _scopeTwo.ServiceProvider.GetService<ServiceSample>();
}
[Fact]
public void Then_It_Should_Get_The_Same_Service_Instance()
{
_serviceSampleOne.Should().Be(_serviceSampleTwo);
}
[Fact]
public void Then_It_Should_Have_The_Same_Repository_Instance()
{
_serviceSampleOne.RepositorySample.Should().Be(_serviceSampleTwo.RepositorySample);
}
}
and
class RepositorySample { }
class ServiceSample
{
public RepositorySample RepositorySample { get; }
public ServiceSample(RepositorySample repositorySample)
{
RepositorySample = repositorySample;
}
}
I would expect to see that InvalidOperationException thrown because I am getting a singleton service from a different scope expecting the repository to be different (since it's scoped). These tests are not failing even if I explicitly dispose the first context that instantiated the scoped RepositorySample and I am a bit confused here.
Why the dependency injection framework Microsoft.Extensions.DependencyInjection 3.1.3 doesn't throw the exception alerting me of the singleton-scope trap?
If I dispose the first scope (as if it was a scoped DbContext in an Mvc scenario that should die when a response is issued), shouldn't this cause the RepositorySample instance to be disposed? It isn't, even when I have RepositorySample implement IDisposable I can see the scoped instance is never disposed (the Dispose method is not executed)
How could I design my test to see the exception as if it was happening with a scoped DbContext that is instantiated in a singleton service?
It's not throwing the exception because the option to validate scopes hasn't been specified.
Change this:
.BuildServiceProvider();
to this:
.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
ServiceProviderOptions.ValidateScopes
true to perform check verifying that scoped services never gets resolved from root provider;
You can also perform the validation when the ServiceProvider is built instead of waiting until a service is resolved:
.BuildServiceProvider(
new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = true
});
That's what an MVC app does at startup. That's why the exception is thrown at startup, not when a controller is resolved. It will throw that exception if it detects errors even if the registered dependencies will never be resolved at runtime. To see it, register your dependencies - ServiceSample, RepositorySample - as you did in your unit test, but don't inject them into any controllers. You'll still get the exception at startup when tries to build the ServiceProvider.
In my Asp.Net Core App I need a singleton service that I can reuse for the lifetime of the application. To construct it, I need a DbContext (from the EF Core), but it is a scoped service and not thread safe.
Therefore I am using the following pattern to construct my singleton service. It looks kinda hacky, therefore I was wondering whether this is an acceptable approach and won't lead to any problems?
services.AddScoped<IPersistedConfigurationDbContext, PersistedConfigurationDbContext>();
services.AddSingleton<IPersistedConfigurationService>(s =>
{
ConfigModel currentConfig;
using (var scope = s.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
currentConfig = dbContext.retrieveConfig();
}
return new PersistedConfigurationService(currentConfig);
});
...
public class ConfigModel
{
string configParam { get; set; }
}
What you're doing is not good and can definitely lead to issues. Since this is being done in the service registration, the scoped service is going to be retrieve once when your singleton is first injected. In other words, this code here is only going to run once for the lifetime of the service you're registering, which since it's a singleton, means it's only going to happen once, period. Additionally, the context you're injecting here only exists within the scope you've created, which goes away as soon as the using statement closes. As such, by the time you actually try to use the context in your singleton, it will have been disposed, and you'll get an ObjectDisposedException.
If you need to use a scoped service inside a singleton, then you need to inject IServiceProvider into the singleton. Then, you need to create a scope and pull out your context when you need to use it, and this will need to be done every time you need to use it. For example:
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Just to emphasize, again, you will need to do this in each method that needs to utilize the scoped service (your context). You cannot persist this to an ivar or something. If you're put off by the code, you should be, as this is an antipattern. If you must get a scoped service in a singleton, you have no choice, but more often than not, this is a sign of bad design. If a service needs to use scoped services, it should almost invariably be scoped itself, not singleton. There's only a few cases where you truly need a singleton lifetime, and those mostly revolve around dealing with semaphores or other state that needs to be persisted throughout the life of the application. Unless there's a very good reason to make your service a singleton, you should opt for scoped in all cases; scoped should be the default lifetime unless you have a reason to do otherwise.
Although Dependency injection: Service lifetimes documentation in ASP.NET Core says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But in your case this is not the issue. Actually you are not resolving the scoped service from singleton. Its just getting an instance of scoped service from singleton whenever it requires. So your code should work properly without any disposed context error!
But another potential solution can be using IHostedService. Here is the details about it:
Consuming a scoped service in a background task (IHostedService)
Looking at the name of this service - I think what you need is a custom configuration provider that loads configuration from database at startup (once only). Why don't you do something like following instead? It is a better design, more of a framework compliant approach and also something that you can build as a shared library that other people can also benefit from (or you can benefit from in multiple projects).
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var persistentConfigBuilder = new ConfigurationBuilder();
var connectionString = builtConfig["ConnectionString"];
persistentStorageBuilder.AddPersistentConfig(connectionString);
var persistentConfig = persistentConfigBuilder.Build();
config.AddConfiguration(persistentConfig);
});
}
Here - AddPersistentConfig is an extension method built as a library that looks like this.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddPersistentConfig(this IConfigurationBuilder configurationBuilder, string connectionString)
{
return configurationBuilder.Add(new PersistentConfigurationSource(connectionString));
}
}
class PersistentConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public PersistentConfigurationSource(string connectionString)
{
ConnectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new PersistentConfigurationProvider(new DbContext(ConnectionString));
}
}
class PersistentConfigurationProvider : ConfigurationProvider
{
private readonly DbContext _context;
public PersistentConfigurationProvider(DbContext context)
{
_context = context;
}
public override void Load()
{
// Using _dbContext
// Load Configuration as valuesFromDb
// Set Data
// Data = valuesFromDb.ToDictionary<string, string>...
}
}
I have static helper class
public static class Current
{
public static string Host
{
get { return "httpContextAccessor here"; }
}
}
How I can get access to current HttpContext inside Host property?
You can't and you shouldn't. This beats the whole purpose of having a dependency injection system at all. Static classes (for runtime data or Service Locator) is an anti-pattern.
In ASP.NET Core you have to inject IHttpContextAccessor in classes where you need it. You can make a non-static class and do something along the lines of:
public class RequestInformation : IRequestInformation
{
private readonly HttpContext context;
public RequestInformation(IHttpContextAccessor contextAccessor)
{
// Don't forget null checks
this.context = contextAccessor.HttpContext;
}
public string Host
{
get { return this.context./*Do whatever you need here*/; }
}
}
and in your class library inject it:
public class SomeClassInClassLibrary
{
private readonly IRequestInformation requestInfo;
public SomeClassInClassLibrary(IRequestInfomation requestInfo)
{
// Don't forget null checks
this.requestInfo = requestInfo;
// access it
var host = requestInfo.Host;
}
}
Be aware that your SomeClassInClassLibrary must be resolved with either Scoped or Transient mode and it can't be Singleton, because HttpContext is only valid for the duration of the request.
Alternatively if SomeClassInClassLibrary has to be singleton, you have to inject a factory and resolve the IRequestInformation on demand (i.e. inside an action).
Last but not least, IHttpContextAccessor isn't registered by default.
IHttpContextAccessor can be used to access the HttpContext for the current thread. However, maintaining this state has non-trivial performance costs so it has been removed from the default set of services.
Developers that depend on it can add it back as needed:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Source: The IHttpContextAccessor service is not registered by default
Attempting to inject data into a FluentValidation validator:
public class MyFormValidator : AbstractValidator<MyForm>
{
private readonly IQueryable<Models.User> _users;
public MyFormValidator(IQueryable<Models.User> users)
{
_users = users;
...
}
}
My validator factory:
public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
private readonly IContainer container;
public DependencyResolverValidatorFactory(IContainer container)
{
this.container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
return container.ResolveOptionalKeyed<IValidator>(validatorType);
}
}
My Autofac configurator:
public class AutofacConfigurator
{
public static void Configure()
{
var builder = new ContainerBuilder();
...
builder.RegisterType<MyFormValidator>()
.Keyed<IValidator>(typeof(IValidator<MyForm>))
.As<IValidator>()
// 2nd parameter returns IQueryable<User>
.WithParameter("users", new SqlRepository<User>(dataContext))
.InstancePerRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Register the validator factory with FluentValidation, and register
// FluentValidation as the model validator provider for the MVC framework.
// see http://www.jerriepelser.com/blog/using-fluent-validation-with-asp-net-mvc-part-3-adding-dependency-injection
var fluentValidationModelValidatorProvider =
new FluentValidationModelValidatorProvider(
new DependencyResolverValidatorFactory(container));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
fluentValidationModelValidatorProvider.AddImplicitRequiredValidator = false;
ModelValidatorProviders.Providers.Add(fluentValidationModelValidatorProvider);
}
}
Getting the following exception:
No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
I have other validators, most of which will not need data injected into them.
This is largely new ground for me (in both Autofac and FluentValidation) and am still trying to understand what I am doing here. I suspect I'm simply registering my type incorrectly. How do I fix this and properly register my type?
(My apologies if this is too similar to other questions that were already asked.)
I have zero experience with FluentValidation, but I doubt it's the cause of your issues anyway, so I'll plow forward regardless.
The exception you're getting means that Autofac can't resolve your service as 'instance per request'. There's a lot of documentation as to what this means on the Autofac documentation page. To summarize, it means that Autofac will attempt to resolve the service from a lifetime scope that is automatically created for each request sent to the webserver. When you register something as .InstancePerRequestScope() but then attempt to resolve that service outside of that scope, you'll get the DependencyResolutionException you see.
So we've established that your MyFormValidator isn't being resolved from a 'Request' scope. Why?
The custom DependencyResolverValidatorFactory you've written takes the actual IContainer that was built by Autofac, and resolves from that. This is a special type of ILifetimeScope, the 'root scope'. There's no request lifetime scope directly associated with this, so you get your exception. You need to to resolve from an ILifetimeScope that is began from the 'request' scope, or a sub-scope that is contained within the request scope.
The Autofac/MVC integration already automatically hosts a request scope (within the AutofacDependencyResolver, see the source), but your custom DependencyResolverValidatorFactory doesn't resolve from it. If you want to do that, I suppose you could modify your DependencyResolverValidatorFactory to accept the AutofacDependencyResolver instance instead, and use that to resolve.
It would look something like this:
public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
private readonly AutofacDependencyResolver resolver;
public DependencyResolverValidatorFactory(AutofacDependencyResolver resolver)
{
this.resolver = resolver;
}
public override IValidator CreateInstance(Type validatorType)
{
return resolver.RequestLiftimeScope.ResolveOptionalKeyed<IValidator>(validatorType);
}
}
Note the RequestLifetimeScope stuck in there.
Then you create this in your .Configure() method using
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
var fluentValidationModelValidatorProvider =
new FluentValidationModelValidatorProvider(
new DependencyResolverValidatorFactory(resolver));
That should get rid of the exception, assuming that this factory does indeed have a request to work from when creating instances of IValidators. If not, You might need to register using the default behavior (.InstancePerDependency(), where it creates a new instance every time it's requested) or a singleton (.SingleInstance()), depending on how/if validators can or should be shared.
Good luck.
I had AutoFac working properly with MVC4. I'm trying to transition to Web API 2. Here's what I've got for setting up AutoFac:
public class AutofacRegistrations
{
public static void RegisterAndSetResolver()
{
// Create the container builder.
var containerBuilder = new ContainerBuilder();
// Register the Web API controllers.
containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// Only generate one SessionFactory ever because it is expensive.
containerBuilder.Register(x => new NHibernateConfiguration().Configure().BuildSessionFactory()).SingleInstance();
// Everything else wants an instance of Session per HTTP request, so indicate that:
containerBuilder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerApiRequest();
containerBuilder.Register(x => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)).InstancePerApiRequest();
containerBuilder.RegisterType<NHibernateDaoFactory>().As<IDaoFactory>().InstancePerApiRequest();
containerBuilder.RegisterType<StreamusManagerFactory>().As<IManagerFactory>().InstancePerApiRequest();
// Build the container.
ILifetimeScope container = containerBuilder.Build();
// Create the depenedency resolver.
var dependencyResolver = new AutofacWebApiDependencyResolver(container);
// Configure Web API with the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver;
}
}
I'm pretty confident all of that is correct. My problem arises when I'm trying to setup some test cases. My base class for my test cases isn't a controller so it isn't automatically passed anything. In MVC4 I did the following:
[SetUp]
public void SetUp()
{
HttpSimulator = new HttpSimulator().SimulateRequest();
Logger = DependencyResolver.Current.GetService<ILog>();
DaoFactory = DependencyResolver.Current.GetService<IDaoFactory>();
Session = DependencyResolver.Current.GetService<ISession>();
ManagerFactory = DependencyResolver.Current.GetService<IManagerFactory>();
}
[TearDown]
public void TearDown()
{
HttpSimulator.Dispose();
}
Unfortunately, there's no DependencyResolver.Current in WebAPI. So I'm left wondering how to do this properly?
This builds, but is NOT correct. I received the message "Session Closed!" when I try to execute a test case:
[SetUp]
public void SetUp()
{
using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope())
{
// TODO: Consider initializing Helpers during setup to keep this DRY.
Logger = (ILog)scope.GetService(typeof(ILog));
DaoFactory = (IDaoFactory)scope.GetService(typeof(IDaoFactory));
Session = (ISession)scope.GetService(typeof(ISession));
ManagerFactory = (IManagerFactory)scope.GetService(typeof(IManagerFactory));
}
}
With WebAPI, you don't need access to the current resolver because the current dependency scope generally comes along with the inbound HttpRequestMessage. That message is also what's responsible for generating the new request scope.
You can see the code for this in the System.Net.Http.HttpRequestMessageExtensions.GetDependencyScope method.
One big thing you'll notice in WebAPI is that you don't actually need to set anything in global static values - that is, you don't need to set a global configuration/resolver because everything is instance-based now.
For testing, what this means is:
Your setup will create an HttpRequestMessage with the appropriate dependency resolver and configuration.
Your teardown will dispose of the HttpRequestMessage which will, in turn, dispose the dependency scopes, etc. down the line.
Individual tests will use HttpRequestMessage.GetDependencyScope() if they need to do manual resolution of something and the request message will be used to coordinate/pass around the scope.
In a more concrete fashion, it might look like:
private HttpRequestMessage _request;
[SetUp]
public void SetUp()
{
var builder = new ContainerBuilder();
// Register stuff.
var container = builder.Build();
var resolver = new AutofacWebApiDependencyResolver(container);
var config = new HttpConfiguration();
config.DependencyResolver = resolver;
config.EnsureInitialized();
this._request = new HttpRequestMessage();
this._request.SetConfiguration(config);
}
[TearDown]
public void TearDown()
{
this._request.Dispose();
}
[Test]
public void Test()
{
// When you need to resolve something, use the request message
this._request.GetDependencyScope().GetService(typeof(TheThing));
}
What's nice about this is that you don't have to fight with global configuration settings or resetting static values after every test.
You might wonder why you'd pass around the whole request message rather than just the dependency resolver - the reason is that the request message is what coordinates and controls the lifetime of the dependency scope. Otherwise, when you call GetDependencyScope multiple times, you'll get multiple different scopes rather than the same one as you'd expect.
Some things to consider from a design perspective:
You might want to put the actual registrations of things into an Autofac module so it can be reused in both tests and in your RegisterAndSetResolver method without having to worry about global statics getting tampered with.
Instead of a RegisterAndSetResolver modifying the global static configuration, you might consider just setting the resolver on the HttpConfiguration object that gets wired up in that WebApiConfig.Register method that WebAPI gives you. Take an HttpConfiguration object in as a parameter and set the resolver on that rather than the global.
If you're doing every registration for everything in a unit test, your unit tests might be closer to "integration tests." You might consider looking at only what's required for the stuff you're testing and using a mock framework to register stubs rather than actually registering a boatload of "real stuff."
Anyway, HttpRequestMessage is the way to go for WebAPI.