【问题标题】:Autofac instance per lifetime scope only on child scope, not on root每个生命周期范围内的 Autofac 实例仅在子范围内,而不是在根范围内
【发布时间】:2018-01-26 19:31:09
【问题描述】:

我在我的应用程序中使用 autofac 并努力让这种情况正常工作。

我将我的类型注册为 PerLifetimeScope,但此规则也适用于根容器。

我有一个实例,它应该只为在子 LifetimeScope 内完成的请求共享,但应该总是为直接完成到根容器的请求返回一个新实例。


为了更好地描述:

我有一个使用 autofac 来解决依赖关系的移动应用程序。

我为应用程序构建了一个全局容器。当用户导航到新页面时,我会从该页面的容器中打开一个新范围。

页面可以使用 EntityFrameworkCore 访问内部 sqlite 数据库。用于访问数据库的 DbContext 实例对于页面范围内的所有依赖项应该是相同的。

我还有一个 ConfigurationsService 类,我像字典(键/值)一样使用它,但也存储在数据库中。 此类应在页面的任何范围之外在数据库中执行更新,而不是与页面所做的其他更改交互。

当单个配置发生更改时,它会请求 DbContext 的一个实例,进行更改并释放该实例。

这里的问题是,当 ConfigurationService 从容器(不是作用域)请求 DbContext 实例时,DbContext 现在将是一个共享实例,并且连接将保持活动状态,直到容器被释放。

我可以为此打开一个作用域,但它会为处于相同情况的每个类留下作用域生命周期的控制权。

更新 1:

下面的代码很好地代表了依赖层次和我上面描述的情况。

// CLASSES
public class Context
{
}

public class Repository
{
    public Repository(Context context)
    {
        Context = context;
    }

    public Context Context { get; }
}

public class ViewModel
{
    public ViewModel(Repository repository1, Repository repository2)
    {
        Repository1 = repository1;
        Repository2 = repository2;
    }

    public Repository Repository1 { get; }
    public Repository Repository2 { get; }
}

// SAMPLE
var repository1 = container.Resolve<Repository>();
var repository2 = container.Resolve<Repository>();
var viewModel = container.Resolve<ViewModel>();
  1. repository1 和 repository2 变量的上下文引用应该是不同的实例。
  2. viewModel.Repository1 和 viewModel.Repository2 的上下文引用应该是同一个实例。

ViewModel 是可能具有类似依赖层次结构的众多类之一。

我可以让它在我的情况下工作的方法是创建一个服务,该服务仅在非根范围内将实例作为单例进行管理。

// CLASSES
public class ScopeSingletons
{
    private readonly List<object> _instances = new List<object>();

    public T Get<T>(Func<T> factory)
    {
        var instance = _instances.OfType<T>().SingleOrDefault();

        if (instance == null)
            _instances.Add(instance = factory());

        return instance;
    }
}

// SAMPLE
var builder = new ContainerBuilder();

builder.RegisterType<ScopeSingletons>().AsSelf().InstancePerLifetimeScope();
builder.RegisterType<ViewModel>().AsSelf();
builder.RegisterType<Repository>().AsSelf();
builder.Register<Context>(c =>
{
    if (Equals((c as IInstanceLookup)?.ActivationScope.Tag, "root"))
        return new Context();

    return c.Resolve<ScopeSingletons>().Get(() => new Context());
});

var container = builder.Build();
var scope = container.BeginLifetimeScope();

var repository1 = container.Resolve<Repository>();
var repository2 = container.Resolve<Repository>();
var viewModel = scope.Resolve<ViewModel>();

在这种情况下,一切都会按照我的需要进行。上下文解析对于根范围内的每个请求都是唯一的,并在子范围内共享。

【问题讨论】:

    标签: c# autofac


    【解决方案1】:

    这是...有点复杂的事情,并且在某种程度上暗示可能存在一些设计问题。根据我自己的经验,每当我发现我想做 X 除了这个我想要 Y 的奇怪情况时,这意味着我设计了错误的东西。

    但假设这是你需要的。

    从“要求”看来,ConfigurationServices 用法...

    • 需要控制自己的DbContext实例的创建和处置
    • 通常是单例或以其他方式存在于根容器级别,因为它在范围创建机制的上下文之外使用。
    • 如果在“页面”内解析,则不必与页面“共享”DbContext。 (它可以,但它没有必须。)

    Sounds like a prime use case for the Owned&lt;T&gt; relationship.

    我会这样设置...

    • ConfigurationServices 是一个无状态的单身人士。
    • ConfigurationServices 中需要上下文时,它会处理上下文的解析、执行和清理。

    看起来像这样:

    public class ConfigurationServices
    {
      Func<Owned<DbContext>> _contextFactory;
      public ConfigurationServices(Func<Owned<DbContext>> contextFactory)
      {
        this._contextFactory = contextFactory;
      }
    
      public void DoWork()
      {
        using(var context = this._contextFactory().Value)
        {
          context.DoSomethingInDatabase();
        }
      }
    }
    

    Func&lt;T&gt; 关系处理动态解析Owned&lt;T&gt; 的全新实例。 Owned&lt;T&gt; 部分表示容器不会在处置时持有对它的引用,而是让控制处置。

    在这种情况下,ConfigurationServices 将是一个无状态的单例,你可以像这样注册它。

    builder.RegisterType<ConfigurationServices>()
           .SingleInstance();
    

    无论它在哪里得到解决,它都来自根容器范围(所有单例都这样做),这意味着 Func&lt;Owned&lt;DbContext&gt;&gt; 也将来自那里......但因为你使用的是 Owned&lt;T&gt; 你会' t 有无意的内存泄漏。容器不会保存要丢弃的引用。

    【讨论】:

    • 我尝试使用 Owned 关系,但很难注册类型,以便它可以与我的依赖层次结构一起使用。我用示例代码更新了问题。我认为我的情况很像 MVC 应用程序的 InstancePerRequest 配置。
    猜你喜欢
    • 2021-02-17
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    相关资源
    最近更新 更多