Repository Design Pattern
The Repository Design Pattern is a structural pattern that mediates data access by providing an abstraction over the data layer. It allows you to decouple the data access logic and business logic by encapsulating the data access logic in a separate repository class. This pattern provides a collection-like interface for accessing domain objects.
Pros
- Decoupling: Separates the data access logic from business logic, promoting cleaner code organization and separation of concerns.
- Testability: Makes the application easier to unit test by allowing mocking of the repository.
- Consistency: Centralizes data access logic, ensuring consistency across different parts of the application.
- Flexibility: Makes it easier to switch out data access technology (e.g., from Entity Framework to another ORM) without changing the business logic.
Cons
- Complexity: Adds an extra layer of abstraction, which can increase the complexity of the codebase.
- Overhead: This may introduce additional overhead in smaller applications where a simple data access strategy could suffice.
- Duplication: Sometimes can lead to duplicate code if not implemented correctly.
Implementation in .NET Core
To implement the Repository Pattern in .NET Core, you need to follow these steps.
1. Create a Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
2. Create an Interface for the Repository
public interface IProductRepository
{
IEnumerable<Product> GetAll();
Product GetById(int id);
void Add(Product product);
void Update(Product product);
void Delete(int id);
}
3. Implement the Repository
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public IEnumerable<Product> GetAll()
{
return _context.Products.ToList();
}
public Product GetById(int id)
{
return _context.Products.Find(id);
}
public void Add(Product product)
{
_context.Products.Add(product);
_context.SaveChanges();
}
public void Update(Product product)
{
_context.Products.Update(product);
_context.SaveChanges();
}
public void Delete(int id)
{
var product = _context.Products.Find(id);
if (product != null)
{
_context.Products.Remove(product);
_context.SaveChanges();
}
}
}
4. Register the Repository in the Dependency Injection Container
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IProductRepository, ProductRepository>();
services.AddControllersWithViews();
}
5. Use the Repository in a Controller
public class ProductsController : Controller
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public IActionResult Index()
{
var products = _productRepository.GetAll();
return View(products);
}
}
Unit of Work Pattern
Concept
The Unit of Work Pattern maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. It helps to manage transactions by ensuring that all operations within a business transaction are committed or rolled back as a single unit.
Pros
- Atomicity: Ensures that a series of operations either all succeed or all fail, maintaining data consistency.
- Efficiency: Minimizes database calls by batching them together.
- Encapsulation: Encapsulates the transaction logic, making the code cleaner and more maintainable.
Cons
- Complexity: Adds another layer of complexity, especially in managing the life cycle of the Unit of Work.
- Overhead: This can introduce performance overhead due to managing the additional logic and state.
Implementation of Unit of Work in .NET Core
1. Create an Interface for the Unit of Work
public interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
int Complete();
}
2. Implement the Unit of Work
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public UnitOfWork(AppDbContext context)
{
_context = context;
Products = new ProductRepository(_context);
}
public IProductRepository Products { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
3.nRegister the Unit of Work in the Dependency Injection Container
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddControllersWithViews();
}
4. Use the Unit of Work in a Controller
public class ProductsController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public ProductsController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IActionResult Index()
{
var products = _unitOfWork.Products.GetAll();
return View(products);
}
public IActionResult Create(Product product)
{
if (ModelState.IsValid)
{
_unitOfWork.Products.Add(product);
_unitOfWork.Complete();
return RedirectToAction(nameof(Index));
}
return View(product);
}
}
Summary
The repository Pattern and Unit of Work Pattern work together to create a maintainable, testable, and scalable data access layer in .NET Core applications. The Repository Pattern provides a way to manage data access logic, while the Unit of Work Pattern ensures that a series of operations are treated as a single transaction, maintaining data integrity.