仓储模式Repository的选择与设计

1、项目小,扩展性差

public interface IRepository<T> where T : class,new()
    {
        /// <summary>
        /// 创建对象
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        T Create(T model);

        /// <summary>
        /// 更新对象
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        T Update(T model);

        /// <summary>
        /// 根据对象全局唯一标识检索对象
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        T Retrieve(Guid guid);

        /// <summary>
        /// 根据条件表达式检索对象
        /// </summary>
        /// <param name="expression">条件表达式</param>
        /// <returns></returns>
        /// <exception cref = "ArgumentNullException" > source 为 null</exception>
        T Retrieve(Expression<Func<T, bool>> expression);

        /// <summary>
        /// 根据对象全局唯一标识删除对象
        /// </summary>
        /// <param name="guid">对象全局唯一标识</param>
        /// <returns>删除的对象数量</returns>
        int Delete(Guid guid);

        /// <summary>
        /// 根据对象全局唯一标识集合删除对象集合
        /// </summary>
        /// <param name="guids">全局唯一标识集合</param>
        /// <returns>删除的对象数量</returns>
        int BatchDelete(IList<Guid> guids);

        List<T> GetAll();

        List<T> GetAll(Expression<Func<T, bool>> expression, Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder, int skip, int take, out int total);
    }

 
IRepository接口包含了CRUD操作,如果在业务中还需要扩展,只需在IRepository接口中添加即可。

public class RepositoryImpl<T> : IRepository<T> where T : class, new()
    {
        protected readonly string ConnectionString;

        protected RepositoryImpl(ISqlHelp sqlHelp)
        {
            ConnectionString = sqlHelp.SQLConnectionString();
        }

        public int BatchDelete(IList<Guid> guids)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                foreach (var item in guids)
                {
                    var model = dbcontext.Set<T>().Find(item);
                    dbcontext.Entry(model).State = EntityState.Deleted;
                }
                return dbcontext.SaveChanges();
            }
        }

        public T Create(T model)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                dbcontext.Entry(model).State = EntityState.Added;
                var createRowCount = dbcontext.SaveChanges();
                return createRowCount > 0 ? model : null;
            }
        }

        /// <summary>
        /// 删除模型
        /// </summary>
        /// <param name="guid">指定的全局标识</param>
        /// <returns>删除数量</returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public int Delete(Guid guid)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                var model = dbcontext.Set<T>().Find(guid);
                if (model == null) throw new ArgumentOutOfRangeException(nameof(guid));
                dbcontext.Entry(model).State = EntityState.Deleted;
                return dbcontext.SaveChanges();
            }
        }

        public List<T> GetAll()
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                return dbcontext.Set<T>().Where(q => true).ToList();
            }
        }

        public List<T> GetAll(Expression<Func<T, bool>> expression, Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder, int skip, int take, out int total)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                total = dbcontext.Set<T>().Where(expression).Count();
                switch (sortOrder)
                {
                    case SortOrder.Ascending:
                        return dbcontext.Set<T>().Where(expression).OrderBy(sortPredicate).Skip(skip).Take(take).ToList();

                    case SortOrder.Descending:
                        return dbcontext.Set<T>().Where(expression).OrderByDescending(sortPredicate).Skip(skip).Take(take).ToList();

                }
                throw new InvalidOperationException("基于分页功能的查询必须指定排序字段和排序顺序。");
            }
        }

        /// <summary>
        /// 返回序列中的第一个元素
        /// </summary>
        /// <param name="expression">查询表达式</param>
        /// <returns>T</returns>
        /// <exception cref="ArgumentNullException">source 为 null</exception>
        public T Retrieve(Expression<Func<T, bool>> expression)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                return dbcontext.Set<T>().FirstOrDefault(expression);
            }
        }

        public T Retrieve(Guid guid)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                return dbcontext.Set<T>().Find(guid);
            }
        }

        public T Update(T model)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                dbcontext.Entry(model).State = EntityState.Modified;
                var updateRowAcount = dbcontext.SaveChanges();
                return updateRowAcount > 0 ? model : null;
            }
        }
    }

 
RepositoryImpl为IRepository接口的实现。其中ISqlHelp接口包含获取数据库链接字符串的功能,DbContext为EntityFramework类库。
 

