Вы должны запретить вашему контейнеру создавать объекты, ориентированные на данные, например, объект UserAccount
.Это приводит к сложным сценариям, таким как тот, в котором вы сейчас находитесь.
В общем случае ваш DI-контейнер должен разрешать только компоненты - это классы в вашей системе, которые содержат поведение приложениябез какого-либо интересного состояния.Эти типы классов, как правило, долго живут или, по крайней мере.Объекты, ориентированные на данные, как и объекты, лучше всего создавать вручную.Невыполнение этого требования может привести к появлению сущностей с большими конструкторами, что легко вызывает запах кода избыточного внедрения конструктора.В качестве исправления вы можете прибегнуть к использованию Property Injection, но это вызывает собственный запах кода, вызывающий Temporal Coupling .
Вместо этого, лучшим решением будет:
- Создание сущностей вручную, в отличие от использования контейнера DI
- Предоставление зависимостей для сущности с помощью инъекции метода, в отличие от использования инъекции свойства
С инъекцией метода, ваш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 на непосредственного потребителя сущности.Но, как обсуждалось выше, это сделано намеренно, поскольку корень композиции в целом и контейнер 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);
}
}