Autofac为新线程创建子作用域无法按预期方式工作“无法解析实例并且无法创建嵌套生命周期...”

时间:2019-04-19 12:35:20

标签: c# .net dependency-injection autofac

当有人调用URL“ / index / 5”时,我想返回一个视图,同时我想启动一个新线程,在其中我想通过一些数据库调用和业务逻辑来确定是否应该发送别人的通知。这是我的设置的简化表示,但出现错误。

如何让我的子范围在并行线程中工作?

应用启动

var builder = new ContainerBuilder();
builder.RegisterType<MyRepository>().As<IMyRepository();
builder.RegisterType<Entities>().As<Entities>().InstancePerRequest();
var container = builder.Build();

控制器

    private readonly IMyRepository _myRepository ;

    public MyController(
        IMyRepository myRepository
       )
    {
        _myRepository = myRepository;
    }

    public async Task<ActionResult> Index(int id)
    {
        _myRepository.DoSomething(id);

        return View();
    }

存储库:

private ILifetimeScope _lifeTimeScopeChild = null;

public void DoSomething(int id){
            //start new thread with child scope
            using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
            {
                _lifeTimeScopeChild = threadLifeTime;
                 Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
                 t.Start(id);  
            }
        }

        private void MySeparateThread(object id) {
                    var _entities = _lifeTimeScopeChild.Resolve<Entities>(); //IT CRASHES HERE
        }

错误:

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

我要完成的工作:https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#thread-scope

1 个答案:

答案 0 :(得分:3)

关键部分不是错误消息的第一部分,而是最后一部分:

  

它已经被处置了。

using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
    _lifeTimeScopeChild = threadLifeTime;
    Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
    t.Start(id);  
}
在您的threadLifeTime块的声明中创建了

using。在该块的末尾将其丢弃。这是using块的唯一目的。有一个假设是,此时可以放置该对象。您已经完成了。

您还将创建一个单独的线程并将threadLifeTime传递给该线程。没有显示发生这种情况的代码部分,但这就是发生的情况。您可能没有明确传递它,但是在ParameterizedThreadStartMySeparateThread中引用了它。

该线程继续与最初调用它的方法分开执行。因此,在您移交该对象后,该对象将立即处置。无论该线程在做什么,它都在尝试使用已处置的对象。那是错误。

通常在using块的末尾,变量(threadLifeTime)将超出范围。不会有任何引用,因此,将其丢弃也无关紧要。但是现在有一个引用,这是一个问题,因为它是引用已处置的东西。

短期解决方案是不创建单独的线程。如果对此有一个正确的答案涉及多线程,那么它将更加复杂并且超出了答案的范围。一个好的经验法则是让代码无需多线程就可以工作,然后在需要时添加它。否则,您不知道问题是否出在多线程或其他方面。

另一个问题是:

_lifeTimeScopeChild = threadLifeTime;

它表示不仅将其传递给其他线程,还将其分配给类中的字段。出于完全相同的原因,这也是一个问题。即使在对象被处置后,您分配给该字段的引用仍然存在。如果在此_lifeTimeScopeChild块完成之后有任何尝试使用using的地方,它将得到相同的错误。

要回答的问题是“我的方法需要这个对象还是我的 class 需要这个对象?”

如果您的方法需要它,则在方法中声明并使用它,但是不允许您无法控制的对它的任何引用从方法中“转义”。如果处置它,则将破坏尝试使用它的其他任何功能。而且,如果您不进行处理,那么它就不会在应有的时间处理。

如果您的类需要它,则可以考虑在创建类时创建它,或者在需要时使用延迟实例化来创建它。 (我将从前者开始。)然后使您的类实现IDisposable,并在处置您的类时,即处置该类使用的任何可使用资源。