zoukankan      html  css  js  c++  java
  • Unit of work + Repository

    (Unit of work + Repository)

    今日后开启进阶模式!

      谈到MVC与EntityFramework,则不得不说一说事务与仓储(Unit of work + Repository)。

      仓储(Repository):领域对象集合。用于操作领域对象与数据库上下文(DbContext)的交互(在此不得不说一声,领域对象和数据库表对象还是有区别的。领域对象实际上是一组有业务关系的数据库对象的抽象。最简单的形式就是主表、关系表在同一个领域对象中进行定义。例如我们前几章看到的UserProfile,它即定义了用户信息,又定义了用户角色关系信息)。

      事务(Transaction):多个业务处理有必然的顺序性、依赖性,则默认这些业务为一个原子操作。在这里我们使用工作单元,即Unit of work模式,来维护事务的原子性。

      首先,先让我们构建仓储接口。为了更好的使用EntityFramework的延迟加载,所有查询集合的接口我们均使用IQueryable接口类型作为返回值。

     View Code

    1 /// <summary>
    2 /// 基础仓储类型接口
    3 /// 采用泛型类接口定义
    4 /// </summary>
    5 /// <typeparam name="T">泛型参数</typeparam>
    6 public interface IRepository<T> : IDisposable where T : class
    7 {
    8 /// <summary>
    9 /// 返回当前表的所有记录
    10 /// </summary>
    11 /// <returns>T</returns>
    12 IQueryable<T> Entries();
    13
    14 /// <summary>
    15 /// 通过过滤条件进行查询
    16 /// </summary>
    17 /// <param name="predicate">过滤条件表达式</param>
    18 /// <returns>T</returns>
    19 IQueryable<T> Filter(Expression<Func<T, bool>> predicate);
    20
    21 /// <summary>
    22 /// 通过过滤条件进行查询
    23 /// </summary>
    24 /// <param name="predicate">过滤条件表达式</param>
    25 /// <param name="includes">需贪婪加载的属性名称</param>
    26 /// <returns>IQueryable</returns>
    27 IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes);
    28
    29 /// <summary>
    30 /// 是否存在满足表达式的记录
    31 /// </summary>
    32 /// <param name="predicate">过滤条件表达式</param>
    33 /// <returns>Boolean</returns>
    34 bool Contains(Expression<Func<T, bool>> predicate);
    35
    36 /// <summary>
    37 /// 按照数据库主键查询特定的实例
    38 /// </summary>
    39 /// <param name="keys">主键列表</param>
    40 /// <returns>T</returns>
    41 T Single(params object[] keys);
    42
    43 /// <summary>
    44 /// 按照指定表达式查询特定的实例
    45 /// </summary>
    46 /// <param name="predicate">过滤条件表达式</param>
    47 /// <returns>T</returns>
    48 T FirstOrDefault(Expression<Func<T, bool>> predicate);
    49
    50 /// <summary>
    51 /// 插入一条记录
    52 /// </summary>
    53 /// <param name="t">新实例</param>
    54 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    55 /// <returns>T</returns>
    56 T Create(T t, bool submitImmediately = false);
    57
    58 /// <summary>
    59 /// 删除一行记录
    60 /// </summary>
    61 /// <param name="t">要删除的实例</param>
    62 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    63 void Delete(T t, bool submitImmediately = false);
    64
    65 /// <summary>
    66 /// 删除满足表达式的记录
    67 /// </summary>
    68 /// <param name="predicate">过滤条件表达式</param>
    69 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    70 void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false);
    71
    72 /// <summary>
    73 /// 更新一条记录
    74 /// </summary>
    75 /// <param name="t">要更新的实例</param>
    76 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    77 void Update(T t, bool submitImmediately = false);
    78
    79 /// <summary>
    80 /// 获取当前实例的主键
    81 /// </summary>
    82 /// <param name="t">实例</param>
    83 /// <returns>Object</returns>
    84 object GetKeyValue(T t);
    85 }

      接下来是实现这个接口,真正去处理数据查询和操作的时候了。

     View Code

    1 /// <summary>
    2 /// 仓储类型定义
    3 /// 采用泛型类定义
    4 /// </summary>
    5 /// <typeparam name="T">泛型参数</typeparam>
    6 public class Repository<T> : IRepository<T> where T : class
    7 {
    8 /// <summary>
    9 /// 数据库上下文
    10 /// </summary>
    11 public UsersContext Context { get; private set; }
    12
    13 /// <summary>
    14 /// 当前表记录集合
    15 /// </summary>
    16 protected DbSet<T> DbSet
    17 {
    18 get
    19 {
    20 return Context.Set<T>();
    21 }
    22 }
    23
    24 /// <summary>
    25 /// 构造器
    26 /// </summary>
    27 public Repository()
    28 {
    29 Context = new UsersContext();
    30 }
    31
    32 /// <summary>
    33 /// 构造器
    34 /// </summary>
    35 /// <param name="connectionString">连接字符串(名称)</param>
    36 public Repository(string connectionString)
    37 {
    38 Context = new UsersContext(connectionString);
    39 }
    40
    41 /// <summary>
    42 /// 构造器
    43 /// </summary>
    44 /// <param name="context">数据库上下文</param>
    45 public Repository(UsersContext context)
    46 {
    47 Context = context;
    48 }
    49
    50 /// <summary>
    51 /// 析构器
    52 /// </summary>
    53 public void Dispose()
    54 {
    55 if (Context != null)
    56 Context.Dispose();
    57 }
    58
    59 /// <summary>
    60 /// 返回当前表的所有记录
    61 /// </summary>
    62 /// <returns></returns>
    63 public IQueryable<T> Entries()
    64 {
    65 return DbSet.AsQueryable();
    66 }
    67
    68 /// <summary>
    69 /// 通过过滤条件进行查询
    70 /// </summary>
    71 /// <param name="predicate">过滤条件表达式</param>
    72 /// <returns>T</returns>
    73 public IQueryable<T> Filter(Expression<Func<T, bool>> predicate)
    74 {
    75 return DbSet.Where(predicate);
    76 }
    77
    78 /// <summary>
    79 /// 通过过滤条件进行查询
    80 /// </summary>
    81 /// <param name="predicate">过滤条件表达式</param>
    82 /// <param name="includes">需贪婪加载的属性名称</param>
    83 /// <returns>IQueryable</returns>
    84 public IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes)
    85 {
    86 var query = DbSet.Where(predicate);
    87 if (includes != null)
    88 {
    89 foreach (var item in includes)
    90 {
    91 query = query.Include(item);
    92 }
    93 }
    94 return query;
    95 }
    96
    97 /// <summary>
    98 /// 是否存在满足表达式的记录
    99 /// </summary>
    100 /// <param name="predicate">过滤条件表达式</param>
    101 /// <returns>Boolean</returns>
    102 public bool Contains(Expression<Func<T, bool>> predicate)
    103 {
    104 return DbSet.Count(predicate) > 0;
    105 }
    106
    107 /// <summary>
    108 /// 按照数据库主键查询特定的实例
    109 /// </summary>
    110 /// <param name="keys">主键列表</param>
    111 /// <returns>T</returns>
    112 public T Single(params object[] keys)
    113 {
    114 return DbSet.Find(keys);
    115 }
    116
    117 /// <summary>
    118 /// 按照指定表达式查询特定的实例
    119 /// </summary>
    120 /// <param name="predicate">过滤条件表达式</param>
    121 /// <returns>T</returns>
    122 public T FirstOrDefault(Expression<Func<T, bool>> predicate)
    123 {
    124 return DbSet.FirstOrDefault(predicate);
    125 }
    126
    127 /// <summary>
    128 /// 插入一条记录
    129 /// </summary>
    130 /// <param name="t">新实例</param>
    131 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    132 /// <returns>T</returns>
    133 public T Create(T t, bool submitImmediately = false)
    134 {
    135 var newEntry = DbSet.Add(t);
    136 if (submitImmediately)
    137 Context.SaveChanges();
    138 return newEntry;
    139 }
    140
    141 /// <summary>
    142 /// 删除一行记录
    143 /// </summary>
    144 /// <param name="t">要删除的实例</param>
    145 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    146 public void Delete(T t, bool submitImmediately = false)
    147 {
    148 DbSet.Remove(t);
    149 if (submitImmediately)
    150 Context.SaveChanges();
    151 }
    152
    153 /// <summary>
    154 /// 删除满足表达式的记录
    155 /// </summary>
    156 /// <param name="predicate">过滤条件表达式</param>
    157 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    158 public void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false)
    159 {
    160 try
    161 {
    162 Context.Configuration.AutoDetectChangesEnabled = false; //关闭数据库上下文的自动更新跟踪功能,可提高批量操作的性能
    163
    164 var objects = Filter(predicate);
    165 foreach (var obj in objects)
    166 DbSet.Remove(obj);
    167 if (submitImmediately)
    168 Context.SaveChanges();
    169 }
    170 finally
    171 {
    172 Context.Configuration.AutoDetectChangesEnabled = true; //完成批量操作后,打开数据库上下文的自动更新跟踪功能
    173 }
    174 }
    175
    176 /// <summary>
    177 /// 更新一条记录
    178 /// </summary>
    179 /// <param name="t">要更新的实例</param>
    180 /// <param name="submitImmediately">是否直接提交。默认false。</param>
    181 public void Update(T t, bool submitImmediately = false)
    182 {
    183 var key = GetKeyValue(t);
    184
    185 var originalEntity = DbSet.Find(key);
    186
    187 Context.Entry(originalEntity).CurrentValues.SetValues(t);
    188
    189 if (submitImmediately)
    190 Context.SaveChanges();
    191 }
    192
    193 /// <summary>
    194 /// 获取当前实例的主键
    195 /// </summary>
    196 /// <param name="t">实例</param>
    197 /// <returns>Object</returns>
    198 public object GetKeyValue(T t)
    199 {
    200 var key =
    201 typeof(T).GetProperties().FirstOrDefault(
    202 p => p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), true).Length != 0);
    203 return (key != null) ? key.GetValue(t, null) : null;
    204 }
    205 }

      仓储定义完成。它包含了基本的增、删、改、查功能。无需过多的修饰,作为仓储单元,它的任务就是这么四个操作而已。

      接下来就是我们的Unit of work的定义了。

     View Code 

    namespace Framework.Repositories
    {
    /// <summary>
    /// 工作单元接口定义
    /// </summary>
    public interface IUnitOfWork: IDisposable
    {
    /// <summary>
    /// 获取数据库上下文
    /// </summary>
    UsersContext DbContext { get; }

    /// <summary>
    /// 执行自定义SQL语句。
    /// 该方法提供了一个直接操作数据库表的实现。
    /// </summary>
    /// <param name="commandText">SQL语句</param>
    /// <param name="parameters">参数列表</param>
    /// <returns>Integer</returns>
    int ExecuteSqlCommand(string commandText, params object[] parameters);

    /// <summary>
    /// 提交事务
    /// </summary>
    /// <returns>Integer</returns>
    int Commit();

    /// <summary>
    /// 获取指定类型的仓储实例
    /// </summary>
    /// <typeparam name="T">泛型参数</typeparam>
    /// <returns>IRepository</returns>
    IRepository<T> Repositry<T>() where T : class;
    }

    /// <summary>
    /// 工作单元实现定义
    /// </summary>
    public class UnitOfWork : IUnitOfWork
    {
    /// <summary>
    /// 用户数据库上下文
    /// </summary>
    private UsersContext dbContext = null;

    /// <summary>
    /// 获取数据库上下文
    /// </summary>
    public UsersContext DbContext { get { return dbContext; } }

    /// <summary>
    /// 构造器
    /// </summary>
    public UnitOfWork()
    {
    dbContext = new UsersContext();
    }

    /// <summary>
    /// 构造器
    /// </summary>
    /// <param name="context">数据库上下文</param>
    public UnitOfWork(UsersContext context)
    {
    dbContext = context;
    }

    /// <summary>
    /// 析构器
    /// </summary>
    public void Dispose()
    {
    if (dbContext != null)
    dbContext.Dispose();
    GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 获取指定类型的仓储实例
    /// </summary>
    /// <typeparam name="T">泛型参数</typeparam>
    /// <returns>IRepository</returns>
    public IRepository<T> Repositry<T>() where T : class
    {
    return new Repository<T>(DbContext);
    }

    /// <summary>
    /// 提交事务
    /// </summary>
    /// <returns>Integer</returns>
    public int Commit()
    {
    return dbContext.SaveChanges();
    }

    /// <summary>
    /// 执行自定义SQL语句。
    /// 该方法提供了一个直接操作数据库表的实现。
    /// </summary>
    /// <param name="commandText">SQL语句</param>
    /// <param name="parameters">参数列表</param>
    /// <returns>Integer</returns>
    public int ExecuteSqlCommand(string commandText, params object[] parameters)
    {
    return dbContext.Database.ExecuteSqlCommand(commandText, parameters);
    }
    }
    }

      OK,基础类型的定义已经完成,看看我们如何使用它吧。先模拟一个场景:当前要添加一个用户,同时在添加用户的时候,要吧该用户增加到指定的部门列表(UserDepartment)下。

     View Code

    public void AddNewUser(string userName, string department)
    {
    using (IUnitOfWork unit = new UnitOfWork())
    {
    //获取用户类型的仓储
    var usrRep = unit.Repositry<UserProfile>();
    //创建新用户
    var User = new UserProfile { UserName = userName };

    //将用户信息添加到数据库。
    //注意:我们没有使用Create接口的第二个参数,即表示第二个参数默认为false,这表示当前操作暂时不提交到数据库。
    //如果使用 usrRep.Create(User, true), 则表示直接提交当前记录到数据库。
    //假如有兴趣,可以尝试第二个参数为true,执行该句之后人为抛出一个异常,看看数据库是如何发生变化的。
    usrRep.Create(User);

    //throw new Exception("");

    //获取部门类型的仓储
    var depRep = unit.Repositry<Department>();
    //根据部门名称获取部门信息
    var Department = depRep.FirstOrDefault(p => p.Name.Equals(department));
    //将当前用户添加到该部门内
    Department.DepartmentUsers.Add(new UserDepartment { UserId = User.UserId, DepartmentId = Department.Id });

    //提交前面所有的操作。这个才是最关键的。没有这句,一切都是瞎忙活!!!
    unit.Commit();
    }
    }

      当然,通常我们不会这么去做。我们会在UserProfile的定义中添加一个Department集合,同样会在Department中添加一个UserProfile集合,构建用户与部门的多对多关系(EntityFramework的多种数据映射关系以后我们会提到,网上相应的资料也很多),这样就可以很容易的只是用用户仓储实例进行操作,只需一个usrRep.Create(User, true)操作就可以完成上面的业务。不过我们只是为了说明Unitofwork如何工作,大家不必太较真。

      好了,今天就此结束。希望我能坚持不懈,也希望大家能一起见证我们努力的结果。

     
    分类: .NET MVC
  • 相关阅读:
    C#设计模式学习笔记-单例模式
    面向对象的七种设计原则
    继承 示例1
    继承和多态的那些事
    体检套餐管理项目
    魔兽登录系统
    清空表
    mysql批量插入
    mkdir用大括号同时建立多个同级和下级目录
    linux查看机器位数
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3900191.html
Copyright © 2011-2022 走看看