public sealed class UserServer
    {
        private readonly IRepository<User> _userRepository;

        public UserServer(IRepository<User> userRepository)
        {
            _userRepository = userRepository;
        }

        public void CreateUser()
        {
            var user = new User();
            _userRepository.Create(user);
        }
    }

这是最简单的仓储使用方式,优点是简单、快速,缺点是扩展性差且违反开放-关闭原则(Open-Close Principle)。但如果项目小且项目生存周期短可选择此模式进行快速搭建。



2、项目大,可扩展性好,不对并发做处理。

因为项目要求高扩展性,每次修改都对IRepository修改也违反软件设计原则。这里IRepository接口不变,但是RepositoryImpl做如下修改:

public class RepositoryImpl<T> : IRepository<T> where T : class, new()
    {
        protected readonly string ConnectionString;

        protected RepositoryImpl(ISqlHelp sqlHelp)
        {
            ConnectionString = sqlHelp.SQLConnectionString();
        }

        public virtual int BatchDelete(IList<Guid> guids)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                foreach (var item in guids)
                {
                    var model = dbcontext.Set<T>().Find(item);
                    dbcontext.Entry(model).State = EntityState.Deleted;
                }
                return dbcontext.SaveChanges();
            }
        }

        public virtual T Create(T model)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                dbcontext.Entry(model).State = EntityState.Added;
                var createRowCount = dbcontext.SaveChanges();
                return createRowCount > 0 ? model : null;
            }
        }

        /// <summary>
        /// 删除模型
        /// </summary>
        /// <param name="guid">指定的全局标识</param>
        /// <returns>删除数量</returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public virtual int Delete(Guid guid)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                var model = dbcontext.Set<T>().Find(guid);
                if (model == null) throw new ArgumentOutOfRangeException(nameof(guid));
                dbcontext.Entry(model).State = EntityState.Deleted;
                return dbcontext.SaveChanges();
            }
        }

        public virtual List<T> GetAll()
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                return dbcontext.Set<T>().Where(q => true).ToList();
            }
        }

        public virtual List<T> GetAll(Expression<Func<T, bool>> expression, Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder, int skip, int take, out int total)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                total = dbcontext.Set<T>().Where(expression).Count();
                switch (sortOrder)
                {
                    case SortOrder.Ascending:
                        return dbcontext.Set<T>().Where(expression).OrderBy(sortPredicate).Skip(skip).Take(take).ToList();

                    case SortOrder.Descending:
                        return dbcontext.Set<T>().Where(expression).OrderByDescending(sortPredicate).Skip(skip).Take(take).ToList();

                }
                throw new InvalidOperationException("基于分页功能的查询必须指定排序字段和排序顺序。");
            }
        }

        /// <summary>
        /// 返回序列中的第一个元素
        /// </summary>
        /// <param name="expression">查询表达式</param>
        /// <returns>T</returns>
        /// <exception cref="ArgumentNullException">source 为 null</exception>
        public virtual T Retrieve(Expression<Func<T, bool>> expression)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                return dbcontext.Set<T>().FirstOrDefault(expression);
            }
        }

        public virtual T Retrieve(Guid guid)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                return dbcontext.Set<T>().Find(guid);
            }
        }

        public virtual T Update(T model)
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                dbcontext.Entry(model).State = EntityState.Modified;
                var updateRowAcount = dbcontext.SaveChanges();
                return updateRowAcount > 0 ? model : null;
            }
        }
    }
}

即在每个方法实现上加上了virtual关键字使方法可以重载。在示例1中业务使用User对象的仓储方式为IRepository<User>,如果业务需要针对User对象集合做批量修改,这时就必须去修改IRepository和RepositoryImpl,所以这里将添加接口IUserRepository,

    /// <summary>
    /// 用户仓储接口
    /// </summary>
    public interface IUserRepository:IRepository<User>
    {
        /// <summary>
        /// 批量修改用户生日
        /// </summary>
        void BatchUpdateUserBirthday();
    }

UserRepositoryImpl实现为

