c# - Autofac 为新线程创建子范围未按预期工作 "Instances cannot be resolved and nested lifetimes cannot be created..."

标签 c# .net dependency-injection autofac

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

如何让我的子作用域在并行线程中工作?

应用启动

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

Controller

    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

最佳答案

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

it has already been disposed.

using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
    _lifeTimeScopeChild = threadLifeTime;
    Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
    t.Start(id);  
}

threadLifeTime 是在 using block 的声明中创建的。在该 block 的末尾,它被处理掉。这是 using block 的唯一目的。有一个内置的假设,即此时可以处置对象。你已经完成了。

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

该线程继续独立于最初调用它的方法执行。因此,在您交出该对象后,它会立即被处理掉。无论该线程在做什么,它都会尝试使用已处置的对象来完成。那是错误。

通常在 using block 结束时,变量 (threadLifeTime) 会超出范围。不会有对它的引用,所以它是否被处置并不重要。但是现在有对它的引用,这是一个问题,因为它是对已处置的东西的引用。

短期解决方案是不创建单独的线程。如果有一个涉及多线程的正确答案,它会更复杂并且超出这个答案的范围。一个好的经验法则是首先让代码在没有多线程的情况下工作,然后在需要时添加它。否则你不知道问题是多线程还是别的什么。

另一个问题是:

_lifeTimeScopeChild = threadLifeTime;

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

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

如果您的方法需要它,则在方法中声明并使用它,但不允许任何您无法控制的对它的引用从方法中“转义”。如果你处置它,那么你会破坏任何其他试图使用它的东西。如果您不处置它,那么它就不会在应该处置的时候处置。

如果您的类需要它,那么请考虑在创建类时创建它,或者在需要时使用惰性实例化来创建它。 (我会从前者开始。)然后让你的类实现 IDisposable,当你的类被释放时,也就是你释放你的类使用的任何一次性资源的时候。

关于c# - Autofac 为新线程创建子范围未按预期工作 "Instances cannot be resolved and nested lifetimes cannot be created...",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55761962/

相关文章:

c# - 我无法让我的扩展方法工作 (C#)

c# - 对 EF-to-Linq 查询进行排序的最快方法是什么?

angularjs - Angular ngCookies 错误 : [$injector:modulerr]

c# - Autofac IoC GetInstance 方法

c# - Windows .NET 更新后最后一位数字的四舍五入发生变化

c# - 删除 List<object> 的重复对

.net - 如何使用反射检测字段上的 "new"修饰符?

c# - 以无边界形式绘制夹具

.net - 为什么不总是使用泛型?

c# - 根据程序集使用 Simple Injector 注入(inject)不同的依赖项