一般不建议在业务逻辑层直接访问数据库。由于这样可能会致使以下结果:数据库
在业务逻辑层经过仓库模式访问数据则能够实现以下特色:编程
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也能够实现。
若有不对,请多多指教。