Внедрение свойства / метода с использованием 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, но это вызывает собственный запах кода, вызывающий временную связь.
Вместо этого, лучшим решением является:
- Создание объектов вручную, в отличие от использования контейнера DI
- Предоставление зависимостей для объекта с использованием метода 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.