Разрешение компонентов Autofac в фоновых задачах в ASP.NET

Как использовать Autofac в ASP.NET вместе с ContainerDisposalModule, как я могу поддерживать вызовы fire и забудь о вызовах с зависимостями компонентов, которые необходимо разрешить? Проблема, с которой я сталкиваюсь, заключается в том, что запрос ASP.NET завершает и удаляет область действия запроса до запуска Задачи, поэтому любые компоненты, которые необходимо разрешить в новом потоке, завершаются с сообщением "Экземпляры не могут быть разрешены". и вложенные времена жизни не могут быть созданы из этого LifetimeScope, поскольку он уже был уничтожен ". Каков наилучший способ для поддержки огня и забыть о вызове с использованием Autofac в ASP.NET? Я не хочу откладывать запрос на выполнение определенных задач, которые могут быть выполнены в фоновых потоках.

2 ответа

Решение

Вам необходимо создать новую область действия, независимую от области действия запроса. В блоге ниже показан пример того, как сделать это с помощью MVC, но ту же концепцию можно применить к WebForms.

http://aboutcode.net/2010/11/01/start-background-tasks-from-mvc-actions-using-autofac.html

Если вам нужно убедиться, что асинхронная работа определенно выполнена после того, как запрос завершен, тогда это не очень хороший подход. В таких случаях я бы рекомендовал публиковать сообщение в очереди во время запроса, позволяя отдельному процессу его забрать и выполнить работу.

Ответ от Алекса, адаптированный к текущим версиям Autofac и MVC:

  • использование InstancePerRequest для контекста базы данных
  • добавлять ILifetimeScope как зависимость, чтобы добраться до контейнера
  • SingleInstance гарантирует, что это корневая область жизни
  • использование HostingEnvironment.QueueBackgroundWorkItem чтобы надежно запустить что-то в фоновом режиме
  • использование MatchingScopeLifetimeTags.RequestLifetimeScopeTag чтобы избежать необходимости знать тэг, который autofac использует для времени жизни PerRequest

https://groups.google.com/forum/ https://groups.google.com/forum/

Суть: 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);
            }
        });
    }
}

контроллер

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;
  }
}
Другие вопросы по тегам