zoukankan      html  css  js  c++  java
  • 仓储模式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

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

  • 相关阅读:
    洛谷P1508 Likecloud-吃、吃、吃 [2017年4月计划 动态规划10]
    洛谷P1855 榨取kkksc03 [2017年4月计划 动态规划 09]
    洛谷P1164 小A点菜 [2017年4月计划 动态规划08]
    洛谷P1244 [NOI2000] 青蛙过河 [2017年4月计划 动态规划07]
    洛谷P1757 通天之分组背包 [2017年4月计划 动态规划06]
    洛谷P1877 [HAOI2012]音量调节 [2017年4月计划 动态规划05]
    洛谷P1474 [USACO 2.3]货币系统 Money Systems [2017年4月计划 动态规划04]
    洛谷P1832 A+B Problem(再升级) [2017年4月计划 动态规划03]
    洛谷P1968 美元汇率[2017年4月计划 动态规划02]
    洛谷P2347 砝码称重 [2017年4月计划 动态规划01]
  • 原文地址:https://www.cnblogs.com/GonM/p/Repository.html
Copyright © 2011-2022 走看看