前言
之前我們系統學習了EntityFramework,個人覺得有些東西不能學了就算完了,必須要學以致用,在Web API上也少不了增(C)、刪(D)、改(U)、查(R)。鑒於此,我們通過EF來實現Web API上的增刪改查。之前對於EF的基本操作都是很零散的,我們應該對於CRUD都是通過完整封裝來實現,並且也顯得比較專業,接下來首先對EF利用Responsitory倉儲模式進行完整封裝。
EntityFramework完整封裝
我們建立一個Core(核心類庫),里面存放有關EF的完成封裝。
第一步
建立所有實體的基類,將實體的公共屬性放入其中,取為BaseEntity
public class BaseEntity<T> { public T Id { get; set; } }
第二步
建立倉儲接口IRepository,包括基本的增、刪、改、查等方法
public interface IRepository<TEntity> where TEntity : class { /// <summary> /// 獲得數據列表 /// </summary> /// <returns></returns> IQueryable<TEntity> GetList(); /// <summary> /// 通過id獲得實體 /// </summary> /// <param name="id"></param> /// <returns></returns> TEntity GetById(object id); /// <summary> /// 添加實體 /// </summary> /// <param name="entity"></param> int Insert(TEntity entity); /// <summary> /// 添加實體集合 /// </summary> /// <param name="entities"></param> int Insert(IEnumerable<TEntity> entities); /// <summary> /// 刪除實體 /// </summary> /// <param name="entity"></param> int Delete(TEntity entity); /// <summary> /// 根據條件刪除實體 /// </summary> /// <param name="entities"></param> int DeleteByRequirement(Expression<Func<TEntity, bool>> func); /// <summary> /// 更新實體 /// </summary> /// <param name="entity"></param> int Update(TEntity entity); /// <summary> /// 更新實體集合 /// </summary> /// <param name="entities"></param> int Update(IEnumerable<TEntity> entities); }
第三步
利用倉儲服務RepositoryService實現上述倉儲接口IRepository
public class RepositoryService<TEntity> : IRepository<TEntity> where TEntity : class { private IDbContext Context; private bool IsNoTracking; /// <summary> /// 獲取實體集合 /// </summary> private IDbSet<TEntity> Entities { get { return this.Context.Set<TEntity>(); } } private DbEntityEntry Entry(TEntity entity) { return this.Context.Entry<TEntity>(entity); } public RepositoryService(IDbContext context, bool isNoTracking) { this.Context = context; this.IsNoTracking = isNoTracking; } /// <summary> /// 獲取所有數據 /// </summary> /// <returns></returns> public IQueryable<TEntity> GetList() { if (!IsNoTracking) return this.Entities.AsQueryable(); else return this.Entities.AsNoTracking().AsQueryable(); } /// <summary> /// 通過id獲取實體 /// </summary> /// <param name="id"></param> /// <returns></returns> public TEntity GetById(object id) { return Entities.Find(id); } /// <summary> /// 添加實體 /// </summary> /// <param name="entity"></param> public int Insert(TEntity entity) { Entities.Add(entity); return this.Context.SaveChanges(); } public int Insert(IEnumerable<TEntity> entities) { if (entities == null) throw new ArgumentNullException("entities"); foreach (var entity in entities) { Entities.Add(entity); } return this.Context.SaveChanges(); } /// <summary> /// 刪除實體 /// </summary> /// <param name="entity"></param> public int Delete(TEntity entity) { if (!IsNoTracking) this.Entities.Remove(entity); else this.Entities.Attach(entity); this.Entities.Remove(entity); return this.Context.SaveChanges(); } public int DeleteByRequirement(Expression<Func<TEntity, bool>> func) { var list = GetList().Where(func).ToList(); list.ForEach(e => { if (!IsNoTracking) this.Entities.Remove(e); else this.Entities.Attach(e); this.Entities.Remove(e); }); return this.Context.SaveChanges(); } /// <summary> /// 更新實體 /// </summary> /// <param name="entity"></param> public int Update(TEntity entity) { if (!IsNoTracking) return this.Context.SaveChanges(); else this.Context.Entry(entity).State = EntityState.Modified; return this.Context.SaveChanges(); } public int Update(IEnumerable<TEntity> entities) { if (entities == null) throw new ArgumentNullException("enetities"); if (!IsNoTracking) return this.Context.SaveChanges(); else foreach (var t in entities) { this.Context.Entry(t).State = EntityState.Modified; } return this.Context.SaveChanges(); } /// <summary> /// 釋放資源 /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (Context != null) { this.Context.Dispose(); this.Context = null; } } } }
第四步
用接口IDbContext封裝EF上下文DbContext中的公共方法
public interface IDbContext { /// <summary> /// 獲得實體集合 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <returns></returns> IDbSet<TEntity> Set<TEntity>() where TEntity : class; /// <summary> /// 執行存儲過程 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="commandText"></param> /// <param name="parameters"></param> /// <returns></returns> IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : class; /// <summary> /// 執行SQL語句查詢 /// </summary> /// <typeparam name="TElement"></typeparam> /// <param name="sql"></param> /// <param name="parameters"></param> /// <returns></returns> IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters); DbEntityEntry Entry<TEntity>(TEntity entity) where TEntity : class; /// <summary> /// 保存數據 /// </summary> /// <returns></returns> int SaveChanges(); /// <summary> /// 變更追蹤代碼 /// </summary> bool ProxyCreationEnabled { get; set; } /// <summary> /// DetectChanges方法自動調用 /// </summary> bool AutoDetectChangesEnabled { get; set; } /// <summary> /// 調用Dispose方法 /// </summary> void Dispose(); }
第五步
實現EF上下文中的數據庫連接、模型初始化以及映射等(也可以手動關閉全局變更追蹤相對比較靈活)
public class EFDbContext : DbContext, IDbContext { public EFDbContext(string connectionString) : base(connectionString) { } static EFDbContext() { Database.SetInitializer<EFDbContext>(new DropCreateDatabaseIfModelChanges<EFDbContext>()); } /// <summary> /// 一次性加載所有映射 /// </summary> /// <param name="modelBuilder"></param> protected override void OnModelCreating(DbModelBuilder modelBuilder) { var typesToRegister = Assembly.GetExecutingAssembly().GetTypes() .Where(type => !String.IsNullOrEmpty(type.Namespace)) .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in typesToRegister) { dynamic configurationInstance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(configurationInstance); } base.OnModelCreating(modelBuilder); } /// <summary> /// 獲得實體集合 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <returns></returns> public new IDbSet<TEntity> Set<TEntity>() where TEntity : class { return base.Set<TEntity>(); } /// <summary> /// 實體狀態 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="entity"></param> /// <returns></returns> public new DbEntityEntry Entry<TEntity>(TEntity entity) where TEntity : class { return base.Entry<TEntity>(entity); } /// <summary> /// 執行存儲過程 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="commandText"></param> /// <param name="parameters"></param> /// <returns></returns> public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : class { if (parameters != null && parameters.Length > 0) { for (int i = 0; i <= parameters.Length - 1; i++) { var p = parameters[i] as DbParameter; if (p == null) throw new Exception("Not support parameter type"); commandText += i == 0 ? " " : ", "; commandText += "@" + p.ParameterName; if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output) { commandText += " output"; } } } var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList(); bool acd = this.Configuration.AutoDetectChangesEnabled; try { this.Configuration.AutoDetectChangesEnabled = false; for (int i = 0; i < result.Count; i++) result[i] = this.Set<TEntity>().Attach(result[i]); } finally { this.Configuration.AutoDetectChangesEnabled = acd; } return result; } /// <summary> /// SQL語句查詢 /// </summary> /// <typeparam name="TElement"></typeparam> /// <param name="sql"></param> /// <param name="parameters"></param> /// <returns></returns> public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters) { return this.Database.SqlQuery<TElement>(sql, parameters); } /// <summary> /// 當查詢或者獲取值時是否啟動創建代理 /// </summary> public virtual bool ProxyCreationEnabled { get { return this.Configuration.ProxyCreationEnabled; } set { this.Configuration.ProxyCreationEnabled = value; } } /// <summary> /// 當查詢或者獲取值時指定是否開啟自動調用DetectChanges方法 /// </summary> public virtual bool AutoDetectChangesEnabled { get { return this.Configuration.AutoDetectChangesEnabled; } set { this.Configuration.AutoDetectChangesEnabled = value; } } }
以上就是對利用EntityFramework來實現基本操作的完整封裝。接下來就是相關類以及映射(場景:一個Student對應一個Flower,而一個Flower對應多個Student)
Student
public class Student : BaseEntity<int> { public string Name { get; set; } public int FlowerId { get; set; } public virtual Flower Flower { get; set; } }
Flower
public class Flower : BaseEntity<int> { public string Remark { get; set; } public virtual ICollection<Student> Students { get; set; } }
相關映射
public class StudentMap:EntityTypeConfiguration<Student> { public StudentMap() { ToTable("Student"); HasKey(p => p.Id); Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasRequired(p => p.Flower).WithMany(p => p.Students).HasForeignKey(p => p.FlowerId); } } public class FlowerMap:EntityTypeConfiguration<Flower> { public FlowerMap() { ToTable("Flower"); HasKey(p => p.Id); Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); } }
CRUD
接下來就是Web API控制器中執行增、刪等操作,我們創建一個StudentController控制器,然后首先創建倉儲服務。(執行Action方法,依據默認約定,未添加特性)
public IRepository<Student> _repository; public EFDbContext _ctx; public StudentController() { _ctx = new EFDbContext("DBByConnectionString"); _repository = new RepositoryService<Student>(_ctx, true); //關閉局部變更追蹤 }
執行R操作(即默認請求到HttpGet方法)
public IEnumerable<Student> GetAllStudent() { return _repository.GetList().Select(d => new Student { Name = d.Name, Flower = d.Flower, Id = d.Id }).ToList(); }
當執行此查詢操作時卻出錯了,真遺憾:
上述修改如下即可:
return _repository.GetList().ToList().Select(d => new Student { Name = d.Name, Flower = d.Flower, Id = d.Id }).ToList();
不知道還有沒有更好的解決方案,用ToList直接將所有數據進行加載到內存中,在性能上消耗比較大。(期待你的解決方案)
特此記錄
在此感謝園友(_天光雲影)給出的解決方案,在GetList之后利用 Linq 進行Select,最后進行ToList即可!!!
執行CUD等操作

public Student GetStudentById(int id) { var student = _repository.GetById(id); if (student == null) throw new HttpResponseException(HttpStatusCode.NotFound); else return student; } //添加操作(HttpPost) public HttpResponseMessage PostStudent(Student stu) { var insertStudent = _repository.Insert(stu); var response = Request.CreateResponse<Student>(HttpStatusCode.Created, stu); string uri = Url.Link("DefaultApi", new { id = stu.Id }); response.Headers.Location = new Uri(uri); return response; } //更新操作(HttpPut) public void PutStudent(int id, Student stu) { stu.Id = id; if (_repository.Update(stu) <= 0) { throw new HttpResponseException(HttpStatusCode.NotFound); } } //刪除操作(HttpDelete) public void DeleteStudent(int id) { Student stu = _repository.GetById(id); if (stu == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _repository.Delete(stu); }
總結
這節主要介紹了利用倉儲模式完整封裝EF來進行Web API基本操作,基本操作中關於返回狀態碼等信息,無非就是以下幾個對象
HttpResponseException 返回異常
HttpResponseMessage 返回信息(諸如狀態碼等)
HttpStatusCode 狀態碼枚舉(如頁面未找到等)
源代碼下載