【问题标题】:Autofac: creating nested scopes per instance on-the-flyAutofac:即时为每个实例创建嵌套范围
【发布时间】:2018-10-10 16:33:23
【问题描述】:

我想为用户创建的每个项目实现一个应用程序范围的容器和一个(嵌套的)容器。我查看了Owned<T>,但随后-据我所知-我的内部项目集合必须是<Owned<Project>>,这是我不想要的,而且我未能将项目依赖项注入到内部使用的对象中项目范围(“循环组件依赖”)。我考虑在项目工厂中使用新的 ContainerBuilder,但后来缺少“嵌套”方面。

我想要一些类的例子(带有依赖项):

  • 在全球范围内:ProjectManager(IProjectFactory)
  • 在每个项目的范围内:Project(IDocumentFactory documentFactory)Document(IProject project, IProjectSettings settings)

因此,对于项目范围,我将注册 IDocumentFactoryIProjectSettings(以及项目本身?)。

当一个项目被关闭/释放时,所有创建的依赖项当然也应该被释放。

如果可能,具体类(ProjectFactory 除外)应该与 Autofac 无关。

仅供参考:该应用程序是使用 C# 和 Autofac 4.8 的桌面应用程序。

谢谢!

更新:感谢您的 cmets,讨论帮助我找到了自己的意见。目前我正在我的ProjectFactory 中解决这样的问题:

    public Project Create()
    {
        var scope = _globalScope.BeginLifetimeScope(MyIocHelper.RegisterProjectDependencies);

        var p = scope.Resolve<Project>();

        _projectScopes.Add(p, scope);
        p.Disposing += project_Disposing;

        return p;
    }

注意事项:

  • 据我所知,没有必要在生命周期范围内使用标签。
  • Project 第一次调用其 Dispose 方法时会引发 Disposing 事件。
  • 工厂保留Dictionary&lt;Project, ILifetimeScope&gt;,并在项目处置时清理。

【问题讨论】:

  • 实际上,我突然想到,您可能希望在项目中拥有一些文档集合并在某处使用它,例如,在 UI 上向用户显示文档名称。在这种情况下,我建议不要使用 autofac 来管理文档。 Container 没有提供范围内当前可用文档列表的功能,因此您必须在项目中跟踪它们。但是现在您正在引入生命周期管理的冲突 - 现在项目和容器关心文档对象的生命周期,谁应该负责处置它们?
  • 所以,请澄清这个系统的非常高级的功能,以便我们了解所涉及实体的生命周期。最好不要提供您对实现的想法,因为如果没有先解决设计问题,它将没有用处。
  • 我了解您关于冲突处置链的想法,并且正在考虑仅对工厂使用 autofac - 也是因为我期望解决每个实例(文档和嵌套项)的依赖关系会带来性能损失.

标签: c# autofac ioc-container


【解决方案1】:

您可以通过组合命名生命周期范围和实例每生命周期范围注册来完成您正在寻找的事情。

此处的文档:http://autofac.readthedocs.io/en/latest/lifetime/working-with-scopes.html#tagging-a-lifetime-scope

你需要:

  1. 将您的 ProjectManager 注册为 SingleInstance
  2. 这样注册项目:

         builder.Register<Project>()
            .As<IProject>()
            .InstancePerMatchingLifetimeScope("project");
    

    这将保证每个标记为“项目”的范围都可以解析一次项目(例如通过文档)。

  3. 在 ProjectManager 中实现一个 OpenProject(或类似的)方法。此方法应实例化一个标记为“项目”的 LifetimeScope,在其中注册 IDocumentFactoryIProjectSettings,因此每个项目范围仅解析一次,并将范围本身附加到项目实例上。 这很关键:你需要在处置项目时处置范围。

    public class ProjectManager : IProjectFactory
    {
        private readonly ILifetimeScope _scope;
    
        public ProjectManager(ILifetimeScope scope)
        {
            // this is going to be the global scope.
            _scope = scope;
        }
    
    
        public Project OpenProject(IDocumentFactory docFactory, IProjectSettings settings)
        {
            var projectScope = _scope.BeginLifetimeScope("project");
            projectScope.RegisterInstance(docFactory).AsImplementedInterfaces();
            projectScope.RegisterInstance(settings).AsImplementedInterfaces();
    
            return projectScope.Resolve<Project>();
        }
    }
    
    public class ProjectScope : IDisposable
    {
        private readonly ILifetimeScope _scope;
    
        public ProjectManager(ILifetimeScope scope)
        {
            // this is going to be the project scope.
            _scope = scope;
        }
    
        public void Dispose() {
            if (_scope != null) {
                _scope.Dispose();
                _scope = null;
            }
        }
    }
    
    public class Project : IDisposable
    {
        private readonly ProjectScope _scope; 
    
        public Project(ProjectScope scope /*, ...*/)
        {
            _scope = scope;
        }
    
        public void Dispose() {
            // pay attention that this method will be called 2 times, once by you
            // and another time by the underlying LifetimeScope. So this code should
            // handle that gracefully (so the _scope == null).
            if (_scope != null) {
                _scope.Dispose();
                _scope = null;
            }
        }
    }
    

考虑到这一切,您将每个 using Autofac 排除在每个班级之外,除了全局管理器和 ProjectScope 这两个例外。如果您在 Project 类本身中接受单个 using Autofac,则可以更改有关如何处理范围的一些位:您可以直接获取 ILifetimeScope 并直接处理它。

希望这会有所帮助!

【讨论】:

  • ProjectScope 似乎不正确。它提供了 external 生命周期范围,然后该范围被 ProjectScope 对象 显式处理 - 这不是它应该做的。作用域不是由ProjectScope 对象拥有,因此它不知道何时是终止作用域的正确时间。这很可能会破坏 autofac 的范围管理,并导致应用程序中出现难以理解的错误。但总的来说,我会说你的答案指向了正确的方向。
  • 这个想法是将项目的处置级联到包含它的 LifetimeScope。 Ence,我从 ProjectScope 处理 Autofac 的 LifetimeScope。我知道这与某人通常会做的相反,但我认为这没有错。简单地说,处置项目将自动处置包含在封闭“项目”范围内的所有其他对象。我错了吗?
  • 是的,你错了。您绝对不应该处置您没有开始的生命周期范围。在任何情况下。此外,处置项目应该意味着它也通过杀死范围来完成 - 这将负责杀死它下面的所有内容,而无需您做任何事情。
  • 如果一个。 chiesa 的建议是“次优的”,什么是更好的解决方案?我的要求是牵强附会还是方向错误?
  • 好吧,即使我认为(作为个人观点)亚历山大的立场有点过于教条,但他提出的观点是正确的。您应该确保项目范围内的对象没有被外部引用(或者当项目范围处置时,您将引用已处置的对象)。作为替代方案,您可以直接在 Project 类上实现范围实例化和管理的逻辑。在这种情况下,您将负责管理 LifetimeScope 的处置给实例化它的同一个类,从而保持代码“更紧凑”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 2016-03-02
  • 1970-01-01
相关资源
最近更新 更多