I'm working on a WCF project in which I'm using Autofac as IoC container and MediatR as a mediator to execute my requests & commands.
The "base" implementation of the WCF contract takes an instance of IMediator as a dependency to delegate the work associated with each request to the associated handler. I also have several decorators I stack up the base implementation for things like authorization and error handling.
As specified in this page of Autofac documentation, the use of a MultitenantServiceImplementationDataProvider is necessary in order to satisfy WCF internals when you use decorators on the service implementation. Nothing more multitenant-related is needed, so it just consists of:
AutofacServiceHostFactory.ServiceImplementationDataProvider = new MultitenantServiceImplementationDataProvider();
Also, in the .svc I specified the qualified name of the interface since it's supported by Autofac and I have decorators on top on my base implementation.
Now, on to MediatR.
MediatR uses service location to instantiate the appropriate handlers when it is given a request. More specifically, it relies on CSL.
Not a problem, since Autofac provides a bridge to support CSL.
The "tricky" part relies in the fact that my handlers take DbContext as dependencies, and I want them disposed by Autofac after each WCF request.
So the AutofacServiceLocator has to be given the scope that is created for the specific request, since the root scope is not disposed and neither would be the DbContext instances.
Autofac got you covered with the AutofacInstanceContext.Current static property which is the equivalent of the AutofacDependencyResolver.RequestLifetimeScope in ASP.NET MVC.
So far so good, here's how I registered the ServiceLocatorProvider the Mediator class takes a depedency on:
builder
.Register(x => new ServiceLocatorProvider(() => new AutofacServiceLocator(AutofacInstanceContext.Current.OperationLifetime)))
.InstancePerLifetimeScope();
It works as expected on my development box but I get a NullReferenceException on the staging environment and I don't really know where to look for - GoogleBing didn't give relevant results.
Only things that differ from both environments:
HTTP on my box vs HTTPS on the staging env.
debugattribute on <system.web> element was set to false on staging env.
And that's about it...
.NET frameworks as the same, 4.5.2.
Anyone has an idea?
Thanks!
Fixed it by changing:
builder
.Register(x => new ServiceLocatorProvider(() => new AutofacServiceLocator(AutofacInstanceContext.Current.OperationLifetime)))
.InstancePerLifetimeScope();
with
builder
.Register(x =>
{
var serviceLocator = new AutofacServiceLocator(AutofacInstanceContext.Current.OperationLifetime);
return new ServiceLocatorProvider(() => serviceLocator);
}
.InstancePerLifetimeScope();
I wouldn't be able to tell you exactly why it didn't work, but I guess that by the time the lambda expression () => new AutofacServiceLocator(AutofacInstanceContext.Current.OperationLifetime) was executed internally by MediatR, it was too late and the current operation context was disposed of or released.
Any insight would still be greatly appreciated!
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.
I have a WebApi project and in a controller I'm using HostingEnvironment.QueueBackgroundWorkItem to start a job in a separate thread:
public IHttpActionResult DoJob()
{
HostingEnvironment.QueueBackgroundWorkItem(ct =>
{
var service = new MyService(logService, userInfo);
var entity = service.DoJob(ct);
});
return Ok();
}
Currently, in the above I'm using a concrete instance (which I want to replace) of my service . I also use Autofac and for my services, I configured as follows:
builder.RegisterAssemblyTypes(assemblies)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerRequest();
The above registration works fine for all services when using normally within a request.
Will the above registration also work for services when run in a separate thread after the request terminates?
Will the above registration also work for services when run in a separate thread after the request terminates?
Yes the configuration for the registration will work.
Whatever method you call in your service may or may not work:
What this means is that if you try to resolve components that are registered as instance-per-request but there’s no current request… you’re going to get an exception..
Either one of two following cases are correct.
You might get autofac to work sometimes, because the request still exists, but sometimes won't because the request ends.
or (most likely)
the execution of QueueBackgroundWorkItem() is outside the lifetime scope so it won't work at all.
So ultimately your service should create it's own lifetime scope (IDisposable etc etc).
Dependency injection is nothing to do with thread, unless we missed to configure the dependency injection of all the accessible members of that thread at the start of the application/service.
Scenario
I am working to follow the IoC pattern and use the Microsoft DI Framework, but I'm hitting a wall and can't decide if it's my mindset, my approach, or I'm just doing it wrong.
I have a multi-tenant application that utilizes a Utility class to handle isolation and accessing the data of the tenant based on their unique configuration. The tenant is identified during authentication, but the tenant data has to be accessible and handled based on the request. This Utility class is registered under ConfigureServices in the Startup.cs and the constructor requires two parameters - a TenantDbContext and a Tenant.
public class TenantUtility{
public TenantUtility(TenantDbContext context, Tenant tenant){/*...*/}
}
Problem
Realizing that I'm probably pushing the limits of the DI Framework, I'm trying to build an implementationFactory in the Startup.cs. I've tried two approaches for accessing the tenant Id to build the Tenant object: one is using a User Claim, the second is a Route parameter.
services.AddTransient<TenantUtility>((svc)=> {
var tenantContext = svc.GetService<TenantDbContext>();
var accessor = svc.GetService<IHttpContextAccessor>();
var httpContext = accessor.HttpContext;
//httpContext is NULL...
//How do I get access to the tenant?
Common.Tenant t = new Common.Tenant();
//Set Tenant Identifier in t (once I get it)
return new StudentDataManager(tenantContext, t);
});
In both situations, when I setup the ImplementationFactory inside the Startup.cs, I don't have access to the user and I don't have access to the RouteData (or can't figure out how to get it) -- I even tried using IHttpContextAccessor, but the HttpContext property is null.
Am I approaching the pattern incorrectly? Should I be able to use DI for this level of detail be injected into the Utility before the Utility class is passed into the Controller?
You shouldn't pass in non-DI-managed types into services you want the container to provide for you (in this case, your Tenant parameter). Only request services that the container can provide. If you need a tenant, perhaps another service like an ITenantAccessor could be injected and would be able to get one.
I suspect your implementation could be done in middleware (if not using MVC) or as a filter (if using MVC). In either case, you can use DI from the filter or middleware class to inject your tenant service. In the class's invoke method, you will have access to the current context and request and should be able to do the things you need to. Remember that ConfigureServices runs before the app has started, so there is no context and no request is yet being made.
If you want to see some examples of filters, and especially how to do DI into filters, check out:
https://github.com/ardalis/GettingStartedWithFilters
and
http://ardalis.com/real-world-aspnet-core-mvc-filters
If you want to do it in middleware, then these might help:
https://github.com/ardalis/NotFoundMiddlewareSample
and
http://ardalis.com/using-custom-middleware-to-record-and-fix-404s-in-aspnet-core-apps
It seems to me that what you are injecting in the TenantUtility as a frist parameter is a valid abstraction (though it's probably better to use some interface), but the other one is a value-object. You don't generally inject value objects as they are not abstraction of some operations that you need to perform, but data. So I would pass the Tenant as a parameter to an operation on the TenantUtility class.
Another thing that I can suggest is to use a full-blown DI container like SimpleInjector (there are many others like Ninject, CastlWindsor, etc.). They are much more advanced as far as I know and can easily be integrated. Many of them have .Net Core integration already.
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...