Unity: Инъекция нескольких зарегистрированных типов PerResolveLifetimeManager

Я использую репозитории, которые используют единицу шаблона работы:

public BaseRepository(IUnitOfWork unitOfWork, IEntityFactory factory) { ... }

Раньше мне требовался только один экземпляр IUnitOfWork, введенный в репозитории (используя Unity), как показано ниже:

// Unit of work for the UserDbContext
container.RegisterType<iunitofwork, entityframeworkunitofwork="">(new PerResolveLifetimeManager(), new InjectionConstructor(new UserDbContext()));
container.RegisterType<iuserrepository, userrepository="">();
container.RegisterType<itokenrepository, tokenrepository="">();
</itokenrepository,></iuserrepository,></iunitofwork,>

Теперь мне нужно ввести другой репозиторий, но этот репозиторий должен использовать другой экземпляр IUnitOfWork:

// Unit of work for the OrderDbContext
container.RegisterType<iunitofwork, entityframeworkunitofwork="">(new PerResolveLifetimeManager(), new InjectionConstructor(new OrderDbContext()));
container.RegisterType<iorderrepository, orderrepository="">();
</iorderrepository,></iunitofwork,>

Как я могу использовать Unity для объяснения, указать, какой IUnitOfWork будет введен в какой-либо репозиторий?

EDIT:

Используя ответ Daniel J.G, у меня есть следующий код:

container.RegisterType<iunitofwork, entityframeworkunitofwork="">(new PerResolveLifetimeManager(), new InjectionConstructor(new UserDbContext()));
container.RegisterType<iunitofwork, entityframeworkunitofwork="">("OrderDbContext", new PerResolveLifetimeManager(), new InjectionConstructor(new OrderDbContext()));
container.RegisterType<iuserrepository, userrepository="">();
container.RegisterType<itokenrepository, tokenrepository="">();
container.RegisterType<iorderrepository, orderrepository="">(
 new InjectionConstructor(
 new ResolvedParameter<iunitofwork>("OrderDbContext"),
 new ResolvedParameter<ientityfactory<order, int,="" ordertbl="">>()
 )
);
</ientityfactory<order,></iunitofwork></iorderrepository,></itokenrepository,></iuserrepository,></iunitofwork,></iunitofwork,>

Но возникает следующее исключение:

[ResolutionFailedException: сбой разрешения зависимостей, введите = "WebTest.Controllers.TestController", name = "(none)". Исключение произошло во время: при разрешении. Исключение составляет: InvalidOperationException - тип IUnitOfWork не имеет доступного конструктора.

Конкретная реализация UserRepository:

public class UserRepository : EntityFrameworkRepository<user, usertbl="">, IUserRepository
{
 public UserRepository(IUnitOfWork unitOfWork, IEntityFactory<user, usertbl=""> factory)
 : base(unitOfWork, factory)
 { }
}
</user,></user,>

Я также регистрирую предприятия-сущности. То есть:

container.RegisterType<ientityfactory<user, int,="" usertbl="">, UserFactory>();
</ientityfactory<user,>

Конструктор EntityFrameworkUnitOfWork:

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
 public EntityFrameworkUnitOfWork(DbContext context)
 {
 Context = context;
 }
 ...
}
1 ответ

Вы можете использовать named registration > , чтобы вы могли регистрировать и разрешать различные варианты IUnitOfWork.

Unity представляет собой единую неназванную регистрацию (которая является стандартной по умолчанию) и столько же именных регистраций, сколько вам нужно. Таким образом, вы можете оставить одну из регистраций IUnitOfWork без имени в качестве регистрации по умолчанию и добавить другую именованную регистрацию. Например:

container.RegisterType<iunitofwork, unitofwork="">(new PerResolveLifetimeManager(), 
 new InjectionConstructor(new UserDbContext()));
container.RegisterType<iunitofwork, unitofwork="">("OrderUow", 
 new PerResolveLifetimeManager(), 
 new InjectionConstructor(new OrderDbContext()));
</iunitofwork,></iunitofwork,>

С помощью кода выше, когда вы разрешаете экземпляр IUnitOfWork без указания имени, как в container.Resolve, вы получите экземпляр, который использует UserDbContext. Для получения любого именованного экземпляра вам нужно сделать что-то вроде container.Resolve<iunitofwork>("OrderUow")</iunitofwork>, и он предоставит вам экземпляр, который использует OrderDbContext. (Если вы хотите использовать именованную регистрацию, вы всегда вынуждены разрешать тип, передающий имя)

Осталось только правильно установить параметры вставки для ваших репозиториев, чтобы они получили правильный экземпляр IUnitOfWork. Вы можете сделать это с настройкой ResolvedParameter, это позволяет вам указать именованную регистрацию для любой зависимости. (См. этот вопрос SO)

Таким образом, код для подключения ваших репозиториев будет следующим:

container.RegisterType<iuserrepository, userrepository="">();
container.RegisterType<itokenrepository, tokenrepository="">();
container.RegisterType<iorderrepository, orderrepository="">(new InjectionConstructor(
 new ResolvedParameter<iunitofwork>("OrderUow"),
 //additional dependencies in the OrderRepository constructor, resolved using default Unity registrations
 new ResolvedParameter<ientityfactory<order,ordertbl>>()));
</ientityfactory<order,ordertbl></iunitofwork></iorderrepository,></itokenrepository,></iuserrepository,>

Обратите внимание, что только в случае OrderRepository нам нужно добавить дополнительную информацию для регистрации, поэтому Unity знает, что зависимость IUnitOfWork должна быть разрешена с использованием указанной именной регистрации, а не по умолчанию.

С учетом этих регистраций, если у вас есть класс со следующими зависимостями:

