2

I have a situation where I need to manually instantiate some objects in Application_BeginRequest that are dependent on some of the same components that I've registered with Autofac. I'd like to use the same instances of components that I've registered with Autofac with InstancePerLifetimeScope for injection into my MVC and WebAPI controllers. My config for both MVC and Web API works as expected, and an example of a component registration looks like so:

builder.Register(c => new MyDbContext()).AsSelf().InstancePerLifetimeScope();

Now I want to use that same instance in the class I'm instantiating in Application_BeginRequest. I've tried the following methods:

//Tried with MVC controllers
DependencyResolver.Current.GetService<MyDbContext>()));
AutofacDependencyResolver.Current.ApplicationContainer.Resolve<MyDbContext>()));
AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<MyDbContext>()));
//Tried with Web API controllers
GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(MyDbContext))

But none give me what I'm looking for, even at later points in the request lifecylce (ie, beyond BeginRequest). As an aside, I need this to work with the Web API configuration, but I tried the first 3 methods just to see if I could get any of the resolved instances to match up with what Autofac is injecting.

I have what I believe is a decent understanding of lifetime scopes in Autofac and my assumption is that the instances that are being resolved for my application's controllers are in a child scope that the none of the above 4 methods are pointed towards, but it's fuzzy to me what each of the above methods is trying to do in regard to which scope they are looking at and how they decide. What's even fuzzier is what lifetime scopes Autofac is automatically creating for the components that are ultimately injected into my controllers and when they're created.

Clarification on the points above would be a big bonus, but my primary question here is how do I get Autofac to hand me the same instances of registered components in Global.asax that it resolves for Web API and MVC controllers?

3 Answers 3

2

If you read up on this subject, you'll notice that folks mention that in most circumstances for a web application, InstancePerLifetimeScope and InstancePerRequest can be used interchangeably. What they don't mention are the exceptions where these two registrations behave differently. Gerrod has an excellent article on lifetime scopes inside of ASP.NET MVC/Web API applications and how they work in which he elaborates on this piece of info that most omit. Understanding this difference is crucial to this scenario, and his article cleared up any misunderstandings I had in regards to InstancePerLifetimeScope registration in relation to ASP.NET applications. It also made me realize that, because I need to share instances of resolved components across my MVC/Web API controllers and within the global.asax, InstancePerLifetimeScope is no longer a suitable means of registration for this application- I now need to use InstancePerRequest.

As per the docs, InstancePerRequest actually uses InstancePerMatchingLifetimeScope under the hood. What I need is a reference to the child scope that is tagged with "AutofacWebRequest" that lives under the root scope. This is the scope that both my MVC and Web API controllers resolve their dependencies from, since they both use the same tag. So how do I get a reference to that particular scope? This was my solution, and I'd love to know if there's a better way to do it.

First off, I need to change my registration from

builder.Register(c => new MyDbContext()).AsSelf().InstancePerLifetimeScope();

to

builder.Register(c => new MyDbContext()).AsSelf().InstancePerRequest();

Now, after I've built my container I have the following method:

private void SetDependencyResolversForMvcAndWebApi(ILifetimeScope container)
{
    container.ChildLifetimeScopeBeginning += CaptureRequestLifetimeScope;
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}

The only part that wasn't there before is the event subscription. Everytime my container creates a child scope, my event handler gets called. The event handler looks like this:

private void CaptureRequestLifetimeScope(object sender, LifetimeScopeBeginningEventArgs args)
{
    if (args.LifetimeScope.Tag == MatchingScopeLifetimeTags.RequestLifetimeScopeTag)
    {
        //Get the ILifetimeScope created for components registered with InstancePerRequest
        var requestScope = args.LifetimeScope;

        //This is the same DbContext instance that will be injected into
        //my WebAPI and MVC controllers
        var context = requestScope.Resolve<MyDbContext>();
        //do the rest of my stuff
    }
}

