关于MVC EF架构及Repository模式的一点心得
一直都想写博客,可惜真的太懒了或者对自己的描述水平不太自信,所以。。。一直都是不想写的状态,关于领域驱动的东西看了不少,但是由于自己水平太差加上工作中实在用不到,所以一直处于搁置状态,最近心血来潮突然想重新写一个自己的项目架构,于是开始了新一论的学习历程。
在设计之前我理了一下自己的大致需求也参考了不同人的项目架构,在此特别感谢郭明峰的OSharp给我的启示,每个人对架构的需求和使用习惯都是不一至的,在不同大小的项目上使用的架构也不尽相同,如何取舍性能、开发速度及维护性的问题上每开发者都在自己心中有一杆秤,下面谈谈我理想中的架构,它应该提供一些常用的操作封装及各种工具方便实现多个功能,他应该包括数据操作的方便处理,他应该提供日志及缓存的处理功能,它的每种实现是自己换的但又得具有默认值让我在做私单的时候更快捷方便,基于以上特点于是总结了如下模块:
1,工具库:提供了各种常用操作,如字符串截取,各种验证之类的helper类
2,核心库:提供缓存日志数据的接口及缓存日志的基础实现
3,WEB库:针对网站相关的扩展及操作封装
4,MVC库:针对MVC网站的相关扩展及操作封装
5,基础数据库模块实现库(如:EF)
回到每个架构的重点:数据访问模块上来,现在比较流行使用EF+ IUnitOfWork+Repository来实现数据的处理,诚然这种方式让每一个模块相互的依赖变得更小,更方便测试,更XXX,但是在我的实践中,还是发现他真的麻烦,在我做一个中小项目的时候,添加一个普通的表不得不去添加实体,实体配置,仓储接口,仓储实现,服务接口,服务实现,controller viewmodel,view一系列下来即便是拷贝也是非常慢,即使我们可以用代码生成器,我还是觉得太麻烦了。当然Repository模式的反对者也不少,作为一种数据操作的隔离手段,我个人觉得Repository还是很有必要存在的,经过各种取舍,最后自己使用了一种折中的方式来使用Repository,我的方式是使用IStore来代替IUnitOfWork,本质是一样的,只是IStore实现的功能要多一些,同时使用了IStore使得我们可以很容易的替换我们的数据库功能实现,如下图:
由于一般的项目不需要一人写定义一人写实现,所以直接省去了IUserRepository及IUserService这种并不会修改实现的接口,当然如果有团队共同开发的时候使用也是完全没有问题的,有了以上的图就开以开始实现代码了:
IDataStore:
1 /// <summary> 2 /// 数据存储器接口 3 /// </summary> 4 public interface IStore : IDisposable 5 { 6 #region UnitOfWork 7 /// <summary> 8 /// 获取或设置 是否开启事务提交 9 /// </summary> 10 bool TransactionEnabled { get; set; } 11 /// <summary> 12 /// 提交操作 13 /// </summary> 14 bool Commit(); 15 /// <summary> 16 /// 异步提交 17 /// </summary> 18 Task<bool> CommitAsync(); 19 #endregion 20 21 #region 基础操作 22 /// <summary> 23 /// 添加对像 24 /// </summary> 25 void Add<TEntity>(TEntity entity) where TEntity : class; 26 /// <summary> 27 /// 更新对像 28 /// </summary> 29 void Update<TEntity>(TEntity entity) where TEntity : class; 30 /// <summary> 31 /// 删除对像 32 /// </summary> 33 void Remove<TEntity>(TEntity entity) where TEntity : class; 34 #endregion 35 36 #region 查询操作 37 /// <summary> 38 /// 获取数据集查询对像 39 /// </summary> 40 IQueryable<TEntity> GetQueryEntities<TEntity, TKey>() where TEntity : class; 41 #endregion 42 }
IRepository:
/// <summary> /// 仓储接口 /// </summary> /// <typeparam name="TEntity">实体类型</typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> public interface IRepository<TEntity, TKey> { #region 属性 /// <summary> /// 获取当前数据存储器 /// </summary> IStore DataStore { get; } /// <summary> /// 获取当前实体的查询数据集 /// </summary> IQueryable<TEntity> Entities { get; } #endregion #region 基础操作 /// <summary> /// 添加实体到仓库 /// </summary> void Add(TEntity entity); /// <summary> /// 更新仓库中的实体 /// </summary> void Update(TEntity entity); /// <summary> /// 从仓库删除指定key的实体 /// </summary> void Remove(TEntity entity); #endregion #region 查询 /// <summary> /// 获取指定key的实体 /// </summary> TEntity FindByKey(TKey key); #endregion }
RepositroyBase:
1 /// <summary> 2 /// 仓储基类 3 /// </summary> 4 public abstract class RepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey> 5 where TEntity : EntityBase<TKey>, new() 6 { 7 8 public IStore DataStore { get; set; } 9 10 public IQueryable<TEntity> Entities 11 { 12 get { return this.DataStore.GetQueryEntities<TEntity, TKey>(); } 13 } 14 15 public void Add(TEntity entity) 16 { 17 this.DataStore.Add<TEntity>(entity); 18 } 19 20 public void Update(TEntity entity) 21 { 22 this.DataStore.Update<TEntity>(entity); 23 } 24 25 public void Remove(TEntity entity) 26 { 27 this.DataStore.Remove<TEntity>(entity); 28 } 29 30 public TEntity FindByKey(TKey key) 31 { 32 return this.Entities.Where(t => t.Id.Equals(key)).SingleOrDefault(); 33 } 34 }
当然BaseEntity这种东西就看自己爱好添加了,按key删除,按条件更新的方法这里就不写了
,另外关于返回Repository返回IEnumerable还是IQueryable这个问题我个人认为如果不按照DDD的的标准来看,一个仓库每次拿你想要的东西还是给你一个通道你想拿多少就拿多少本质上都是可以的,不过对于隔离开发人员直接操作数据确实存在隐患,不过谁叫他简单呢?看完定义,然后如果要使用EF做为ORM来操作数据库或者任意继承至IDataStore的库来完成数据操作,不过这里要自己写的话,解析表达式还是有点困难的。。贴下简单EF基类的简单实现:
1 /// <summary> 2 /// EF数据存储器(抽象类) 3 /// </summary> 4 public abstract class EFDataStore : DbContext, IStore 5 { 6 #region DbContext 7 protected EFDataStore() { } 8 protected EFDataStore(DbCompiledModel model) : base(model) { } 9 public EFDataStore(string nameOrConnectionString) : base(nameOrConnectionString) { } 10 public EFDataStore(DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection) { } 11 public EFDataStore(ObjectContext objectContext, bool dbContextOwnsObjectContext) : base(objectContext, dbContextOwnsObjectContext) { } 12 public EFDataStore(string nameOrConnectionString, DbCompiledModel model) : base(nameOrConnectionString, model) { } 13 public EFDataStore(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection) { } 14 /// <summary> 15 /// 实体映射集合 16 /// </summary> 17 public IEnumerable<IEntityMapper> EntityConfigurations { get; set; } 18 19 protected override void OnModelCreating(DbModelBuilder modelBuilder) 20 { 21 //foreach (var mapper in EntityConfigurations) 22 //{ 23 // mapper.RegistTo(modelBuilder.Configurations); 24 //} 25 } 26 #endregion 27 28 public bool TransactionEnabled { get; set; } 29 30 public IQueryable<TEntity> GetQueryEntities<TEntity, TKey>() where TEntity : class 31 { 32 return this.Set<TEntity>(); 33 } 34 35 public bool Commit() 36 { 37 var r = this.SaveChanges(); 38 //this.Dispose(); 39 return r > 0; 40 } 41 42 public async Task<bool> CommitAsync() 43 { 44 return (await this.SaveChangesAsync()) > 0; 45 } 46 47 public void Add<TEntity>(TEntity entity) where TEntity : class 48 { 49 this.Set<TEntity>().Add(entity); 50 } 51 52 public void Update<TEntity>(TEntity entity) where TEntity : class 53 { 54 this.Entry(entity).State = EntityState.Modified; 55 } 56 57 public void Remove<TEntity>(TEntity entity) where TEntity : class 58 { 59 this.Entry(entity).State = EntityState.Deleted; 60 } 61 }
这。写着写着自己都有点不知所云了,希望对正在学习阶段的朋友有一点点帮助!