数据访问层设计的好坏直接影响到写代码的心情:)--- 其实直接影响的是测试。
个人以为,开始一个新工程时首先应该完成数据访问层(Data Access Layer,DAL),这一层应该完全独立。原则:除了DAL,任何层都不应该操作数据库。
我们首先实现Repository (仓储)模式:
DAL层结构:
DataAccessLayer
|——Contacts(接口和抽象类)
|——具体实现类
1. IRepository :仓储类接口
public interface IRepository<TEntity> where TEntity : class
{
TEntity Add(TEntity t);
Task<TEntity> AddAsyn(TEntity t);
int Count();
Task<int> CountAsync();
void Delete(TEntity entity);
Task<int> DeleteAsyn(TEntity entity);
void Dispose();
TEntity Find(Expression<Func<TEntity, bool>> match);
IEnumerable<TEntity> FindAll(Expression<Func<TEntity, bool>> match);
Task<IEnumerable<TEntity>> FindAllAsync(Expression<Func<TEntity, bool>> match);
Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> match);
IEnumerable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate);
Task<IEnumerable<TEntity>> FindByAsyn(Expression<Func<TEntity, bool>> predicate);
TEntity Get(int id);
IEnumerable<TEntity> GetAll();
Task<IEnumerable<TEntity>> GetAllAsyn();
Task<TEntity> GetAsync(int id);
void Save();
Task<int> SaveAsync();
TEntity Update(TEntity t, object key);
Task<TEntity> UpdateAsyn(TEntity t, object key);
}
这是所有Repository类的父类,其中的方法根据需要增减;注意其中集合类的返回类型都是IEnumerable,而不是ICollection,因为我们不想让外部类操作数据库。IEnumerable不能Query。
2. Repository:所有仓储类的基类
public class Repository<T> : IRepository<T> where T : class
{
protected DbContext _context;
public Repository(DbContext dbContext)
{
_context = dbContext;
}
public IEnumerable<T> GetAll()
{
return _context.Set<T>();
}
public virtual async Task<IEnumerable<T>> GetAllAsyn()
{
return await _context.Set<T>().ToListAsync();
}
public virtual T Get(int id)
{
return _context.Set<T>().Find(id);
}
public virtual async Task<T> GetAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}
public virtual T Add(T t)
{
_context.Set<T>().Add(t);
_context.SaveChanges();
return t;
}
public virtual async Task<T> AddAsyn(T t)
{
_context.Set<T>().Add(t);
await _context.SaveChangesAsync();
return t;
}
public virtual T Find(Expression<Func<T, bool>> match)
{
return _context.Set<T>().SingleOrDefault(match);
}
public virtual async Task<T> FindAsync(Expression<Func<T, bool>> match)
{
return await _context.Set<T>().SingleOrDefaultAsync(match);
}
public IEnumerable<T> FindAll(Expression<Func<T, bool>> match)
{
return _context.Set<T>().Where(match).ToList();
}
public async Task<IEnumerable<T>> FindAllAsync(Expression<Func<T, bool>> match)
{
return await _context.Set<T>().Where(match).ToListAsync();
}
public virtual void Delete(T entity)
{
_context.Set<T>().Remove(entity);
_context.SaveChanges();
}
public virtual async Task<int> DeleteAsyn(T entity)
{
_context.Set<T>().Remove(entity);
return await _context.SaveChangesAsync();
}
public virtual T Update(T t, object key)
{
if (t == null)
return null;
T exist = _context.Set<T>().Find(key);
if (exist != null)
{
_context.Entry(exist).CurrentValues.SetValues(t);
_context.SaveChanges();
}
return exist;
}
public virtual async Task<T> UpdateAsyn(T t, object key)
{
if (t == null)
return null;
T exist = await _context.Set<T>().FindAsync(key);
if (exist != null)
{
_context.Entry(exist).CurrentValues.SetValues(t);
await _context.SaveChangesAsync();
}
return exist;
}
public int Count()
{
return _context.Set<T>().Count();
}
public async Task<int> CountAsync()
{
return await _context.Set<T>().CountAsync();
}
public virtual void Save()
{
_context.SaveChanges();
}
public async virtual Task<int> SaveAsync()
{
return await _context.SaveChangesAsync();
}
public virtual IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate)
{
IEnumerable<T> query = _context.Set<T>().Where(predicate);
return query;
}
public virtual async Task<IEnumerable<T>> FindByAsyn(Expression<Func<T, bool>> predicate)
{
return await _context.Set<T>().Where(predicate).ToListAsync();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_context.Dispose();
}
this.disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
此类的主要作用是对接口方法进行集中实现,工程中具体仓储类继承此类后就不用再实现这些方法了。
3. 具体仓储类
假设我们的领域类中有个Blog:
public class Blog
{
[Key]
public int BlogId { get; set; }
public string Title { get; set; }
public string CreatedBy { get; set; }
public DateTime? CreatedOn { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
public virtual List<Post> Posts { get; set; }
}
我们应该对其实现一个仓储类,在此之前,应该设计此具体仓储类的接口(面向接口编程)
public interface IBlogRepository : IRepository<Blog>
{
Blog GetByTitle(string blogTitle);
}
添加了一个根据标题获取数据的方法
public class BlogRepository : Repository<Blog>, IBlogRepository
{
public BlogRepository(DbContext dbContext) : base(dbContext) { }
public Blog GetByTitle(string blogTitle)
{
return GetAll().FirstOrDefault(x => x.Title == blogTitle);
}
/*
add override methods
*/
public async Task<Blog> GetSingleAsyn(int blogId)
{
return await _context.Set<Blog>().FindAsync(blogId);
}
public override Blog Update(Blog t, object key)
{
Blog exist = _context.Set<Blog>().Find(key);
if (exist != null)
{
t.CreatedBy = exist.CreatedBy;
t.CreatedOn = exist.CreatedOn;
}
return base.Update(t, key);
}
public async override Task<Blog> UpdateAsyn(Blog t, object key)
{
Blog exist = await _context.Set<Blog>().FindAsync(key);
if (exist != null)
{
t.CreatedBy = exist.CreatedBy;
t.CreatedOn = exist.CreatedOn;
}
return await base.UpdateAsyn(t, key);
}
}
可以看到,它继承了仓储基类和接口,这样基类中的方法就自动实现了;然后实现接口中添加的方法就行。同时,由于基类中的方法都是虚方法,这里也可以对其进行重写。
4. RepositoryWrapper(可选)
随着领域类越来越多,我们就要写越来越多的仓储类,每个都注入或者new很麻烦,这时,我们可以将工程中的仓储类包装一下:
首先是接口类:
public interface IRepositoryWrapper
{
IBlogRepository Blog { get; }
}
实现类:
public class RepositoryWrapper : IRepositoryWrapper
{
private DataContext _dataContext;
private IBlogRepository _blogRepository;
public RepositoryWrapper(DataContext dataContext)
{
_dataContext = dataContext;
}
public IBlogRepository Blog
{
get
{
if (_blogRepository is null)
{
_blogRepository = new BlogRepository(_dataContext);
}
return _blogRepository;
}
}
}
这就是Repository模式的全部内容。
5. 在Asp.net Core项目中消费仓储类
首先在Startup中注册IOC:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton<IRepositoryWrapper, RepositoryWrapper>();
}
我们在Controller中消费仓储类:
[Route("api/[controller]")]
[ApiController]
public class BlogController : Controller
{
private readonly IRepositoryWrapper _repositoryWrapper;
public BlogController(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
[Route("~/api/GetBlogs")]
[HttpGet]
public async Task<IEnumerable<Blog>> Index()
{
return await _repositoryWrapper.Blog.GetAllAsyn();
}
[Route("~/api/AddBlog")]
[HttpPost]
public async Task<Blog> AddBlog([FromBody]Blog blog)
{
await _repositoryWrapper.Blog.AddAsyn(blog);
await _repositoryWrapper.Blog.SaveAsync();
return blog;
}
[Route("~/api/UpdateBlog")]
[HttpPut]
public async Task<Blog> UpdateBlog([FromBody]Blog blog)
{
var updated = await _repositoryWrapper.Blog.UpdateAsyn(blog, blog.BlogId);
return updated;
}
[Route("~/api/DeleteBlog/{id}")]
[HttpDelete]
public string Delete(int id)
{
_repositoryWrapper.Blog.Delete(_repositoryWrapper.Blog.Get(id));
return "Employee deleted successfully!";
}
protected override void Dispose(bool disposing)
{
_repositoryWrapper.Blog.Dispose();
base.Dispose(disposing);
}
}
6. Unit Of Work(UOW)
Unit Of Work可以看到,UOW就是在Repository外部包了薄薄的一层,进一步进行了代码隔离。所以Repository是UOW的前提。个人感觉,UOW可做可不做,上面介绍的Repository Pattern已经能满足大部分开发要求。并且很方便测试。
首先是IUnitOfWork接口:
public interface IUnitOfWork:IDisposable
{
IRepository<User> Users { get; }
Task Commit();
}
UOW类:
public class UnitOfWork : IUnitOfWork
{
private bool disposed = false;
private IRepository<User> _userRepo;
private readonly RepositoryContext _repositoryContext;
public UnitOfWork(RepositoryContext repositoryContext)
{
_repositoryContext = repositoryContext;
}
public IRepository<User> Users
{
get
{
if (_userRepo is null)
{
_userRepo = new UserReposiory(_repositoryContext);
}
return _userRepo;
}
}
public async Task Commit()
{
await _repositoryContext.SaveChangesAsync();
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed && disposing)
{
_repositoryContext.Dispose();
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
StartUp(IOC)
services.AddTransient<IUnitOfWork, UnitOfWork>();
Controller
[Route("api/user")]
[ApiController]
public class UserController : ControllerBase
{
private readonly DAL.UnitOfWork _unitOfWork;
public UserController(DAL.UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<IActionResult> GetAll()
{
return Ok(await _unitOfWork.Users.GetAll());
}
}
贴出工程DAL层结构:
image.png
网友评论