I've tested this by holding onto a reference of the resolved DbContext that's resolved within the CaptureRequestLifetimeScope event handler and comparing it to the DbContext instances that are injected into my Web API and MVC controllers, and they are indeed pointing to the same object.

Sign up to request clarification or add additional context in comments.

1 Comment

I see but the problem is not the instance per lifetime. Instance per life time will work with this solution. But I suggest you to use instance per request, this way you will prevent your context resolve in root container or in an single instance. Beside of access external you solve your problem with internal access. I am still curious about external access.
0

Well, I think you should try changing .InstancePerLifetimeScope() into .InstancePerRequest();

As Autofac says about InstancePerLifetimeScope:

When you resolve the instance per lifetime scope component, you get a single instance per nested scope (e.g., per unit of work).

So when you do Resolve<MyDbContext>() you are probably doing it in a different Lifetime scope than you controller (I guess it's because you're doing an explicit resolution); that's why you're getting a diffent instance.

InstancePerRequest instead:

Some application types naturally lend themselves to “request” type semantics, for example ASP.NET web forms and MVC applications. In these application types, it’s helpful to have the ability to have a sort of “singleton per request.”

The begin phase of your request is already in the request phase, so you shoud get the same instance there and inside your controllers.

Instance per request builds on top of instance per matching lifetime scope by providing a well-known lifetime scope tag, a registration convenience method, and integration for common application types.

Based on this, probably, you can also go for the .InstancePerMatchingLifetimeScope("myrequest"), but you will have to manually instanciate a Lifetime scope everywhere like this using(var scope1 = container.BeginLifetimeScope("myrequest")); I think is not so practical.

Obviously I suppose you will not use those elements outside of the request scope, or you will get an exception. In that case, you are forced to the MatchingLifetimeScope.

If you need more details, the Autofac guide is extremely clear.

5 Comments

I had tried InstancePerRequest(), which under the hood uses InstancePerMatchingLifetimeScope() with a tag of "AutofacWebRequest". The problem is that in Application_BeginRequest that lifetime scope has yet to be created. gerrod has an excellent article here which has done a great job of clearing up when InstancePerLifetimeScope and InstancePerRequest are interchangeable and when they're not. Now all that remains is to figure out when the scope with the tag of "AutofacWebRequest" is created and thus accessible.
What about doing this in the beginRequest using(var scope1 = container.BeginLifetimeScope("AutofacWebRequest"))? I think this call will create the scope with that tag manually. Then, after that, Autofac should find the scope with that name already there and use it again giving you the same object as before. Just an idea but I'm with cell and I can't try myself right now :)
I haven't tried that, but it's my impression that this will create a child scope tagged "AutofacWebRequest" of whatever our current scope is then dispose of it, right? So this would actually be a different scope with the same tag as the one created by InstancePerRequest()
@joelmdev it happens as you told.
@ErkanDemirel I believe I've sorted it. see my answer
0
DependencyResolver.Current.GetService<MyDbContext>()));
AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<MyDbContext>()));

are same. They get current request lifetime scope. If you resolve some instance here, it shares it with cotrollers. (In this stiuation, instance per request and instance per lifetime will be same. Because their lifetime is same).

AutofacDependencyResolver.Current.ApplicationContainer.Resolve<MyDbContext>()));

This wil be resolved from root container. That's why it will create another instance for request lifetime scope. This will not be shared. And this will live in root container. (If you have per request instance, this will give error).

I have tested first one. It shares instance resolved in Application_BeginRequest with mvc controllers but not with Api controllers.

Then I tried to get GlobalConfiguration.Configuration.DependencyResolver.GetRequestLifetimeScope() in Application_BeginRequest it returns null.

I think, if it's api request, Autofac doesn't start request lifetime yet in Application_BeginRequest (May be it's related .net).

So if it's api request and if we can't reach autofac request life time. I don't know how to share this instance with mvc and api controllers which is resolved in Application_BeginRequest.

May be Travis can make it clear.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.