IMO baik Repository
abstraksi maupun UnitOfWork
abstraksi memiliki tempat yang sangat berharga dalam perkembangan yang berarti. Orang akan berdebat tentang detail implementasi, tetapi seperti ada banyak cara untuk menguliti kucing, ada banyak cara untuk menerapkan abstraksi.
Pertanyaan Anda secara khusus digunakan atau tidak dan mengapa.
Karena Anda pasti menyadari bahwa Anda sudah memiliki kedua pola ini yang dibangun ke dalam Entity Framework, DbContext
is the UnitOfWork
dan DbSet
is the Repository
. Anda biasanya tidak perlu menguji unit UnitOfWork
atau Repository
dirinya sendiri karena mereka hanya memfasilitasi antara kelas Anda dan implementasi akses data yang mendasarinya. Apa yang perlu Anda lakukan, lagi dan lagi, adalah mengejek kedua abstraksi ini saat unit menguji logika layanan Anda.
Anda dapat memalsukan, memalsukan, atau apa pun dengan pustaka eksternal yang menambahkan lapisan dependensi kode (yang tidak Anda kendalikan) antara logika yang melakukan pengujian dan logika yang sedang diuji.
Jadi poin kecilnya adalah memiliki abstraksi Anda sendiri UnitOfWork
dan Repository
memberi Anda kontrol dan fleksibilitas maksimum saat mengejek pengujian unit Anda.
Semuanya baik-baik saja, tetapi bagi saya, kekuatan sebenarnya dari abstraksi ini adalah menyediakan cara sederhana untuk menerapkan teknik Pemrograman Berorientasi Aspek dan mematuhi prinsip SOLID .
Jadi, Anda memiliki IRepository
:
public interface IRepository<T>
where T : class
{
T Add(T entity);
void Delete(T entity);
IQueryable<T> AsQueryable();
}
Dan implementasinya:
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();
}
}
Sejauh ini tidak ada yang luar biasa, tetapi sekarang kami ingin menambahkan beberapa logging - mudah dengan Dekorator logging .
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();
}
}
Semua selesai dan tanpa perubahan pada kode kami yang ada . Ada banyak masalah lintas sektoral lain yang dapat kami tambahkan, seperti penanganan pengecualian, cache data, validasi data, atau apa pun, dan di seluruh proses desain dan pembuatan kami, hal paling berharga yang kami miliki yang memungkinkan kami menambahkan fitur sederhana tanpa mengubah kode yang ada. adalah IRepository
abstraksi kami .
Sekarang, berkali-kali saya telah melihat pertanyaan ini di StackOverflow - "bagaimana Anda membuat Entity Framework berfungsi di lingkungan multi tenant?".
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
Jika Anda memiliki Repository
abstraksi maka jawabannya adalah “mudah menambahkan dekorator”
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 Anda harus selalu menempatkan abstraksi sederhana di atas komponen pihak ketiga yang akan dirujuk di lebih dari beberapa tempat. Dari perspektif ini, ORM adalah kandidat yang tepat karena direferensikan di banyak kode kami.
Jawaban yang biasanya terlintas dalam pikiran ketika seseorang berkata "mengapa saya harus memiliki abstraksi (misalnya Repository
) atas perpustakaan pihak ketiga ini atau itu" adalah "mengapa Anda tidak?"
Dekorator PS sangat mudah diterapkan menggunakan Kontainer IoC, seperti SimpleInjector .
[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>)));
}
}
}