namespaceRepositoryPatternPractice.Repositories { //Interface here act like a protocol or a license //Generic Interface -> Interface<T> //where keyword can set some restriction on the generic publicinterfaceIRepository<TEntity> whereTEntity: class { //Three main group of functions
/* Func<input, output> -> 委派物件(將函數當作物件的容器) eg. Func<int, int> fn = n=>n*n; Expression (eg. LINQ) turn an lambda to an expression tree and the LINQ can input a lambda expression (put in a generic delegate) */
using System; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore;
namespaceRepositoryPatternPractice.Repositories { publicclassRepository<TEntity> : IRepository<TEntity> whereTEntity: class { //the context here is generic, so it has nothing to do with my application //so you can DI some specific contexts //protected because the specific repository can use it protectedreadonly DbContext Context;
public TEntity Get(int id) { return Context.Set<TEntity>().Find(id); }
//don't return IQueryable!! //Repository should encapsulate the query //so on the Service or Controller won't get too much pressure publicIEnumerable<TEntity> GetAll() { return Context.Set<TEntity>().ToList(); }
public Author GetAuthorByName(string name) { return MypostgresContext.Authors.Where(x=>x.AuthorName.Equals(name)).ToList().FirstOrDefault(); } } }
IUnitOfWork, UnitOfWork
這裡開始是unit of work的實作
IUnitOfWork
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
using System; using RepositoryPatternPractice.Repositories;
namespaceRepositoryPatternPractice { //Unit of work //interfce chain with IDisposable, so the class implement this interface //need to implement the Dispose() method publicinterfaceIUnitOfWork : IDisposable { //Repository act the collection of objects in memory IBookRepository Books { get; } IAuthorRepository Authors { get; } intComplete(); } }
public IBookRepository Books { get; privateset; } public IAuthorRepository Authors { get; privateset; }
//we will use this context across all repositories publicUnitOfWork(MypostgresContext context) { this._context = context; //use the same context to initialize our repository Books = new BookRepository(_context); Authors = new AuthorRepository(_context); }
using System; using RepositoryPatternPractice.Models;
namespaceRepositoryPatternPractice { classProgram { publicstaticvoidMain(string[] args) { //example of using these interfaces and classes //using block like try/finally and call Dispose() using ( var unitOfWork = new UnitOfWork(new MypostgresContext()) ) { //Example 1 var books = unitOfWork.Books.GetAll(); Console.WriteLine("Initial State: "); foreach (Book b in books) { Console.WriteLine($"book: {b.BookName}, author: {unitOfWork.Authors.Get(b.AuthorId).AuthorName}"); } Console.WriteLine();
//Example 2 unitOfWork.Books.AddRange(new List<Book>() { new Book() { BookName="AIGuide", Price=300, Author=unitOfWork.Authors.GetAuthorByName("Xuan")}, new Book() { BookName="PSGuide", Price=230, Author=unitOfWork.Authors.GetAuthorByName("Xuan")} });
如果想要做到非同步, 只要把Repository內部的實作改成非同步就好, 其餘都一樣. 其實Repository pattern, Unit of Work pattern很常會一起使用, 甚至搭配DI, 但這裡為求簡單就沒有使用DI了 希望大家看完這篇文章對於Repository Pattern和UnitOfWork Pattern更加理解