關於Repository模式,在這篇文章中有介紹,Entity Framework返回IEnumerable仍是IQueryable? html
這篇文章介紹的是使用Entity Framework實現的Repositoy模式設計,歡迎各位拍磚.java
閱讀目錄:sql
1、實現的思路和結構圖express
2、Repository設計具體的實現代碼ide
3、Repository設計的具體的使用spa
4、總結設計
總結一下,Repository在實際使用中,有下面三種特色:code
Repository的共同性component
有一些公共的方法(增刪改查), 這些方法無關於Repository操做的是哪一個實體類,能夠把這些方法定義成接口IRepository<TEntity>, 而後有個基類BaseRepository<TEntity>實現該接口的方法。htm
常見的方法,好比Find, Filter, Delete, Create等
Repository的差別性
每一個Repository類又會有一些差別性,應當容許它們可以繼承BaseRepository<TEntity>以外,還可以再擴展本身的一些方法。因此每一個類均可以再定義一個本身特有的接口,定義一些屬於本身Repository的方法。
Repository的協同性
不一樣的Repository可能須要協同,Repository對數據的修改,須要在統一的保存.
最終實現的類結構圖以下:
IRepository<TEntity>接口定義了Repository共有的方法, BaseRepository<TEntity>實現了這些接口的方法。其它的Repository類再集成BaseRepository<TEntity>方法,就自然的獲得了對數據操做的基本方法。
IRepository<TEntity>代碼
public interface IRepository<TEntity> where TEntity : class { IQueryable<TEntity> All(); IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate); IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0, int size = 50); bool Contains(Expression<Func<TEntity, bool>> predicate); TEntity Find(params object[] keys); TEntity Find(Expression<Func<TEntity, bool>> predicate); TEntity Create(TEntity t); void Delete(TEntity t); void Delete(Expression<Func<TEntity, bool>> predicate); void Update(TEntity t); TEntity Single(Expression<Func<TEntity, bool>> expression); }
BaseRepository<TEntity>代碼
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class { protected readonly DbContext Context; public BaseRepository(DbContext context) { Context = context; } public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression) { return All().FirstOrDefault(expression); } public IQueryable<TEntity> All() { return Context.Set<TEntity>().AsQueryable(); } public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate) { return Context.Set<TEntity>().Where<TEntity>(predicate).AsQueryable<TEntity>(); } public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0, int size = 50) { var skipCount = index * size; var resetSet = filter != null ? Context.Set<TEntity>().Where<TEntity>(filter).AsQueryable() : Context.Set<TEntity>().AsQueryable(); resetSet = skipCount == 0 ? resetSet.Take(size) : resetSet.Skip(skipCount).Take(size); total = resetSet.Count(); return resetSet.AsQueryable(); } public virtual void Create(TEntity TObject) { Context.Set<TEntity>().Add(TObject); } public virtual void Delete(TEntity TObject) { Context.Set<TEntity>().Remove(TObject); } public virtual void Update(TEntity TObject) { try { var entry = Context.Entry(TObject); Context.Set<TEntity>().Attach(TObject); entry.State = EntityState.Modified; } catch (OptimisticConcurrencyException ex) { throw ex; } } public virtual int Delete(Expression<Func<TEntity, bool>> predicate) { var objects = Filter(predicate); foreach (var obj in objects) Context.Set<TEntity>().Remove(obj); return Context.SaveChanges(); } public bool Contains(Expression<Func<TEntity, bool>> predicate) { return Context.Set<TEntity>().Any(predicate); } public virtual TEntity Find(params object[] keys) { return Context.Set<TEntity>().Find(keys); } public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate) { return Context.Set<TEntity>().FirstOrDefault<TEntity>(predicate); } }
IUnitOfWork接口定義了方法獲取特定的Repository, 執行存儲過程, SaveChange方法提交修改,統一更新數據。
IUnitOfWork接口代碼:
public interface IUnitOfWork : IDisposable { TRepository GetRepository<TRepository>() where TRepository : class; void ExecuteProcedure(string procedureCommand, params object[] sqlParams); void SaveChanges(); }
UnitOfWork代碼, 代碼中使用到了Autofac中的IComponentContext來獲取Repository實例
public class UnitOfWork : IUnitOfWork { private readonly IComponentContext _componentContext; protected readonly DbContext Context; public UnitOfWorkRepository(DbContext context, IComponentContext componentContext) { Context = context; _componentContext = componentContext; } public TRepository GetRepository<TRepository>() where TRepository : class { return _componentContext.Resolve<TRepository>(); } public void ExecuteProcedure(string procedureCommand, params object[] sqlParams) { Context.Database.ExecuteSqlCommand(procedureCommand, sqlParams); } public void SaveChanges() { Context.SaveChanges(); } public void Dispose() { if (Context != null) Context.Dispose(); } }
這裏咱們定義一個操做Student的Repository類,看看如何實際用於開發中。這裏加入StudentRepository有本身特定的方法,須要獲取全部的Students,這個擴展的方法名字叫GetAllStudents
那麼定義一個接口IStudentRepository, 包含了方法GetAllStudents(), 同時繼承IRepository<Student>接口
public interface IStudentRepository : IRepository<Student> { IEnumerable<dynamic> GetAllStudents(); }
而後定義StudentRepository類來實現這個接口
public class StudentRepository : BaseRepository<Student>, IStudentRepository { private readonly SchoolContext _context; public StudentRepository(SchoolContext context) : base(context) { _context = context; } public IEnumerable<dynamic> GetAllStudents() { return _context.Students; } }
使用Repository的代碼以下:
IUnitOfWork unitOfWork = new UnitOfWork(); var studentRepository = unitOfWork.GetRepository<IStudentRepository>(); var students = studentRepository.GetAllStudents(); //同時你也可使用定義於IRepository<Student>中的方法, 好比 //unitOfWork.Delete(students.First()); //unitOfWork.SaveChanges();
上面的設計,把Repository的通用代碼剝離到父類中,同時又容許每一個Repository擴展本身的方法,達到了比較理想的狀態。
只是如今的設計和Autofac耦合了,可是想剝離Autofac的話,直接使用下面的方式獲取IStudentRepository的實例就很困難。
unitOfWorkRepository.GetRepository<IStudentRepository>();
若是有什麼好的辦法,歡迎指教。也歡迎各位拍磚。
最後,附上本文的相關源代碼. RepositoryDesign.zip有朋友反映這個設計有問題,但願你們批判的看待。若是有不一樣見解,歡迎指教。
感謝熱心的園友提供的意見。 這個Repository的設計是不成熟的,在使用了一段EF和看了一些文章以後,本身的一些探索和思考,尚未應用到實際項目中。
Eric.Chen,我寫的不是代碼 是寂寞, Vincent Yang 中提到的UnitOfWork問題,已經修改好.
郭明鋒 提到的Single方法不合適,已經改爲FirstOrDefault()
最新的源代碼 RepositoryDesign1.zip