public FooClass(IOrderRepository orderRepository, IUserRepository userRepository, ITokenRepository tokenRepository)
{
 ...
}
  • Оба userRepository и tokenRepository получат один и тот же экземпляр IUnitOfWork, который будет использовать контекст UserDbContext.

  • orderRepository получит другой экземпляр IUnitOfWork, который использует контекст OrderDbContext.

Edit

При использовании new InjectionConstructor вам нужно будет учитывать все параметры конструктора, который вы хотите использовать. В случае ваших репозиториев у вас есть IUnitOfWork и IEntityFactory как зависимости в конструкторе. Я обновил регистрацию IOrderRepository выше с зависимостью, которую я угадываю по типу IEntityFactory (в случае UserRepository, который будет иметь тип IEntityFactory).

Для добавления дополнительных зависимостей в конструкторе добавьте еще ResolvedParameter или просто typeOf(T). Обратите внимание, что порядок параметров в InjectionConstructor должен соответствовать порядку параметров в конструкторе типа, который вы регистрируете.

  • Другими словами, если у вас есть конструктор типа

    public UserRepository(IUnitOfWork unitOfWork, IEntityFactory<user, usertbl=""> factory)
     : base(unitOfWork, factory)
    { }
    </user,>
  • Вы добавили бы InjectionConstructor одним из двух способов:

    container.RegisterType<iuserrepository, userrepository="">(new InjectionConstructor(
     //Let assume the Uow was registered as a named registration 
     new ResolvedParameter<iunitofwork>("UserUow"),
     //provide information about additional parameters in the constructor
     new ResolvedParameter<ientityfactory<user,usertbl>>()));
    container.RegisterType<iuserrepository, userrepository="">(new InjectionConstructor(
     //Let assume the Uow was registered as a named registration 
     new ResolvedParameter<iunitofwork>("UserUow"),
     //provide information about additional parameters in the constructor
     typeof(<ientityfactory<user,usertbl>>)));
    </ientityfactory<user,usertbl></iunitofwork></iuserrepository,></ientityfactory<user,usertbl></iunitofwork></iuserrepository,>

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

Изменить 2 - Пример кода

Фиктивные интерфейсы и реализации:

public class DbContext
{
 public string Name { get; set; }
}
public interface IUnitOfWork
{
 DbContext DbContext { get; }
} 
public class UnitOfWork : IUnitOfWork
{
 private readonly DbContext _dbContext;
 public UnitOfWork(DbContext dbContext)
 {
 _dbContext = dbContext;
 }
 public DbContext DbContext { get { return _dbContext; } }
}
public interface IOrderRepository
{
 IUnitOfWork UnitOfWork { get;}
}
public class BaseRepository
{
 private readonly IUnitOfWork _uow;
 public BaseRepository(IUnitOfWork uow)
 {
 _uow = uow;
 }
 public IUnitOfWork UnitOfWork { get { return _uow; } }
}
public class OrderRepository : BaseRepository, IOrderRepository
{
 private IFooAdditionalDependency _foo;
 public OrderRepository(IUnitOfWork uow, IFooAdditionalDependency foo)
 : base(uow)
 {
 _foo = foo;
 }
}
public interface IFooAdditionalDependency { }
public class FooAdditionalDependency : IFooAdditionalDependency
{
}
public interface IUserRepository
{
 IUnitOfWork UnitOfWork { get; }
}
public class UserRepository : BaseRepository, IUserRepository
{
 public UserRepository(IUnitOfWork uow)
 : base(uow)
 {
 }
}
public interface ITokenRepository
{
 IUnitOfWork UnitOfWork { get; }
}
public class TokenRepository : BaseRepository, ITokenRepository
{
 public TokenRepository(IUnitOfWork uow)
 : base(uow)
 {
 }
}

Контейнер Unity Регистрация:

container.RegisterType<iunitofwork, unitofwork="">(new PerResolveLifetimeManager(), 
 new InjectionConstructor(new DbContext{Name = "UserDB"}));
container.RegisterType<iunitofwork, unitofwork="">("OrderDbContext", 
 new PerResolveLifetimeManager(),
 new InjectionConstructor(new DbContext { Name = "OrderDB" }));
container.RegisterType<iuserrepository, userrepository="">();
container.RegisterType<itokenrepository, tokenrepository="">();
container.RegisterType<iorderrepository, orderrepository="">(new InjectionConstructor(
 new ResolvedParameter<iunitofwork>("OrderDbContext"), 
 typeof(IFooAdditionalDependency)));
container.RegisterType<ifooadditionaldependency, fooadditionaldependency="">();
</ifooadditionaldependency,></iunitofwork></iorderrepository,></itokenrepository,></iuserrepository,></iunitofwork,></iunitofwork,>

Дамми-контроллер, требующий репозиториев:

public class HomeController : Controller
{
 public HomeController(IOrderRepository orderRepository, IUserRepository userRepository, ITokenRepository tokenRepository)
 {
 Debug.WriteLine("Order repository context: {0}, User repository context:{1}", orderRepository.UnitOfWork.DbContext.Name, userRepository.UnitOfWork.DbContext.Name);
 Debug.WriteLine("Order repository context: {0}, User repository context:{1}", orderRepository.UnitOfWork.DbContext.GetType().Name, userRepository.UnitOfWork.DbContext.GetType().Name);
 Debug.Assert(orderRepository.UnitOfWork != userRepository.UnitOfWork);
 Debug.Assert(userRepository.UnitOfWork == tokenRepository.UnitOfWork);
 }
 public ActionResult Index()
 {
 return View();
 }
}

licensed under cc by-sa 3.0 with attribution.