public sealed class UserRepositoryImpl: RepositoryImpl<User>,IUserRepository
    {
        public UserRepositoryImpl(ISqlHelp sqlHelp) : base(sqlHelp)
        {

        }

        public void BatchUpdateUserBirthday()
        {
            using (var dbcontext = new DbContext(ConnectionString))
            {
                var usersFromDb = dbcontext.Set<User>().Where(q => q.Name.Equals("zhang"));
                foreach (var item in usersFromDb)
                {
                    item.Name = "wang";
                    dbcontext.Entry(item).State = EntityState.Modified;
                }
                dbcontext.SaveChanges();
            }
        }
    }

这里不对代码的实现合理性做讨论,只是为了说明仓储模式的设计。

而在业务层中的使用如下:

public sealed class UserServer
    {
        private readonly IUserRepository _userRepository;

        public UserServer(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }

        public void CreateUser()
        {
            var user = new User();
            _userRepository.Create(user);
        }

        public void BatchUpdateBirthDay()
        {
            _userRepository.BatchUpdateUserBirthday();
        }

此仓储模式在实际使用中稍显复杂,每添加一个实体,需要添加对应的接口和实现两个文件,但是这里的一点复杂度换来代码的高扩展性和维护性是值得的。

3、项目庞大,扩展性高,有并发处理需求

因为项目涉及高并发,采用仓储模式+工作单元模式的设计,使用工作单元的原因是可以提高数据库写操作负载,并且在仓储模式中可以根据不同的数据库链接字符串读不同的库。

对于并发的,可以分为多线程、并行处理、异步编程、响应式编程。(引用:《Concurrency in C# Cookbook》—Author,Stephen Cleary)

在仓储中我会使用异步编程实现并发。

仓储接口如下:

public interface IRepository<T> where T:class,IEntity,new ()
    {
        /// <summary>
        /// 根据条件表达式获取集合
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        Task<List<T>> FindByAsync(Expression<Func<T, bool>> predicate);

        IQueryable<T> FindQueryableByAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 根据对象全局唯一标识检索对象
        /// </summary>
        /// <param name="ID"></param>
        /// <returns></returns>
        Task<T> RetrieveAsync(Guid ID);

        /// <summary>
        /// 根据条件表达式检索对象
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        Task<T> RetrieveAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 获取所有数据
        /// </summary>
        /// <returns></returns>
        Task<List<T>> GetAllAsync();

        /// <summary>
        /// 获取所有数据
        /// </summary>
        /// <returns></returns>
        List<T> GetAll();

        /// <summary>
        /// 根据条件表示分页获取数据集合
        /// </summary>
        /// <param name="predicate">断言表达式</param>
        /// <param name="sortPredicate">排序断言</param>
        /// <param name="sortOrder">排序方式</param>
        /// <param name="skip">跳过序列中指定数量的元素,然后返回剩余的元素</param>
        /// <param name="take">从序列的开头返回指定数量的连续元素</param>
        /// <returns>item1:数据集合;item2:数据总数</returns>
        Task<Tuple<List<T>,int>> GetAllAsync(Expression<Func<T, bool>> predicate, Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder, int skip, int take);
    }

工作单元接口如下:

/// <summary>
    /// Unit Of Work Pattern
    /// </summary>
    public interface IUnitOfWork : IDisposable
    {
        DbContext DbContext { get; set; }

        /// <summary>
        /// 提交所有更改
        /// </summary>
        Task CommitAsync();

        #region Methods
        /// <summary>
        /// 将指定的聚合根标注为“新建”状态。
        /// </summary>
        /// <typeparam name="T">需要标注状态的聚合根类型。</typeparam>
        /// <param name="obj">需要标注状态的聚合根。</param>
        void RegisterNew<T>(T obj)
            where T : class, IEntity;
        /// <summary>
        /// 将指定的聚合根标注为“更改”状态。
        /// </summary>
        /// <typeparam name="T">需要标注状态的聚合根类型。</typeparam>
        /// <param name="obj">需要标注状态的聚合根。</param>
        void RegisterModified<T>(T obj)
            where T : class;
        /// <summary>
        /// 将指定的聚合根标注为“删除”状态。
        /// </summary>
        /// <typeparam name="T">需要标注状态的聚合根类型。</typeparam>
        /// <param name="obj">需要标注状态的聚合根。</param>
        void RegisterDeleted<T>(T obj)
            where T : class;
        #endregion
    }

 
仓储实现如下:

public class RepositoryImpl<T> : IRepository<T> where T : class, IEntity, new()
    {
        protected readonly DbContext Context;

        protected RepositoryImpl(IContextHelper contextHelper)
        {
            Context = contextHelper.DbContext;
        }

        public virtual async Task<List<T>> FindByAsync(Expression<Func<T, bool>> predicate)
        {
            return await Context.Set<T>().Where(predicate).ToListAsync();
        }

        public virtual IQueryable<T> FindQueryableByAsync(Expression<Func<T, bool>> predicate)
        {
            return Context.Set<T>().Where(predicate);
        }

        public virtual async Task<List<T>> GetAllAsync()
        {
            return await Context.Set<T>().ToListAsync();
        }

        public List<T> GetAll()
        {
            return Context.Set<T>().ToList();
        }

        public virtual async Task<Tuple<List<T>, int>> GetAllAsync(Expression<Func<T, bool>> predicate,
            Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder, int skip, int take)
        {
            var result = Context.Set<T>().Where(predicate);
            var total = result.Count();
            switch (sortOrder)
            {

                case SortOrder.Ascending:
                    var resultAscPaged = await
                        Context.Set<T>().Where(predicate).OrderBy(sortPredicate).Skip(skip).Take(take).ToListAsync();
                    return new Tuple<List<T>, int>(resultAscPaged, total);

                case SortOrder.Descending:
                    var resultDescPaged = await
                        Context.Set<T>().Where(predicate)
                            .OrderByDescending(sortPredicate)
                            .Skip(skip)
                            .Take(take).ToListAsync();
                    return new Tuple<List<T>, int>(resultDescPaged, total);
            }
            throw new InvalidOperationException("基于分页功能的查询必须指定排序字段和排序顺序。");
        }

        public virtual async Task<T> RetrieveAsync(Expression<Func<T, bool>> predicate)
        {
            return await Context.Set<T>().FirstOrDefaultAsync(predicate);
        }

        public virtual async Task<T> RetrieveAsync(Guid id)
        {
            return await Context.Set<T>().FindAsync(id);
        }
    }

工作单元实现如下:

public class UnitOfWork : IUnitOfWork
    {
        public DbContext DbContext { get; set; }
        public UnitOfWork(IContextHelper contextHelp)
        {
            DbContext = contextHelp.DbContext;
        }

        /// <summary>
        /// Saves all pending changes
        /// </summary>
        /// <returns>The number of objects in an Added, Modified, or Deleted state</returns>
        public virtual async Task CommitAsync()
        {
            // Save changes with the default options
            try
            {
                await DbContext.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                ex.Entries.Single().Reload();
            }

        }

        /// <summary>
        /// Disposes the current object
        /// </summary>
        public virtual void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes all external resources.
        /// </summary>
        /// <param name="disposing">The dispose indicator.</param>
        private void Dispose(bool disposing)
        {
            if (!disposing) return;
            if (DbContext == null) return;

            DbContext.Dispose();
            DbContext = null;
        }

        public virtual void RegisterNew<TEntity>(TEntity obj) where TEntity : class, IEntity
        {
            DbContext.Set<TEntity>().Add(obj);
        }

        public virtual void RegisterModified<TEntity>(TEntity obj) where TEntity : class
        {
            DbContext.Entry(obj).State = EntityState.Modified;
        }

        public virtual void RegisterDeleted<TEntity>(TEntity obj) where TEntity : class
        {
            DbContext.Entry(obj).State = EntityState.Deleted;
        }

    }

在业务层中的使用同2。

引自:https://www.cnblogs.com/Zhang-Xiang/p/7839540.html

本文版权归作者和博客园共有,欢迎转载。未经作者同意下,必须在文章页面明显标出原文链接及作者,否则保留追究法律责任的权利。
如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!

原文地址:https://www.cnblogs.com/GonM/p/Repository.html

时间: 2024-10-10 12:50:30

仓储模式Repository的选择与设计的相关文章

4.CRUD Operations Using the Repository Pattern in MVC【在MVC中使用仓储模式进行增删查改】

原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-using-the-repository-pattern-in-mvc/ 上一篇文章,讲到了MVC中基本的增删查改,这篇文章,我会继续说到,使用仓储模式,进行增删查改. 什么是仓储模式呢,我们先来了解一下:  仓储模式是为了在程序的数据访问层和业务逻辑层之间创建一个抽象层,它是一种数据访问模式,提供了一种更松散耦合的数据访问方法.我们把创建数据访问的逻辑代码写在单独的类中,或者类库中

5.在MVC中使用泛型仓储模式和工作单元来进行增删查改

原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pattern-and-uni/ 系列目录: Relationship in Entity Framework Using Code First Approach With Fluent API[[使用EF Code-First方式和Fluent API来探讨EF中的关系]] Code First Mig

MVC+EF 理解和实现仓储模式和工作单元模式

MVC+EF 理解和实现仓储模式和工作单元模式 原文:Understanding Repository and Unit of Work Pattern and Implementing Generic Repository in ASP.NET MVC using Entity Framework 文章介绍 在这篇文章中,我们试着来理解Repository(下文简称仓储)和Unit of Work(下文简称工作单元)模式.同时我们使用ASP.NET MVC和Entity Framework 搭

6.在MVC中使用泛型仓储模式和依赖注入实现增删查改

原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pattern-and-dep/ 系列目录: Relationship in Entity Framework Using Code First Approach With Fluent API[[使用EF Code-First方式和Fluent API来探讨EF中的关系]] Code First Mig

1.仓储模式在MVC应用程序中的使用

目录 1.仓储模式在MVC应用程序中的使用 2.泛型仓储模式在MVC应用程序中的使用 3.MVC Code-First和仓储模式的应用 4.待续.... 这篇文章中,我会解释仓储模式在MVC程序中的使用. 首先,我们需要理解什么是仓储模式[repository Pattern],来看看下面的图片 没有使用仓储模式的MVC应用程序:      使用了仓储模式的MVC应用程序: 仓储模式,是一个抽象层,它将数据库的访问逻辑,映射到实体的访问逻辑. 下面,我们来看做一个应用程序,来体验一下仓储模式吧.

细说MVC中仓储模式的应用

文章提纲 概述要点 理论基础 详细步骤 总结 概述要点 设计模式的产生,就是在对开发过程进行不断的抽象. 我们先看一下之前访问数据的典型过程. 在Controller中定义一个Context, 例如: private AccountContext db = new AccountContext(); 在Action中访问,例如获取用户列表: var users=db.SysUsers; 类似于这种,耦合性太高.业务逻辑直接访问数据存储层会导致一些问题,如 重复代码:不容易集中使用数据相关策略,例

ASP.NET Mvc实用框架(一)Ioc、仓储模式和单元工作模式

Framework.EF 首先看一下这个类库: Extended文件夹存放的是EntityFramework.Extensions这个插件的源代码,没有别的原因,就是本人觉得这个插件挺好的,每次省的下载而已 IDependency:用于依赖注入的接口 IRepository和Repository:用于仓储模式 IUnitOfWork和UnitOfWork:用于单元工作模式 Page:分页实体 1.什么是依赖注入? 记得第一次接触依赖注入的时候是在我大二暑假自己出去实习的时候,当时带我的人让我看一

在MVC程序中,使用泛型仓储模式和工作单元实现增删查改

在这片文章中,我将自己动手为所有的实体:写一个泛型仓储类,还有一个工作单元. 工作单元的职责就是:为每一个实体,创建仓储实例.仓储(仓库)的职责:增删查改的功能实现. 我们将会在控制器中,创建工作单元类(UnitOfWork)的实例,然后根据实体,创建仓储实例,再就是使用仓储里面的方法,做操作了. 下面的图中,解释了,仓储和EF 数据上文的关系,在这个图里面,MVC控制器和仓储之间的交互,是通过工作单元来进行的,而不是直接和EF接触. 那么你可能就要问了,为什么要使用工作单元??? 工作单元,就

仓储模式在MVC中的应用学习系列

好久没写博客了,学习的东西,还是需要记录下来,自己懂还得懂得表达出来,这才是最重要的.好了废话说多了,现在开始正题.     在这个系列中,我会把仓储模式和工作单元在MVC应用程序中的应用写出来.有不对的地方,欢迎大家指正. 目录 1.仓储模式在MVC应用程序中的使用 2.泛型仓储模式在MVC应用程序中的使用 3.MVC Code-First和仓储模式的应用 4.待续....