ИМО как абстракция Repository
, так и абстракция UnitOfWork
имеют очень ценное место в любом значимом развитии. Люди будут спорить о деталях реализации, но так же, как есть много способов скинуть кошку, есть много способов реализовать абстракцию.
Ваш вопрос конкретно использовать или не использовать и почему.
Как вы, несомненно, поняли, что у вас уже есть оба шаблона, встроенные в Entity Framework, DbContext
- UnitOfWork
, а DbSet
- Repository
. Обычно вам не нужно тестировать единицы UnitOfWork
или Repository
, поскольку они просто облегчают между вашими классами и базовыми реализациями доступа к данным. То, что вам нужно будет делать, снова и снова, издевается над этими двумя абстракциями, когда модуль тестирует логику ваших услуг.
Вы можете высмеивать, подделывать или что угодно с внешними библиотеками, добавляя уровни зависимостей кода (которые вы не контролируете) между логикой, выполняющей тестирование, и проверяемой логикой.
So что ваша собственная абстракция для UnitOfWork
и Repository
дает вам максимальный контроль и гибкость при издевательском тестировании вашего устройства.
Все очень хорошо, но для меня реальная сила этих абстракций это простой способ применения методов Ориентированного программирования и придерживаться принципов SOLID.
Итак, у вас есть IRepository
:
public interface IRepository
where T : class
{
T Add(T entity);
void Delete(T entity);
IQueryable AsQueryable();
}
И его реализация:
public class Repository : IRepository
where T : class
{
private readonly IDbSet _dbSet;
public Repository(PPContext context)
{
_dbSet = context.Set();
}
public T Add(T entity)
{
return _dbSet.Add(entity);
}
public void Delete(T entity)
{
_dbSet.Remove(entity);
}
public IQueryable AsQueryable()
{
return _dbSet.AsQueryable();
}
}
Пока что ничего необычного, но теперь мы хотим добавить некоторые протоколы - легко с помощью журнала Decorator .
public class RepositoryLoggerDecorator : IRepository
where T : class
{
Logger logger = LogManager.GetCurrentClassLogger();
private readonly IRepository _decorated;
public RepositoryLoggerDecorator(IRepository decorated)
{
_decorated = decorated;
}
public T Add(T entity)
{
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() );
T added = _decorated.Add(entity);
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
return added;
}
public void Delete(T entity)
{
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
_decorated.Delete(entity);
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
}
public IQueryable AsQueryable()
{
return _decorated.AsQueryable();
}
}
Все сделанные и без изменения нашего существующего кода. Есть много других проблем, связанных с перекрестными циклами, которые мы можем добавить: обработка исключений, кэширование данных, валидация данных или что-то еще, а также на протяжении всего нашего процесса проектирования и сборки наиболее ценная вещь, которая у нас есть, что позволяет нам добавлять простые функции, не меняя какой-либо существующий код является нашей абстракцией IRepository
.
Теперь, много раз я видел этот вопрос в StackOverflow - «как вы заставляете Entity Framework работать в среде с несколькими арендаторами?».
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
Если у вас есть абстракция Repository
, тогда ответ «легко» добавьте декоратор "
public class RepositoryTennantFilterDecorator : IRepository
where T : class
{
//public for Unit Test example
public readonly IRepository _decorated;
public RepositoryTennantFilterDecorator(IRepository decorated)
{
_decorated = decorated;
}
public T Add(T entity)
{
return _decorated.Add(entity);
}
public void Delete(T entity)
{
_decorated.Delete(entity);
}
public IQueryable AsQueryable()
{
return _decorated.AsQueryable().Where(o => true);
}
}
IMO, вы всегда должны размещать простую абстракцию над любым сторонним компонентом, на который будут ссылаться более чем в нескольких местах. С этой точки зрения ORM является идеальным кандидатом, поскольку он упоминается во многих наших кодексах.
Ответ, который обычно приходит на ум, когда кто-то говорит «почему я должен иметь абстракцию (например, Repository
), над той или иной сторонней библиотекой «есть», почему бы и нет? »
PS Декораторы чрезвычайно просты в применении с использованием контейнера IoC, такого как SimpleInjector .
[TestFixture]
public class IRepositoryTesting
{
[Test]
public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
{
Container container = new Container();
container.RegisterLifetimeScope();
container.RegisterOpenGeneric(
typeof(IRepository<>),
typeof(Repository<>));
container.RegisterDecorator(
typeof(IRepository<>),
typeof(RepositoryLoggerDecorator<>));
container.RegisterDecorator(
typeof(IRepository<>),
typeof(RepositoryTennantFilterDecorator<>));
container.Verify();
using (container.BeginLifetimeScope())
{
var result = container.GetInstance>();
Assert.That(
result,
Is.InstanceOf(typeof(RepositoryTennantFilterDecorator)));
Assert.That(
(result as RepositoryTennantFilterDecorator)._decorated,
Is.InstanceOf(typeof(RepositoryLoggerDecorator)));
}
}
}
Эта конструкция просто объявляет константный указатель на неконстантный объект. Таким образом, вам разрешено изменять ссылочный объект, но не сам указатель.
#define flag_it_ptr flag_it_1
выполнят работу без указателей. Я думаю, что вы усложняете простые вещи.