zoukankan      html  css  js  c++  java
  • MVC+UnitOfWork+Repository+EF 之我见

    UnitOfWork+Repository模式简介:

          每次提交数据库都会打开一个连接,造成结果是:多个连接无法共用一个数据库级别的事务,也就无法保证数据的原子性、一致性。解决办法是:在Repository的CRUD操作基础上再包装一层,提供统一的入口,让服务层调用。同一个UnitOfWork实例对象下所有的Repository都共同一个数据库上下文对象(ps:EF用的是DbContext),也就是共用一个事物。提交数据库时,只要有一个操作失败,那么所有的操作都被视为失败。

    项目结构:

    关键代码:

    AggregateRoot.cs:

     1 using System;
     2 using System.Collections.Generic;
     3 
     4 namespace CMS.Domain.Core
     5 {
     6     /// <summary>
     7     /// 表示聚合根类型的基类型。
     8     /// </summary>
     9     public abstract class AggregateRoot : IAggregateRoot
    10     {
    11         #region 方法
    12 
    13         public virtual IEnumerable<BusinessRule> Validate()
    14         {
    15             return new BusinessRule[] { };
    16         }
    17 
    18         #endregion
    19 
    20         #region Object 成员
    21 
    22         public override bool Equals(object obj)
    23         {
    24             if (obj == null)
    25                 return false;
    26 
    27             if (ReferenceEquals(this, obj))
    28                 return true;
    29 
    30             IAggregateRoot ar = obj as IAggregateRoot;
    31 
    32             if (ar == null)
    33                 return false;
    34 
    35             return this.Id == ar.Id;
    36         }
    37 
    38         public override int GetHashCode()
    39         {
    40             return this.Id.GetHashCode();
    41         }
    42 
    43         #endregion
    44 
    45         #region IAggregateRoot 成员
    46 
    47         public Guid Id
    48         {
    49             get;
    50             set;
    51         }
    52 
    53         #endregion
    54     }
    55 }

    Channel.cs:

     1 using CMS.Domain.Core;
     2 
     3 namespace CMS.Domain.Entities
     4 {
     5     public class Channel : AggregateRoot
     6     {
     7         public string Name
     8         {
     9             get;
    10             set;
    11         }
    12 
    13         public string CoverPicture
    14         {
    15             get;
    16             set;
    17         }
    18 
    19         public string Desc
    20         {
    21             get;
    22             set;
    23         }
    24 
    25         public bool IsActive
    26         {
    27             get;
    28             set;
    29         }
    30 
    31         public int Hits
    32         {
    33             get;
    34             set;
    35         }
    36     }
    37 }

    IUnitOfWork.cs:

     1 using System;
     2 
     3 namespace CMS.Domain.Core.Repository
     4 {
     5     /// <summary>
     6     /// 工作单元
     7     /// 提供一个保存方法,它可以对调用层公开,为了减少连库次数
     8     /// </summary>
     9     public interface IUnitOfWork : IDisposable
    10     {
    11         #region 方法
    12 
    13         IRepository<T> Repository<T>() where T : class, IAggregateRoot;
    14 
    15         void Commit();
    16 
    17         #endregion
    18     }
    19 }

    UnitOfWork.cs:

     1 using CMS.Common;
     2 using CMS.Domain.Core;
     3 using CMS.Domain.Core.Repository;
     4 using System;
     5 using System.Collections;
     6 using System.Collections.Generic;
     7 
     8 namespace CMS.Infrastructure
     9 {
    10     public class UnitOfWork : IUnitOfWork, IDisposable
    11     {
    12         #region 变量
    13 
    14         private bool _disposed;
    15         private readonly IDbContext _dbContext;
    16         private Hashtable _repositories;
    17 
    18         #endregion
    19 
    20         #region 构造函数
    21 
    22         public UnitOfWork(IDbContext dbContext)
    23         {
    24             this._dbContext = dbContext;
    25             this._repositories = new Hashtable();
    26         }
    27 
    28         #endregion
    29 
    30         #region 方法
    31 
    32         public virtual void Dispose(bool disposing)
    33         {
    34             if (!this._disposed)
    35                 if (disposing)
    36                     this._dbContext.Dispose();
    37 
    38             this._disposed = true;
    39         }
    40 
    41         #endregion
    42 
    43         #region IUnitOfWork 成员
    44 
    45         public IRepository<T> Repository<T>() where T : class, IAggregateRoot
    46         {
    47             var typeName = typeof(T).Name;
    48 
    49             if (!this._repositories.ContainsKey(typeName))
    50             {
    51                 52 
    53                 var paramDict = new Dictionary<string, object>();
    54                 paramDict.Add("context", this._dbContext);
    55 
    56                 //Repository接口的实现统一在UnitOfWork中执行,通过Unity来实现IOC,同时把IDbContext的实现通过构造函数参数的方式传入
    57                 var repositoryInstance = UnityConfig.Resolve<IRepository<T>>(paramDict);
    58 
    59                 if (repositoryInstance != null)
    60                     this._repositories.Add(typeName, repositoryInstance);
    61             }
    62 
    63             return (IRepository<T>)this._repositories[typeName];
    64         }
    65 
    66         public void Commit()
    67         {
    68             this._dbContext.SaveChanges();
    69         }
    70 
    71         #endregion
    72 
    73         #region IDisposable 成员
    74 
    75         public void Dispose()
    76         {
    77             this.Dispose(true);
    78 
    79             GC.SuppressFinalize(this);
    80         }
    81 
    82         #endregion
    83     }
    84 }

    BaseRepository.cs:

     1 using CMS.Domain.Core;
     2 using CMS.Domain.Core.Repository;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.Data.Entity;
     6 using System.Linq;
     7 using System.Linq.Expressions;
     8 
     9 namespace CMS.Infrastructure
    10 {
    11     public class BaseRepository<T> : IRepository<T> where T : class, IAggregateRoot
    12     {
    13         #region 变量
    14 
    15         private readonly DbContext _db;
    16         private readonly IDbSet<T> _dbset;
    17 
    18         #endregion
    19 
    20         #region 构造函数
    21 
    22         public BaseRepository(IDbContext context)
    23         {
    24             this._db = (DbContext)context;
    25             this._dbset = this._db.Set<T>();
    26         }
    27 
    28         #endregion
    29 
    30         #region IRepository 成员
    31 
    32         public void Add(T item)
    33         {
    34             this._dbset.Add(item);
    35         }
    36 
    37         public void Remove(T item)
    38         {
    39             this._dbset.Remove(item);
    40         }
    41 
    42         public void Modify(T item)
    43         {
    44             this._db.Entry(item).State = EntityState.Modified;
    45         }
    46 
    47         public T Get(Expression<Func<T, bool>> filter)
    48         {
    49             return this._dbset.Where(filter).SingleOrDefault();
    50         }
    51 
    52         public IEnumerable<T> GetAll()
    53         {
    54             return this._dbset.ToList();
    55         }
    56 
    57         public IEnumerable<T> GetPaged<KProperty>(int pageIndex, int pageSize, out int total, Expression<Func<T, bool>> filter, Expression<Func<T, KProperty>> orderBy, bool ascending = true, string[] includes = null)
    58         {
    59             pageIndex = pageIndex > 0 ? pageIndex : 1;
    60 
    61             var result = this.GetFiltered(filter, orderBy, ascending, includes);
    62 
    63             total = result.Count();
    64 
    65             return result.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
    66         }
    67 
    68         public IEnumerable<T> GetFiltered<KProperty>(Expression<Func<T, bool>> filter, Expression<Func<T, KProperty>> orderBy, bool ascending = true, string[] includes = null)
    69         {
    70             var result = filter == null ? this._dbset : this._dbset.Where(filter);
    71 
    72             if (ascending)
    73                 result = result.OrderBy(orderBy);
    74             else
    75                 result = result.OrderByDescending(orderBy);
    76 
    77             if (includes != null && includes.Length > 0)
    78             {
    79                 foreach (var include in includes)
    80                 {
    81                     result = result.Include(include);
    82                 }
    83             }
    84 
    85             return result.ToList();
    86         }
    87 
    88         #endregion
    89     }
    90 }

    IDbContext.cs:

     1 namespace CMS.Infrastructure
     2 {
     3     public interface IDbContext
     4     {
     5         #region 方法
     6 
     7         int SaveChanges();
     8 
     9         void Dispose();
    10 
    11         #endregion
    12     }
    13 }

    CMSDbContext.cs:

     1 using CMS.Infrastructures.Mapping;
     2 using System.Data.Entity;
     3 using System.Data.Entity.ModelConfiguration.Conventions;
     4 
     5 namespace CMS.Infrastructure
     6 {
     7     public class CMSDbContext : DbContext, IDbContext
     8     {
     9         #region 构造函数
    10 
    11         public CMSDbContext()
    12             : base("SqlConnectionString")
    13         {
    14 
    15         }
    16 
    17         #endregion
    18 
    19         #region DbContext 重写
    20 
    21         protected override void OnModelCreating(DbModelBuilder modelBuilder)
    22         {
    23             modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    24 
    25             modelBuilder.Configurations.Add(new ChannelEntityConfiguration());
    26         }
    27 
    28         #endregion
    29     }
    30 }

    UnityConfig.cs:

     1 using Microsoft.Practices.Unity;
     2 using Microsoft.Practices.Unity.Configuration;
     3 using System;
     4 using System.Collections.Generic;
     5 
     6 namespace CMS.Common
     7 {
     8     public class UnityConfig
     9     {
    10         #region 属性
    11 
    12         public static IUnityContainer Container
    13         {
    14             get
    15             {
    16                 return container.Value;
    17             }
    18         }
    19 
    20         #endregion
    21 
    22         #region 方法
    23 
    24         private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(
    25             () =>
    26             {
    27                 var container = new UnityContainer();
    28 
    29                 RegisterTypes(container);
    30 
    31                 return container;
    32             });
    33 
    34         private static void RegisterTypes(IUnityContainer container)
    35         {
    36             container.LoadConfiguration();
    37         }
    38 
    39         public static T Resolve<T>(IDictionary<string, object> paramDict = null)
    40         {
    41             var list = new ParameterOverrides();
    42 
    43             if (paramDict != null && paramDict.Count > 0)
    44             {
    45                 foreach (var item in paramDict)
    46                 {
    47                     list.Add(item.Key, item.Value);
    48                 }
    49             }
    50 
    51             return Container.Resolve<T>(list);
    52         }
    53 
    54         #endregion
    55     }
    56 }

    ChannelApplcationService.cs:

     1 using AutoMapper;
     2 using CMS.Domain.Core.Repository;
     3 using CMS.Domain.Entities;
     4 using CMS.DTO;
     5 using Microsoft.Practices.Unity;
     6 
     7 namespace CMS.Applcation
     8 {
     9     public class ChannelApplcationService
    10     {
    11         #region 属性
    12 
    13         [Dependency]
    14         public IUnitOfWork UnitOfWork { get; set; }
    15 
    16         #endregion
    17 
    18         #region 方法
    19 
    20         public Response<bool> Add(ChannelDTO dto)
    21         {
    22             var resp = new Response<bool>();
    23             var channel = Mapper.Map<Channel>(dto);
    24 
    25             using (this.UnitOfWork)
    26             {
    27                 var channelAddRepository = this.UnitOfWork.Repository<Channel>();
    28 
    29                 channelAddRepository.Add(channel);
    30 
    31                 this.UnitOfWork.Commit();
    32             }
    33 
    34             resp.Result = true;
    35 
    36             return resp;
    37         }
    38 
    39         #endregion
    40     }
    41 }

    序列图:

    心得体会:

    1. Repository的CRUD操作只能作用于继承了AggregateRoot基类的DomainObject(ps:以实际项目情况为准,可以做适当的妥协)。

    2. DomainObject中涉及到集合类型(如IList,ISet等)的聚合属性需要加“virtual”关键字,让ORM框架识别做Lazyload处理。

    3. 各自独立的业务逻辑写在对应的DomainObject方法中,方法体内只能处理自身以及内部聚合对象的数据和状态等信息,被聚合的对象不建议里面再有方法,只需定义相关属性即可(ps:涉及到对外通知、发布消息等场景以DomainEvent的方式处理,关于DomainEvent的概念和使用会开新章进行简述)。

    4. 把需要多个DomainObject交互和协调的业务逻辑放到DomainService中(ps:在Applcation Layer中调用。另外DomainService是否能调用Repository对象我一直很困惑,因为看过有代码是这么写的,但又有人不建议这么做......)。

    5. 在AggregateRoot基类中定义验证BusinessRule的虚方法,供子类重写,并在统一的地方执行(比如Applcation Layer)

    6. 定义DomainException,用来封装Domain Layer层的异常信息,对上层(Applcation Layer)暴露。

    7. Applcation Layer代码的主要作用(可用WCF、WebAPI或直接Dll引用等方式对上层(UI Layer)暴露)

    • 接收UI Layer传递的DTO对象。
    • 通过AutoMapper组件转换成对应的DomainObject,并调用其方法(ps:内部业务逻辑的封装)。
    • 调用Repository对象来实现CRUD操作(ps:这时数据还只是在内存中)。
    • 调用UnitOfWork的Commit方法来实现数据的真正提交(ps:事物级别的)。

       所以可以看出Applcation Layer主要用来处理业务的执行顺序,而不是关键的业务逻辑。

       Applcation Layer如果用WCF或WebAPI的方式对外暴露有个好处,可以针对其作负载均衡,坏处是额外增加了IIS的请求开销。

    8. DTO和DomainObject区别

        DTO(ps:为了简单起见,这里把DTO和ViewModel放在一块说了):

    • 根据实际业务场景加上Required、StringLength等验证特性,结合MVC框架的内部验证机制,可在Controller层做到数据的有效性验证(ps:永远都不要轻易相信浏览器端提交的数据,即使已经有了js脚本验证......)。
    • 负责View数据的展现和表单提交时数据的封装。
    • 负责把数据从UI Layer传递到Applcation Layer,里面只能有属性,而且是扁平的,结构简单的属性。

        DomainObject:通俗点说就是充血模型,包括属性和行为,在DDD整个框架设计体系中占非常重要的地位,其涵盖了整个软件系统的业务逻辑、业务规则、聚合关系等方面。(ps;如果业务很简单,可以只有属性)

    9. UI Layer:自我学习UnitOfWork+Repository以来,一直用的是MVC框架做前台展现,选择的理由:1. Unity4MVC的IOC功能非常强大,2. 天生支持AOP的思想,3. 更加传统、原始的Web处理方式,4. Areas模块对插件化设计的支持,5. Ajax、ModelBuilder、JSON、验证,6. 整个Http访问周期内提供的各种扩展点等。

  • 相关阅读:
    hdu1848(sg函数打表)
    hdu1850(nim博弈)
    hdu1847(sg函数&yy)
    hdu2147(yy)
    poj2133(sg函数)
    Educational Codeforces Round 18D(完全二叉树中序遍历&lowbit)
    atcoder057D(组合数模板)
    euler证明
    04_过滤器Filter_04_Filter生命周期
    04_过滤器Filter_03_多个Filter的执行顺序
  • 原文地址:https://www.cnblogs.com/chenwei1984/p/4203073.html
Copyright © 2011-2022 走看看