В моем случае, это решение, которое я придумала - я бы определенно не рекомендовал следовать ему, и я уверен, что есть уловки, которые моя команда должна будет решить, когда они появятся ...
Поскольку нам требуется несколько механизмов баз данных (Mongo и EF / SQL), мы обернули наши взаимодействия с базой данных в шаблон репозитория и единицы работы.Все наши репозитории реализованы для каждого механизма базы данных, например, IMongoRepository<T> : IWriteableRepository<T>
, а методы, которые не могут быть абстрагированы с помощью IWriteableRepository<T>
, реализованы с помощью IMongoRepository<T>
.Это работает довольно хорошо, и я не возражаю против использования этого шаблона.
IUnitOfWork
также реализован для каждого механизма базы данных, потому что Mongo, SQL и т. Д. Будут обрабатывать транзакции по-разному.Забота о совместном использовании контекстов при поддержке объектов для инъекций была решена с помощью фабрики, то есть что-то вроде
public class FooService
{
public FooService(
IUnitOfWorkFactory<EntityFrameworkUnitOfWork> factory,
IRepositoryContext context,
IWriteableRepository<Bar> repository)
{
this.UnitOfWorkFactory = factory;
this.Context = context;
this.Repository = repository;
}
private IUnitOfWorkFactory<EntityFrameworkUnitOfWork> UnitOfWorkFactory { get; set; }
private IRepositoryContext Context { get; set; }
private IWriteableRepository<Bar> Repository { get; set; }
public bool RemoveBar(int baz)
{
// IUnitOfWorkFactory<T>.Begin(IRepositoryContext)
// where T : IUnitOfWork, new()
//
// 1) Creates a new IUnitOfWork instance by calling parameterless constructor
// 2) Call UseContext(IRepositoryContext) on UoW, passing in the context;
// This causes the UoW to use the passed-in context
// 3) Calls .Begin() on the UoW
// 4) Returns the UoW
using (var unitOfWork = this.UnitOfWorkFactory.Begin(this.Context))
{
var bar = this.Repository
.Query()
.First(x => x.Baz == baz);
this.Repository.Remove(bar);
var (success, rows) = unitOfWork.Commit();
return success && rows > 0;
}
}
}
EntityFrameworkUnitOfWork
(или любой IUnitOfWork
) разрешено реализовывать Begin
,Commit
и Rollback
как хочет.IUnitOfWork
также реализует IDisposable
для обеспечения очистки базовых объектов транзакции.Использование того же контекста также гарантирует, что транзакции будут обязательно применяться к репозиториям, использующим этот контекст.
Кроме того, IUnitOfWork
вместо фабрики может быть передано, если у него есть проверки, чтобы гарантировать, что у него только одна транзакцияоткрывать одновременно;но, чтобы удалить эту связь с реализацией, мы создали фабрику.Это не только гарантирует, что у нас есть только одна транзакция на каждый using
блок, но и мы имеем возможность иметь using
блок, не касаясь конструктора для IUnitOfWork
в нашем потребляющем коде.
Как заявление об отказе от ответственности, я искренне согласен с тем, что вы не должны помещать ORM в репозитории.Это запутает ваш код БД и добавит ненужные сложности.Мы используем эти репозитории, чтобы сделать наши взаимодействия с базами данных независимыми при выполнении простых задач.В противном случае мы получаем более конкретную реализацию наших репозиториев, удаляя большую часть волшебства, от которого страдают другие реализации шаблонов репозиториев.Как правило, когда методы выходят за рамки операций с индивидуальной записью, механизмы и драйверы баз данных имеют разные идеи, как это сделать.
Последнее замечание: может быть очевидно, что если вы внедрите репозиторий, который не подходитс вводимым контекстом (например, IMongoContext
и IEntityFrameworkRepository<Bar>
) ваш код не будет выполняться в транзакциях с базой данных.Причина, по которой это не представляет проблемы, заключается в том, что
- , использующий контекст, отличный от контекста в вашем хранилище, в большинстве случаев уже бессмысленен
- вы должны управлять контекстом и хранилищем в потребляющемкод, и, следовательно, будет знать о конфликтующих контекстах