一般不建議在業務邏輯層直接訪問數據庫。由於這樣可能會致使以下結果:數據庫
在業務邏輯層經過倉庫模式訪問數據則能夠實現以下特色:編程
Entity
以便在編譯時識別問題而不是在運行時使用倉儲模式是爲了分離業務層和數據源層,並實現業務層的Model和數據源層的Model映射。(ViewModel和Entity之間的映射)。即業務邏輯層應該和數據源層無關,業務層只關心結果,數據源層關心細節。緩存
數據源層和業務層之間的分離有三個好處:併發
全部的倉儲要實現該接口。該接口定義了對數據的基本操做。函數
public interface IRepository<TEntity> where TEntity : class { #region 屬性 //IQueryable Entities { get; } #endregion #region 公共方法 void Insert(TEntity entity); void Insert(IEnumerable<TEntity> entities); void Delete(object id); void Delete(TEntity entity); void Delete(IEnumerable<TEntity> entities); void Update(TEntity entity); TEntity GetByKey(object key); #endregion }
該類爲倉儲的泛型基類,實現以前定義的倉儲接口(IRepository
每一個表都會對應一個實體(Entity)。每一個實體(Entity)對應一個倉儲。把實體做爲泛型倉儲基類的參數,來實現每一個實體對應的倉儲。測試
(使用泛型倉儲基類能夠把實體做爲泛型參數來建立對應的倉儲。)this
//泛型倉儲基類 public class EFBaseRepository<TEntity> : IRepository<TEntity> where TEntity : class { //數據上下文 internal DbContext context; //數據集 internal DbSet<TEntity> dbSet; public EFBaseRepository(DbContext context) { this.context = context; this.dbSet = context.Set<TEntity>(); } //public IQueryable Entities => context.Set<TEntity>(); public void Delete(object id) { TEntity entityToDelete = dbSet.Find(id); Delete(entityToDelete); } public void Delete(IEnumerable<TEntity> entities) { dbSet.RemoveRange(entities); } public void Delete(TEntity entityToDelete) { if (context.Entry(entityToDelete).State == EntityState.Detached) { dbSet.Attach(entityToDelete); } dbSet.Remove(entityToDelete); } public TEntity GetByKey(object key) { return dbSet.Find(key); } public void Insert(TEntity entity) { dbSet.Add(entity); } public void Insert(IEnumerable<TEntity> entities) { dbSet.AddRange(entities); } public void Update(TEntity entity) { dbSet.Attach(entity); context.Entry(entity).State = EntityState.Modified; } public virtual IEnumerable<TEntity> Get( Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "", int topNum = 0) { IQueryable<TEntity> query = dbSet; if (filter != null) { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy != null) { query = orderBy(query); } if (topNum != 0) { return query.Take(topNum); } else { return query.ToList(); } } }
能夠把對Person的相關操做封裝到一個類中。在該類中實現PersonRepository(Person倉儲),操做PersonRepository來操做數據。設計
(數據庫有一個Person表,代碼中有一個TPerson實體)code
(該類提供與業務邏輯無關的倉儲操做)
public class PersonService { private EFBaseRepository<TPerson> _personRepository; public PersonService(DbContext dbContext) { var context = dbContext; //實現Person倉儲,TPerson爲對應的Entity _personRepository = new EFBaseRepository<TPerson>(context); } public IEnumerable<TPerson> Get() { return _personRepository.Get(); } public bool AddPerson(TPerson p) { try { _personRepository.Insert(p); } catch (Exception ex) { return false; } return true; } public bool EditPerson(TPerson p) { try { _personRepository.Update(p); } catch (Exception ex) { return false; } return true; } public bool DeletePerson(TPerson p) { try { _personRepository.Delete(p); } catch (Exception) { return false; } return true; } }
該類是對PersonService的封裝,是爲了提供同一數據上下文,和對數據上下文的釋放,及ViewModle和Entity的映射。
該類中每一個方法對應一個數據上下文。若是有須要對多個表操做,將這些操做封裝到一個數據上下文中。數據上下文的釋放在每一個方法中實現。
(全部與業務邏輯相關的操做在該類實現)
public class PersonManage { public IList<PersonVM> GetPersons() { using (var context = new RepositoryDemoEntities()) { var list = new PersonService(context).Get(); var result = new List<PersonVM>(); foreach (var item in list) { result.Add(new PersonVM { Name = item.Name, Age = item.Age, Home = item.Home, PersonID = item.Id }); } return result; } } public bool AddPerson(PersonVM p) { using (var context = new RepositoryDemoEntities()) { var result = new PersonService(context).AddPerson(new EntityFramework.TPerson { Name = p.Name, Home = p.Home, Age = p.Age, Id = p.PersonID }); context.SaveChanges(); return result; } } public bool DeletePerson(PersonVM p) { using (var context = new RepositoryDemoEntities()) { var result = new PersonService(context).DeletePerson(new EntityFramework.TPerson { Name = p.Name, Home = p.Home, Age = p.Age, Id = p.PersonID }); context.SaveChanges(); return result; } } public bool EditPerson(PersonVM p) { using (var context = new RepositoryDemoEntities()) { var result = new PersonService(context).EditPerson(new EntityFramework.TPerson { Name = p.Name, Home = p.Home, Age = p.Age, Id = p.PersonID }); context.SaveChanges(); return result; } } }
倉儲模式使得更容易實現單元測試
EntityFramework
包便可對每一個方法測試[TestClass] public class UnitTest1 { [TestMethod] public void TestShowPerson() { var res = new PersonManage().GetPersons(); Assert.AreNotEqual(0, res.Count); } [TestMethod] public void TestAddPerson() { var p = new PersonVM { Home = "zhengzhou", Age = 22, Name = "Jessica", PersonID = 3 }; var res = new PersonManage().AddPerson(p); Assert.IsTrue(res); } [TestMethod] public void TestEditPerson() { var persons = new PersonManage().GetPersons(); var p = persons[0]; p.Name = "fixed"; var res = new PersonManage().EditPerson(p); Assert.IsTrue(res); } [TestMethod] public void TestDeletePerson() { var persons = new PersonManage().GetPersons(); var p = persons[0]; var res = new PersonManage().DeletePerson(p); Assert.IsTrue(res); } }
小結:
倉儲模式經過對數據庫操做的封裝使數據訪問有一致性和對應用層和數據層的隔離,下降代碼的耦合性,更加容易實現單元測試。
工做單元模式是「維護一個被業務事務影響的對象列表,協調變化的寫入和併發問題的解決」
好比:新入校一個同窗,須要在班級,學校,學生,課程等多個表裏同時操做。這些表要麼都完成,要麼都不完成。具備一致性。
在倉儲模式中使用工做單元模式是爲了當你操做多個倉儲時,共用一個數據上下文(DbContext)使得這些倉儲具備一致性。
在Entity Framework中能夠把DbContext看成是一個工做單元。在同一個DbContext對多個倉儲操做。因此工做單元模式並非必定要本身實現,經過Entity Framework也能夠實現。
上面的倉儲模式其實經過對DbContext的使用了也實現了工做單元模式。
仍是簡單說下如何實現自定義的工做單元 (若是要對每一個操做都產生記錄的話,能夠擴展自定義工做單元來實現)
/// <summary> /// 工做單元接口 /// </summary> public interface IUnitOfWork { /// <summary> /// 保存當前單元操做的結果 /// </summary> /// <returns></returns> void Save(); }
UnitOfWork包含了全部的倉儲,及一個數據上下文,該類實現IDisposable接口(該接口的方法中釋放數據上下文)。
public class UnitOfWork : IUnitOfWork, IDisposable { private RepositoryDemoEntities1 context = new RepositoryDemoEntities1(); private EFBaseRepository<TPerson> _personRepository; public EFBaseRepository<TPerson> PersonRepository { get { return _personRepository ?? new EFBaseRepository<TPerson>(context); } } public void Save() { context.SaveChanges(); } private bool disposed = false; protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
定義一個UnitOfWork的字段,經過構造函數實例化該UnitOfWork
(該類提供與業務邏輯無關的倉儲操做)
public class PersonService { private UnitOfWork unit; public PersonService(UnitOfWork unitOfWork) { unit = unitOfWork; } public IEnumerable<TPerson> Get() { return unit.PersonRepository.Get(); } public bool AddPerson(TPerson p) { try { unit.PersonRepository.Insert(p); } catch (Exception ex) { return false; } return true; } public bool EditPerson(TPerson p) { try { unit.PersonRepository.Update(p); } catch (Exception ex) { return false; } return true; } public bool DeletePerson(TPerson p) { try { unit.PersonRepository.Delete(p); } catch (Exception) { return false; } return true; } }
在此將PersonService封裝,若是有對多個倉儲的操做,封裝在一個工做單元中。
(全部與業務邏輯相關的操做在該類實現)
public class PersonManage { public IList<PersonVM> GetPersons() { using (var unit = new UnitOfWork()) { var list = new PersonService(unit).Get(); var result = new List<PersonVM>(); foreach (var item in list) { result.Add(new PersonVM { Name = item.Name, Age = item.Age, Home = item.Home, PersonID = item.Id }); } return result; } } public bool AddPerson(PersonVM p) { using (var unit = new UnitOfWork()) { var result = new PersonService(unit).AddPerson(new EntityFramework.TPerson { Name = p.Name, Home = p.Home, Age = p.Age, Id = p.PersonID }); unit.Save(); return result; } } public bool DeletePerson(PersonVM p) { using (var unit = new UnitOfWork()) { var result = new PersonService(unit).DeletePerson(new EntityFramework.TPerson { Name = p.Name, Home = p.Home, Age = p.Age, Id = p.PersonID }); unit.Save(); return result; } } public bool EditPerson(PersonVM p) { using (var unit = new UnitOfWork()) { var result = new PersonService(unit).EditPerson(new EntityFramework.TPerson { Name = p.Name, Home = p.Home, Age = p.Age, Id = p.PersonID }); unit.Save(); return result; } } }
[TestClass] public class UnitTest1 { [TestMethod] public void TestShow() { var res = new PersonManage().GetPersons(); Console.WriteLine(res.Count); Assert.AreNotEqual(0, res.Count); } [TestMethod] public void TestAdd() { var res = new PersonManage().AddPerson(new PersonVM { Home = "meiguo", Age = 11, Name = "tidy" }); Assert.IsTrue(res); } [TestMethod] public void TestEdit() { var pmanage = new PersonManage(); var p = pmanage.GetPersons()[0]; p.Name = "fixed"; var res = pmanage.EditPerson(p); Assert.IsTrue(res); } [TestMethod] public void TestDelete() { var pmanage = new PersonManage(); var p = pmanage.GetPersons()[0]; var res = pmanage.DeletePerson(p); Assert.IsTrue(res); } }
小結:
工做單元模式是爲了實現業務的事務功能。經過一個數據上下文對相關的倉儲操做。可是也不是必需要本身實現模式,經過ORM也能夠實現。
若有不對,請多多指教。