zoukankan      html  css  js  c++  java
  • Autofac not working with background tasks

    Autofac not working with background tasks

    I recently implemented something similar using help from this answer and this answer. You need to create a new lifetime scope - it sounds like your doing this in a web application, so you need to create the scope via the per-request tag (example below).

    Another (non-StackOverflow) answer provides similar advice.

    public Task Run<T>(Action<T> action)
    {
        Task.Factory.StartNew(() =>
        {
            using (var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
            {
                var service = lifetimeScope.Resolve<T>();
                action(service);
            }
        });
        return Task.FromResult(0);
    }
    

    评论:

     
     

    Resolving Autofac components in background Tasks in ASP.NET

    回答1

    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;
      }
    }
    

    回答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:

    1. 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();
      
    2. 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
      
    3. 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>>();
      }
      
  • 相关阅读:
    【转载】Scarbee Pre-Bass 贝司的使用教程
    罗兰管弦乐音色表【中英文对照】 ----转载
    快速查询
    免费好用的Noto字体
    用了一年多之后才搞懂阿里云OSS收费细则
    “生成能够被扫描枪正常扫描出中文的二维码”
    .NET Core 3.0正式版发布
    快速删除一个“大目录”
    WSL2(预览版)体验笔记
    局域网地址为什么是192.168.X.X?为什么连上公司的VPN就上不了网?
  • 原文地址:https://www.cnblogs.com/chucklu/p/15427947.html
Copyright © 2011-2022 走看看