Внедрение свойства / метода с использованием 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: метод впрыска выше приветствуется.

2 ответа

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

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

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

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

С методом инъекции, ваш 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();
    }
}

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

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

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);
    }
}

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

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

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

Другие вопросы по тегам