美文网首页asp.net coreASP.NET Core .NET Core
EntityFramework(Core)中使用Reposito

EntityFramework(Core)中使用Reposito

作者: Angeladaddy | 来源:发表于2019-04-16 13:47 被阅读257次

    本文github地址,内有测试惊喜彩蛋

    数据访问层设计的好坏直接影响到写代码的心情:)--- 其实直接影响的是测试。

    个人以为,开始一个新工程时首先应该完成数据访问层(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

    本文github地址,内有测试惊喜彩蛋

    相关文章

      网友评论

        本文标题:EntityFramework(Core)中使用Reposito

        本文链接:https://www.haomeiwen.com/subject/mgzjwqtx.html