Repository
抽象化と抽象化の両方のIMOは、UnitOfWork
意味のある開発において非常に貴重な場所を持っています。人々は実装の詳細について議論しますが、猫のスキンを作成する多くの方法があるように、抽象化を実装する多くの方法があります。
あなたの質問は、具体的に使用するかしないか、そしてその理由です。
Entity Frameworkにこれらの両方のパターンが既に組み込まれていることに気付いたと思いますDbContext
が、UnitOfWork
とDbSet
はRepository
です。それらは単にクラスと基礎となるデータアクセス実装との間を容易にするだけなので、一般的に、UnitOfWork
またはRepository
それら自体を単体テストする必要はありません。サービスのロジックを単体テストするときに、これら2つの抽象概念を模擬する必要があります。
テストを行うロジックとテストされるロジックの間にコードの依存関係の層(制御できない)を追加する外部ライブラリを使用して、モック、偽物などを行うことができます。
したがって、マイナーなポイントは、ユニットテストをモックするときに独自の抽象化をUnitOfWork
行い、Repository
最大限の制御と柔軟性を提供することです。
結構ですが、私にとってこれらの抽象化の本当の力は、アスペクト指向プログラミング手法を適用し、SOLIDの原則に準拠する簡単な方法を提供することです。
だからあなたはあなたを持っていますIRepository
:
public interface IRepository<T>
where T : class
{
T Add(T entity);
void Delete(T entity);
IQueryable<T> AsQueryable();
}
そしてその実装:
public class Repository<T> : IRepository<T>
where T : class
{
private readonly IDbSet<T> _dbSet;
public Repository(PPContext context)
{
_dbSet = context.Set<T>();
}
public T Add(T entity)
{
return _dbSet.Add(entity);
}
public void Delete(T entity)
{
_dbSet.Remove(entity);
}
public IQueryable<T> AsQueryable()
{
return _dbSet.AsQueryable();
}
}
これまでのところ普通のことは何もありませんが、ここではロギングデコレータを使用して簡単なロギングを追加したいと思います。
public class RepositoryLoggerDecorator<T> : IRepository<T>
where T : class
{
Logger logger = LogManager.GetCurrentClassLogger();
private readonly IRepository<T> _decorated;
public RepositoryLoggerDecorator(IRepository<T> 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<T> AsQueryable()
{
return _decorated.AsQueryable();
}
}
すべて完了し、既存のコードに変更はありません。例外処理、データキャッシング、データ検証など、追加できる他の多くの横断的な懸念があります。設計および構築プロセス全体で、既存のコードを変更せずに単純な機能を追加できる最も価値のあるものです。私たちのあるIRepository
抽象化。
今度は、StackOverflowでこの質問を何度も目にしました。「マルチテナント環境でEntity Frameworkをどのように機能させるのですか?」
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
あなたが持っている場合 Repository
抽象化、答えは「デコレータを追加するのは簡単です」です
public class RepositoryTennantFilterDecorator<T> : IRepository<T>
where T : class
{
//public for Unit Test example
public readonly IRepository<T> _decorated;
public RepositoryTennantFilterDecorator(IRepository<T> decorated)
{
_decorated = decorated;
}
public T Add(T entity)
{
return _decorated.Add(entity);
}
public void Delete(T entity)
{
_decorated.Delete(entity);
}
public IQueryable<T> AsQueryable()
{
return _decorated.AsQueryable().Where(o => true);
}
}
IMOは、常に少数の場所で参照されるサードパーティのコンポーネントに単純な抽象を配置する必要があります。この観点から見ると、ORMはコードの大部分で参照されているため、完璧な候補です。
誰かが「なぜ抽象化する必要があるのか(例: Repository
これまたはそのサードパーティのライブラリをは、「なぜあなたはそうしないのですか?」
PSデコレーターは、SimpleInjectorなどのIoCコンテナーを使用して簡単に適用できます。
[TestFixture]
public class IRepositoryTesting
{
[Test]
public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
{
Container container = new Container();
container.RegisterLifetimeScope<PPContext>();
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<IRepository<Image>>();
Assert.That(
result,
Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>)));
Assert.That(
(result as RepositoryTennantFilterDecorator<Image>)._decorated,
Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>)));
}
}
}