Внедрение свойств/методов с использованием Autofac в атрибутах фильтра

Попытка использовать autofac для внедрения зависимостей по свойству.

Экземпляр всегда нулевой, и зависимость не внедряется. Ниже приведен класс, в который необходимо внедрить свойство.

public class UserAccount
{
    public IAccountService AccountService { get; set; }

    public string Message()
    {
        return AccountService.Message();
    }
}

Я пробовал три разных способа внедрить свойство, но ни один из них не увенчался успехом.

Способ 1:

builder.Register(c => {
                var result = new UserAccount();
                var dep = c.Resolve<IAccountService>();
                result.SetDependency(dep);
                return result;
            });

Способ 2:

builder.RegisterType<UserAccount>().PropertiesAutowired();

Способ 3:

builder.Register(c => new UserAccount { AccountService = c.Resolve<IAccountService>()});

PS: метод инъекции выше приветствуется.


person Manti_Core    schedule 22.11.2018    source источник
comment
Объект UserAccount тоже создан Autofac? Если вы создадите его вручную (используя оператор new), он не будет работать, потому что Autofac не участвует в создании объекта.   -  person KBO    schedule 22.11.2018
comment
@KBO да, я создаю его вручную, используя новый, что мне тогда делать?   -  person Manti_Core    schedule 22.11.2018
comment
Пожалуйста, покажите код, который показывает, как вы разрешаете и используете UserAccount.   -  person Steven    schedule 22.11.2018
comment
Возможный дубликат: stackoverflow.com/questions/29915192/   -  person Steven    schedule 22.11.2018
comment
Где атрибут фильтра, упомянутый в заголовке вопроса? Какая структура является атрибутом фильтра?   -  person Travis Illig    schedule 22.11.2018


Ответы (2)


Вы не должны позволять контейнеру создавать объекты, ориентированные на данные, такие как ваша сущность UserAccount. Это приводит к сложным сценариям, таким как тот, в котором вы находитесь сейчас.

Как правило, ваш DI-контейнер должен разрешать только компоненты — это классы в вашей системе, которые содержат поведение приложения, но не имеют какого-либо интересного состояния. Эти типы классов обычно живут долго или, по крайней мере, дольше, чем объекты, ориентированные на данные.

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

Вместо этого лучшим решением является:

  1. Создание объектов вручную вместо использования контейнера внедрения зависимостей
  2. Предоставление зависимостей объекту с помощью внедрения методов, а не внедрения свойств.

С внедрением методов ваш UserAccount будет выглядеть следующим образом:

// This answer assumes that this class is an domain entity.
public class UserAccount
{
    public Guid Id { get; set; }
    public byte[] PasswordHash { get; set; }

    public string Message(IAccountService accountService)
    {
        if (accountService == null)
            throw new ArgumentNullException(nameof(accountService));

        return accountService.Message();
    }
}

Это переносит ответственность за предоставление зависимости с Composition Root для прямого потребителя сущности. Но, как обсуждалось выше, это делается намеренно, поскольку корень композиции в целом и контейнер внедрения зависимостей в частности не должны нести ответственность за создание сущностей и других недолговечных объектов, ориентированных на данные.

Однако это означает, что непосредственный потребитель UserAccount должен внедрить эту зависимость, а вместе с ней и знать о существовании зависимости. Но поскольку этот потребитель будет классом, ориентированным на поведение, типичным решением будет использование внедрения конструктора на этом этапе:

public class UserService : IUserService
{
    private readonly IAccountService accountService;
    private readonly IUserAccountRepository repo;

    public UserService(IAccountService accountService, IUserAccountRepository repo)
    {
        this.accountService = accountService;
        this.repo = repo
    }

    public void DoSomething(Guid id)
    {
        UserAccount entity = this.repo.GetById(id);
        var message = entity.Message(this.accountService);
    }
}
person Steven    schedule 22.11.2018
comment
Но это будет означать, что я должен сделать ссылку на бизнес-уровень в моем уровне представления, чтобы получить доступ к этому репозиторию... это нормально? Я имею в виду, не нарушит ли это разделение слоев? В настоящее время уровень представления знает только о сервисном уровне. - person Manti_Core; 22.11.2018
comment
Это, вероятно, означает, что вы разместили свои абстракции не в том месте, потому что ваш уровень представления не должен зависеть от бизнес-уровня, если ему нужно использовать только свои абстракции. Ваш комментарий также подразумевает, что ваш уровень представления напрямую вызывает UserAccount.Message, что было бы нарушением разделения уровней, поскольку уровень представления обычно не должен вызывать методы предметной области напрямую, а делать это только через взаимодействующий уровень. - person Steven; 22.11.2018
comment
До сих пор я успешно использовал DI и использовал его в своих контроллерах, но есть сценарий, в котором я должен использовать службы в своих пользовательских фильтрах mvc, и для этого мне нужно уменьшить параметр фильтров, и поэтому я создал это класс с именем UserAccount для обработки служб моего фильтра. Мы пытаемся создать базу для проекта. - person Manti_Core; 22.11.2018
comment
Аааа, хорошо. Итак, вы говорите, что UserAccount является не сущностью, а компонентом. В таком случае мой ответ бесполезен. Вместо этого ваш вопрос кажется дубликатом этого q/a. - person Steven; 22.11.2018
comment
То есть отсутствие внедрения зависимостей для атрибутов выглядит как невидимый тупик? Но есть несколько других статей, в которых упоминаются различные методы внедрения фильтра, особенно с помощью ninject. Думаю, я пойду по старой школе, здесь ничего особенного :) - person Manti_Core; 22.11.2018

Используя способ 3, вам нужно зарегистрировать AccountService, т.е.

        builder.RegisterType<AccountService>().As<IAccountService>();
        builder.Register(c => new UserAccount { AccountService = c.Resolve<IAccountService>()});

И когда вы используете UserAccount, убедитесь, что он создан с помощью Autofac.

person Phillip Ngan    schedule 22.11.2018