I develop an app on asp.net api2 with autofac and mediatR, currently facing some issue with the dependency injection.
// This is registered in the global.asax file and working properly in the controller level
//i'm trying to register the entity framework context as instance per request
builder.RegisterType<EFContext>().InstancePerRequest();
However when sending the command throught MediatR pipeline, i get an exception because MetdiatR service provider cannot read the http request scope.
Below code is also located in the global asax file.
builder.Register<ServiceFactory>(context =>
{
var componentContext = context.Resolve<IComponentContext>();
return t => { object o;
return componentContext.TryResolve(t, out o) ? o : null; };
});
as the delegate function for service locator is called it throw an error saying that No scope with a Tag matching
"AutofacWebRequest" is visible from the scope in....
is there any work around to make mediatr ServiceFactory aware of the autofact InstancePerRequest scope ?
Use InstancePerLifetimeScope instead for resolving entity framework context.
I haven't used mediatR but seems like it doesn't follow the request response pattern, due to which Autofac can't associate any Request Lifetime with it.
Note that the difference between Request Scope and Lifetime Scope in Autofac is only that Autofac will treat a request as a lifetime.
You can read further about scopes from here,
https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html
Related
I am currently working on a feature and added the builder code like this in the Autofac
builder.RegisterType<ILTLoPublisher<ScheduleUpdateEvent>>()
.AsImplementedInterfaces()
.InstancePerRequest()
.Keyed<IILTLoPublisher<ScheduleUpdateEvent>>(AuditType.Schedule);
builder.RegisterType<ILTLoPublisher<ScheduleUpdatePart>>()
.AsImplementedInterfaces()
.InstancePerRequest()
.Keyed<IILTLoPublisher<ScheduleUpdatePart>>(AuditType.Part);
builder.RegisterType<ILTLoPublisher<ScheduleUpdateTest>>()
.AsImplementedInterfaces()
.InstancePerRequest()
.Keyed<IILTLoPublisher<ScheduleUpdateTest>>(AuditType.Test);
This code is run as a console app service and the call to this is made from an api service.I want it to be called as below
AutoFacModule autofac = new AutoFacModule();
var builder = new ContainerBuilder();
autofac.LoadBuilder(builder);
Container = builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
var _publisher1 = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdateEvent>>(AuditType.Schedule);
var _publisher2 = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdatePart>>(AuditType.Part);
var _publisher2 = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdateTest>>(AuditType.Test);
}
When i am trying to resolve it using the below code in my implementation class
var _publisher = scope.ResolveKeyed<IILTLoPublisher<ScheduleUpdateEvent>>(AuditType.Schedule);
I am getting the following error
Unable to resolve the type Apiconnector.Integrations.Vilt.Service.Providers.Custom.Publish.ILTLoPublisher`1[LMS.ILT.ScheduleUpdateEvent]' because the lifetime scope it belongs in can't be located
You can't use InstancePerRequest unless the object being resolved is part of a web request (as noted by the comments on the question). More specifically:
The executing application must be a web application.
The executing application needs to have the Autofac web integration in place.
The resolution must be happening in that web application as part of a response to an inbound web request - for example, as part of an MVC controller or ASP.NET Core middleware.
The "per request" semantics have nothing to do with the client making the request - it's about the server handling the request.
You might want to spend some time with the documentation on the topic. There is a section in there about how to implement custom per-request semantics for your app.
If what you are creating is a console app that takes in requests from clients (e.g., a self-hosted web application) then you need to:
Add the existing Autofac web integration for your app type (we do support ASP.NET Web API and ASP.NET Core self hosted scenarios); OR
Implement something custom if you're not using ASP.NET (see that doc I linked).
If what you are creating is a console app that issues requests as a client then you should ignore InstancePerRequest. Instead:
Create a new lifetime scope around each request (like you're doing) and treat that as a unit of work.
Register components as InstancePerLifetimeScope so there will be just one for the duration of that lifetime scope.
That said, without a minimal repro it's hard to see what you're doing beyond that to provide any sort of guidance.
Since you mentioned you're pretty new to all this, it would be very worth your time checking out the Autofac documentation to start understanding concepts like this as well as looking in the Examples repo where there are working examples of many different application types to show you how things work.
In .Net core 1.0 and 1.1 in my Services Collection I had:
services.AddDbContext<VisualJobsDbContext>();
...
...
services.AddScoped<IRecruiterRepository, RecruiterRepository>();
services.AddSingleton<IAccountRepository, AccountRepository>();
this seemed to work. Now I've gone to .net core 2.0 this has stopped working:
I've changed the above to :
services.AddDbContext<VisualJobsDbContext>(ServiceLifetime.Scoped);
...
...
services.AddScoped<IRecruiterRepository, RecruiterRepository>();
services.AddScoped<IAccountRepository, AccountRepository>();
I still receive the following error using swagger:
An unhandled exception occurred while processing the request.
InvalidOperationException: Cannot consume scoped service
FindaJobRepository.Interfaces.IAccountRepository from singleton
FindaJobServices.Interfaces.IAccountService
Does anybody have any ideas what else I can do?
IAccountService is a added as singleton and can't depend on a service with a shorter lifetime. IAccountRepository is a scoped service.
Probably you have a constructor with something like this:
public IAccountService(IAccountRepository accountRepository) {
}
Not sure how that ever worked, but essentially your issue is that you're trying to inject scoped services into a singleton, which based on the error is your AccountService.
Simply, if you need to use scoped services, the classes you inject them into must also be scoped. If you need the lifetime to be singleton, then your only option is use the service locator anti-pattern, where you instead inject IServiceProvider, and then create a scope around your operations:
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.GetRequiredService<MyDbContext>();
// do something with context
}
Importantly, do not save the instances you retrieve from your scope to ivars, statics, etc. on your singleton class. These will disposed when the scope is disposed.
I'm trying to solve a problem I have with asynchronous event-based rest services. I have services that generate events in the application, and those events are handled asynchronouly. The problem I have is my linq to sql data contexts are disposed by then, because of autofac's lifetime scopes.
I found a website that had a solution for it and it involved getting the application wide container. He got it like so :
var accessor = ((IContainerProviderAccessor) HttpContext.Current.ApplicationInstance);
return accessor.ContainerProvider.ApplicationContainer;
However, I can't import IContainerProviderAccessor, which seems to be in autofac's mvc integration assembly. How can I translate this code for web api.
IContainerProviderAccessor is no longer supported. Autofac now use the IDependencyResolver of ASP.net MVC
If you want to access the resolver you can use the DependencyResolver.Current property of ASP.net MVC.
YourDbContext yourDbContext = DependencyResolver.Current.GetService<YourDbContext>();
Another solution would be to inject Func<Owned<YourDbContext>>, it will act as a factory. Each time you need a DbContext inside an API method you will be able to invoke it Autofac will instanciate a new one.
In my setup class I have the following code (using Autofac and the MVC Web API Template in Visual Studio)
builder.RegisterType<CRMUserStore<IdentityUser>>().As<IUserLoginStore<IdentityUser>>()
.InstancePerRequest();
Then in the Startup.Auth class I have the following
UserManagerFactory = () => new UserManager<IdentityUser>(
DependencyResolver.Current.GetService<IUserLoginStore<IdentityUser>>());
This returns null. Then when I try instead of the above
UserManagerFactory = () => new UserManager<IdentityUser>(
_container.Resolve<IUserLoginStore<IdentityUser>>()); //_container is IContainer
I get an error saying
An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code
Additional information: 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.
How do I fix this?
Your IUserLoginStore service is being registered as InstancePerRequest which means it can only be resolved from within the context of a request. I.e. a lifetime scope tagged as 'AutofacWebRequest'.
AutoFac automatically creates a new lifetime scope tagged as 'AutofacWebRequest' for each request, and hence services resolved within the request can access this tagged scope.
I would imagine that the Startup.Auth class is running at the scope of the MVC application and outside of any specific request. Therefore it doesn't have access to the tagged scope and hence the exception No scope with a Tag matching 'AutofacWebRequest'.
If this is the case then changing the IUserLoginStore registration to InstancePerLifetimeScope will allow it to resolve correctly within the Startup.Auth class.
However, this would also change the behaviour when resolving within a request to always getting the application scoped service as well. Without seeing more of your code I can't tell if this would be an issue.
Here's a related question with a nice writeup: Autofac - InstancePerHttpRequest vs InstancePerLifetimeScope
Note - Ensure you have configured Asp.Net MVC to use AutoFac for dependency resolution as described in the AutoFac documentation ( https://code.google.com/p/autofac/wiki/MvcIntegration ).
protected void Application_Start() {
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Other MVC setup...
We have been using Autofac in our application (MVC 4 now) for a long time, we have dozens of attributes on the base controller everything inherits from and it has all been working fine so when the request begins our service is created and then available through all the attributes and on the controller action.
We are now looking at WebApi and have created our WebApi controller and created an attribute on the base controller using the ActionFilterAttribute from the HTTP namespace. However the problem starts here where the service injected on the property on the attribute is not the same instance as that on the ApiController. Looking at the link below this seems to be known ASP.NET Web API and dependencies in request scope
However the solution here is not ideal as we don't want our controllers to know about the dependency injection, we just want to use the service we are injecting to the property and know it is one instance per request.
We are calling this:
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
And
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Our classes are currently registered with Autofac as InstancePerLifetimeScope, what we want is to be able to have per request working for MvcControllers and ApiControllers.
Is that possible?
EDIT:
So basically this line returns the right service for the request (i.e. the same instance that is also on the ApiController)
var service = actionContext.Request.GetDependencyScope().GetService(typeof(IOurService);
But the property injection instance on the ActionFilterAttribute is not the same and if I change the Autofac registration to be InstancePerApiRequest I get the following error:
"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."
This is a known issue and a design problem in Web API. When filter instances are first created in Web API they are cached, so Autofac has to resolve the property injection using the root lifetime scope, not the request lifetime scope. There is no opportunity on a per-request basis for Autofac to do any property injection - the filters are effectively singletons within Web API and there's no hook to change that.
Thereafter, if you need per-request services in your filter, you have to use that GetDependencyScope() trick.
See these issues on Autofac for more details:
Issue #452: Property Injection in Web API ActionFilterAttribute does not use Http request scope
Issue #525: Filter not getting instance per http request