Autofac: создание вложенных областей для каждого экземпляра на лету - PullRequest
0 голосов
/ 30 апреля 2018

Я хотел бы реализовать контейнер для всего приложения и (вложенный) контейнер для каждого проекта, созданного пользователем. Я изучил Owned<T>, но затем - насколько я мог понять - моя внутренняя коллекция проектов должна была бы быть <Owned<Project>>, что мне не нужно, а также я не смог внедрить зависимость проекта в объекты, используемые в Объем проекта («циклическая зависимость компонента»). Я подумал об использовании нового ContainerBuilder в фабрике проекта, но тогда «вложенный» аспект отсутствует.

Несколько примеров классов (с зависимостями), которые я хотел бы получить:

  • В глобальном масштабе: ProjectManager(IProjectFactory)
  • В рамках каждого проекта: Project(IDocumentFactory documentFactory), Document(IProject project, IProjectSettings settings).

Так что для охвата проекта я бы зарегистрировал IDocumentFactory, IProjectSettings (и сам проект?).

Когда проект закрывается / удаляется, все созданные зависимости должны, конечно, также удаляться.

Если возможно, конкретные классы (за исключением ProjectFactory) должны быть независимыми от автофактов.

К вашему сведению: приложение представляет собой настольное приложение, использующее C # и Autofac 4.8.

Спасибо!

ОБНОВЛЕНИЕ: Спасибо за ваши комментарии, обсуждение помогло мне найти собственное мнение. В настоящее время я согласен на что-то вроде этого в моем 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 вызывает событие Disposing, когда его метод Dispose вызывается впервые.
  • Фабрика хранит Dictionary<Project, ILifetimeScope> и очищает его при утилизации проекта.

1 Ответ

0 голосов
/ 30 апреля 2018

Вы можете выполнить то, что ищете, с помощью комбинации именованных областей действия и регистрации экземпляра на весь срок действия.

Документация здесь: 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. Реализация метода OpenProject (или чего-то похожего) в ProjectManager. Этот метод должен создавать экземпляр LifetimeScope, помеченный как «проект», регистрировать в нем IDocumentFactory, IProjectSettings, чтобы они разрешались только один раз для каждой области проекта, и присоединять саму область к экземпляру Project. Это очень важно : вам необходимо ликвидировать область видимости при утилизации проекта.

    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 из каждого класса, за исключением 2 глобальных менеджеров и ProjectScope. Вы можете изменить некоторые биты в том, как обрабатывается область, если вы примете один using Autofac в самом классе Project: вы можете получить непосредственно ILifetimeScope и утилизировать его напрямую.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...