Answer posted by Alex adapted to current Autofac and MVC versions:
- Use
InstancePerRequest
for a database context
- Add
ILifetimeScope
as dependency to get to the container
SingleInstance
ensures it's the root lifetime scope
- Use
HostingEnvironment.QueueBackgroundWorkItem
to reliably run something in the background
- Use
MatchingScopeLifetimeTags.RequestLifetimeScopeTag
to avoid having to know the tagname autofac uses for PerRequest lifetimes
https://groups.google.com/forum/#!topic/autofac/gJYDDls981A https://groups.google.com/forum/#!topic/autofac/yGQWjVbPYGM
Gist: https://gist.github.com/janv8000/35e6250c8efc00288d21
Global.asax.cs:
protected void Application_Start() {
//Other registrations
builder.RegisterType<ListingService>();
builder.RegisterType<WebsiteContext>().As<IWebsiteContext>().InstancePerRequest(); //WebsiteContext is a EF DbContext
builder.RegisterType<AsyncRunner>().As<IAsyncRunner>().SingleInstance();
}
AsyncRunner.cs
public interface IAsyncRunner
{
void Run<T>(Action<T> action);
}
public class AsyncRunner : IAsyncRunner
{
public ILifetimeScope LifetimeScope { get; set; }
public AsyncRunner(ILifetimeScope lifetimeScope)
{
Guard.NotNull(() => lifetimeScope, lifetimeScope);
LifetimeScope = lifetimeScope;
}
public void Run<T>(Action<T> action)
{
HostingEnvironment.QueueBackgroundWorkItem(ct =>
{
// Create a nested container which will use the same dependency
// registrations as set for HTTP request scopes.
using (var container = LifetimeScope.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
{
var service = container.Resolve<T>();
action(service);
}
});
}
}
Controller
public Controller(IAsyncRunner asyncRunner)
{
Guard.NotNull(() => asyncRunner, asyncRunner);
AsyncRunner = asyncRunner;
}
public ActionResult Index()
{
//Snip
AsyncRunner.Run<ListingService>(listingService => listingService.RenderListing(listingGenerationArguments, Thread.CurrentThread.CurrentCulture));
//Snip
}
ListingService
public class ListingService : IListingService
{
public ListingService(IWebsiteContext context)
{
Guard.NotNull(() => context, context);
Context = context;
}
}
You need to create a new lifetime scope that is independent of the request lifetime scope. The blog post below shows an example of how to do this using MVC but the same concept can be applied to WebForms.
http://aboutcode.net/2010/11/01/start-background-tasks-from-mvc-actions-using-autofac.html
If you need to ensure that the async work is definitely performed after the request is finished then this is not a good approach. In such cases I would recommend posting a message onto a queue during the request allowing a separate process to pick it up and perform the work.
There are several ways of how you can do that:
-
The best one in my opinion. You can register the repository as InstancePerLifetimeScope as you said. It works with HttpRequests and LifetimeScopes equally well.
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope();
-
Your registration for HttpRequest may differ from registration for LifetimeScope, then you can have two separate registrations:
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.WithParameter(...)
.InstancePerHttpRequest(); // will be resolved per HttpRequest
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope(); // will be resolved per LifetimeScope
-
You can explicitly create "HttpRequest"
scope using its tag. Exposed through MatchingScopeLifetimeTags.RequestLifetimeScopeTag
property in new versions.
using (var httpRequestScope = container.BeginLifetimeScope("httpRequest")) // or "AutofacWebRequest" for MVC4/5 integrations
{
var repository = httpRequestScope.Resolve<IRepository<Entity>>();
}
Resolving Autofac components in background Tasks in ASP.NET
回答1
Answer posted by Alex adapted to current Autofac and MVC versions:
InstancePerRequest
for a database contextILifetimeScope
as dependency to get to the containerSingleInstance
ensures it's the root lifetime scopeHostingEnvironment.QueueBackgroundWorkItem
to reliably run something in the backgroundMatchingScopeLifetimeTags.RequestLifetimeScopeTag
to avoid having to know the tagname autofac uses for PerRequest lifetimeshttps://groups.google.com/forum/#!topic/autofac/gJYDDls981A https://groups.google.com/forum/#!topic/autofac/yGQWjVbPYGM
Gist: https://gist.github.com/janv8000/35e6250c8efc00288d21
Global.asax.cs:
AsyncRunner.cs
Controller
ListingService
回答2
You need to create a new lifetime scope that is independent of the request lifetime scope. The blog post below shows an example of how to do this using MVC but the same concept can be applied to WebForms.
http://aboutcode.net/2010/11/01/start-background-tasks-from-mvc-actions-using-autofac.html
If you need to ensure that the async work is definitely performed after the request is finished then this is not a good approach. In such cases I would recommend posting a message onto a queue during the request allowing a separate process to pick it up and perform the work.
Configure Autofac Container for background thread
There are several ways of how you can do that:
The best one in my opinion. You can register the repository as InstancePerLifetimeScope as you said. It works with HttpRequests and LifetimeScopes equally well.
Your registration for HttpRequest may differ from registration for LifetimeScope, then you can have two separate registrations:
You can explicitly create
"HttpRequest"
scope using its tag. Exposed throughMatchingScopeLifetimeTags.RequestLifetimeScopeTag
property in new versions.