EF架構~基于(yu)EF數據層的(de)實現
之前寫過關于實現一個完整的EF架構的文章,文章(zhang)的(de)(de)(de)(de)閱讀量(liang)也是滿大的(de)(de)(de)(de),自己很(hen)欣慰,但是,那篇文章(zhang)是我2011年(nian)(nian)寫(xie)的(de)(de)(de)(de),所以(yi),技術(shu)有(you)些不(bu)成熟,所以(yi)今天把我的(de)(de)(de)(de)2014年(nian)(nian)寫(xie)的(de)(de)(de)(de)EF底層架構(gou)公開一(yi)下(xia),這(zhe)個架構(gou)比(bi)2011年(nian)(nian)的(de)(de)(de)(de)有(you)了(le)很(hen)大程度(du)的(de)(de)(de)(de)提(ti)高,主(zhu)要在(zai)接口(kou)規范,查詢規范上,并引入了(le)排序功能,兩步對完善了(le)EF對數據的(de)(de)(de)(de)批量(liang)操作,可以(yi)說,這(zhe)次的(de)(de)(de)(de)架構(gou)是很(hen)有(you)看點的(de)(de)(de)(de)。
一(yi) 一(yi)個基礎操作接口
/// <summary> /// 基礎的數據操作規范 /// 與(yu)ORM架構無關 /// </summary> /// <typeparam name="TEntity"></typeparam> public interface IRepository<TEntity> where TEntity : class { /// <summary> /// 設定數(shu)據上下文,它一般由構架(jia)方法注入 /// </summary> /// <param name="unitOfWork"></param> void SetDbContext(IUnitOfWork unitOfWork); /// <summary> /// 添加實體并提交到(dao)數據服務器 /// </summary> /// <param name="item">Item to add to repository</param> void Insert(TEntity item); /// <summary> /// 移除實體(ti)并提交到(dao)數據服務器 /// 如(ru)果表存(cun)在約(yue)束,需要(yao)先(xian)刪(shan)除子表信息 /// </summary> /// <param name="item">Item to delete</param> void Delete(TEntity item); /// <summary> /// 修改(gai)實體并提交(jiao)到數據服務器 /// </summary> /// <param name="item"></param> void Update(TEntity item); /// <summary> /// 得到指定的實體集(ji)合(延時結果(guo)集(ji)) /// Get all elements of type {T} in repository /// </summary> /// <returns>List of selected elements</returns> IQueryable<TEntity> GetModel(); /// <summary> /// 根據(ju)主鍵得到實體 /// </summary> /// <param name="id"></param> /// <returns></returns> TEntity Find(params object[] id); }
二 一(yi)個(ge)擴展操作接口
/// <summary> /// 擴展的Repository操作規范 /// </summary> public interface IExtensionRepository<TEntity> : IRepository<TEntity>, IOrderableRepository<TEntity> where TEntity : class { /// <summary> /// 添加集合(he)[集合(he)數目不(bu)大(da)時用(yong)此方(fang)法,超大(da)集合(he)使(shi)用(yong)BulkInsert] /// </summary> /// <param name="item"></param> void Insert(IEnumerable<TEntity> item); /// <summary> /// 修改集合[集合數目不大時用(yong)(yong)此方法,超大集合使用(yong)(yong)BulkUpdate] /// </summary> /// <param name="item"></param> void Update(IEnumerable<TEntity> item); /// <summary> /// 刪除(chu)集合[集合數目不大時用此方法,超大集合使用批量刪除(chu)] /// </summary> /// <param name="item"></param> void Delete(IEnumerable<TEntity> item); /// <summary> /// 擴展更新方法,只對EF支持(chi) /// 注意本方(fang)法(fa)不能和GetModel()一起使用,它的表主鍵可以通過post或get方(fang)式(shi)獲取(qu) /// </summary> /// <param name="entity"></param> void Update<T>(Expression<Action<T>> entity) where T : class; /// <summary> /// 根據指定lambda表達式(shi),得到延時(shi)結果(guo)集 /// </summary> /// <param name="predicate"></param> /// <returns></returns> IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate); /// <summary> /// 根(gen)據(ju)指定lambda表達式,得到第一個實體(ti) /// </summary> /// <param name="predicate"></param> /// <returns></returns> TEntity Find(Expression<Func<TEntity, bool>> predicate); /// <summary> /// 批量添加,添加之前可以去(qu)除自增屬(shu)性(xing),默認不(bu)去(qu)除 /// </summary> /// <param name="item"></param> /// <param name="isRemoveIdentity"></param> void BulkInsert(IEnumerable<TEntity> item, bool isRemoveIdentity); /// <summary> /// 批(pi)量添加 /// </summary> /// <param name="item"></param> void BulkInsert(IEnumerable<TEntity> item); /// <summary> /// 批量更新 /// </summary> /// <param name="item"></param> void BulkUpdate(IEnumerable<TEntity> item, params string[] fieldParams); /// <summary> /// 批量刪(shan)除 /// </summary> /// <param name="item"></param> void BulkDelete(IEnumerable<TEntity> item); }
三 一個(ge)排序操(cao)作接口
/// <summary> /// 提供排序功能的(de)規范 /// </summary> public interface IOrderableRepository<TEntity> where TEntity : class { /// <summary> /// 帶排序的結(jie)果(guo)集 /// </summary> /// <param name="orderBy"></param> /// <returns></returns> IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy); /// <summary> /// 根(gen)據(ju)指定lambda表(biao)達式和排序方式,得到延時結果(guo)集 /// </summary> /// <param name="predicate"></param> /// <returns></returns> IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy, Expression<Func<TEntity, bool>> predicate); }
四 基(ji)于ef架構的規約查詢接口
/// <summary> /// EF底層(ceng)構架,關于規約功能的倉儲接口 /// </summary> /// <typeparam name="TEntity"></typeparam> public interface ISpecificationRepository<TEntity> : IExtensionRepository<TEntity> where TEntity : class { /// <summary> /// 根據指定規(gui)約,得到延(yan)時結果集 /// </summary> /// <param name="specification"></param> /// <returns></returns> IQueryable<TEntity> GetModel(ISpecification<TEntity> specification); /// <summary> /// 根據指定規(gui)約,得到第一(yi)個實體(ti) /// </summary> /// <param name="specification"></param> /// <returns></returns> TEntity Find(ISpecification<TEntity> specification); /// <summary> /// 帶排序功能的,根據指(zhi)定規約,得到結果集 /// </summary> /// <param name="orderBy"></param> /// <param name="specification"></param> /// <returns></returns> IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy, EntityFrameworks.Entity.Core.Specification.ISpecification<TEntity> specification); /// <summary> /// 保存之(zhi)后觸(chu)發 /// Occurs after data saved /// </summary> event Action<SavedEventArgs> AfterSaved; /// <summary> /// 保存之前觸發 /// Occurs before data saved /// </summary> event Action<SavedEventArgs> BeforeSaved; }
五 基于工作(zuo)單(dan)元(yuan)的標識(shi)接口
/// <summary> /// 數據上(shang)下文(wen)標(biao)識(shi)接口,它對于業務(wu)層應該是公開的 /// 它(ta)對(dui)于實現上下文(wen)的方法,它(ta)并不(bu)關心(xin),可以是(shi)linq2sql,ef,ado.net,nhibernate,memory,nosql等(deng) /// </summary> public interface IUnitOfWork { }
六 基于ef的(de)DbContext上下(xia)文的(de)倉儲的(de)實現(xian)
/// <summary> /// DbContext上(shang)下文倉儲功能類,領域(yu)上(shang)下文可以(yi)直接繼承它 /// 生(sheng)命(ming)周期(qi):數據上下文的生(sheng)命(ming)周期(qi)為一個HTTP請求的結束(shu) /// 相關(guan)說(shuo)明: /// 1 領域對象使用聲明IRepository和(he)IExtensionRepository接口得到不(bu)同的操作規范 /// 2 可以直接為上下注入Action<string>的委托實(shi)例,用(yong)來記錄savechanges產生的異(yi)常(chang) /// 3 可以訂閱BeforeSaved和AfterSaved兩個事件(jian),用來在方法提交前(qian)與提交后實現代碼(ma)注入 /// 4 所(suo)有領域db上下文都要(yao)繼承iUnitWork接(jie)口(kou),用(yong)來(lai)實現(xian)工作單元,這對于提升程序性(xing)能與(yu)為重要(yao) /// </summary> /// <typeparam name="TEntity"></typeparam> public class DbContextRepository<TEntity> : ISpecificationRepository<TEntity> where TEntity : class { #region Constructors public DbContextRepository(IUnitOfWork db, Action<string> logger) { UnitWork = db; Db = (DbContext)db; Logger = logger; ((IObjectContextAdapter)Db).ObjectContext.CommandTimeout = 0; } public DbContextRepository(IUnitOfWork db) : this(db, null) { } #endregion #region Properties /// <summary> /// 數(shu)據上下文 /// </summary> protected DbContext Db { get; private set; } /// <summary> /// 工作單元上下文,子類可以直(zhi)接使用它 /// </summary> protected IUnitOfWork UnitWork { get; set; } /// <summary> /// Action委托事(shi)例,在派生(sheng)類可以操作它 /// </summary> protected Action<string> Logger { get; private set; } #endregion #region Fields /// <summary> /// 數(shu)(shu)據總(zong)數(shu)(shu) /// </summary> int _dataTotalCount = 0; /// <summary> /// 數據總頁數 /// </summary> int _dataTotalPages = 0; /// <summary> /// 數(shu)據頁面大小(每次向數(shu)據庫提(ti)交的記錄數(shu)) /// </summary> private const int DataPageSize = 10000; #endregion #region Delegates & Event /// <summary> /// 保(bao)存之后 /// </summary> public event Action<SavedEventArgs> AfterSaved; /// <summary> /// 保存之前 /// </summary> public event Action<SavedEventArgs> BeforeSaved; #endregion #region IRepository<TEntity> 成員 public void SetDbContext(IUnitOfWork unitOfWork) { this.Db = (DbContext)unitOfWork; this.UnitWork = unitOfWork; } public virtual void Insert(TEntity item) { OnBeforeSaved(new SavedEventArgs(item, SaveAction.Insert)); Db.Entry<TEntity>(item); Db.Set<TEntity>().Add(item); this.SaveChanges(); OnAfterSaved(new SavedEventArgs(item, SaveAction.Insert)); } public virtual void Delete(TEntity item) { OnBeforeSaved(new SavedEventArgs(item, SaveAction.Delete)); Db.Set<TEntity>().Attach(item); Db.Set<TEntity>().Remove(item); this.SaveChanges(); OnAfterSaved(new SavedEventArgs(item, SaveAction.Delete)); } public virtual void Update(TEntity item) { OnBeforeSaved(new SavedEventArgs(item, SaveAction.Update)); Db.Set<TEntity>().Attach(item); Db.Entry(item).State = EntityState.Modified; try { this.SaveChanges(); } catch (System.Data.OptimisticConcurrencyException ex)//并(bing)發(fa)沖(chong)突(tu)異常 { ((IObjectContextAdapter)Db).ObjectContext.Refresh(RefreshMode.ClientWins, item); this.SaveChanges(); } OnAfterSaved(new SavedEventArgs(item, SaveAction.Update)); } /// <summary> /// 子(zi)類在實現時,可(ke)以重寫,加一些狀(zhuang)態過濾 /// </summary> /// <returns></returns> public virtual IQueryable<TEntity> GetModel() { // return Db.Set<TEntity>().AsNoTracking();//對(dui)象無法自(zi)動添加(jia)(jia)到上下文中(zhong),因為它是使用 NoTracking 合(he)并選項(xiang)檢索(suo)的。請在定義此關系之(zhi)前,將(jiang)該(gai)實體顯式附加(jia)(jia)到 ObjectContext。 return Db.Set<TEntity>();////ObjectStateManager 中已存在具(ju)有同(tong)一鍵(jian)的(de)對象。ObjectStateManager 無(wu)法跟(gen)蹤具(ju)有相同(tong)鍵(jian)的(de)多個對象。 } /// <summary> /// 得(de)到(dao)原生態結果集 /// </summary> /// <returns></returns> public IQueryable<TEntity> GetEntities() { return Db.Set<TEntity>(); } #endregion #region IExtensionRepository<TEntity> 成員 public virtual void Insert(IEnumerable<TEntity> item) { item.ToList().ForEach(i => { Db.Entry<TEntity>(i); Db.Set<TEntity>().Add(i); }); this.SaveChanges(); } public virtual void Delete(IEnumerable<TEntity> item) { item.ToList().ForEach(i => { Db.Set<TEntity>().Attach(i); Db.Set<TEntity>().Remove(i); }); this.SaveChanges(); } public virtual void Update(IEnumerable<TEntity> item) { item.ToList().ForEach(i => { Db.Set<TEntity>().Attach(i); Db.Entry(i).State = EntityState.Modified; }); try { this.SaveChanges(); } catch (System.Data.OptimisticConcurrencyException ex)//并發沖突異(yi)常 { ((IObjectContextAdapter)Db).ObjectContext.Refresh(RefreshMode.ClientWins, item); this.SaveChanges(); } } public void Update<T>(Expression<Action<T>> entity) where T : class { T newEntity = typeof(T).GetConstructor(Type.EmptyTypes).Invoke(null) as T;//建立(li)指定類型(xing)的實例(li) List<string> propertyNameList = new List<string>(); MemberInitExpression param = entity.Body as MemberInitExpression; foreach (var item in param.Bindings) { string propertyName = item.Member.Name; object propertyValue; var memberAssignment = item as MemberAssignment; if (memberAssignment.Expression.NodeType == ExpressionType.Constant) { propertyValue = (memberAssignment.Expression as ConstantExpression).Value; } else { propertyValue = Expression.Lambda(memberAssignment.Expression, null).Compile().DynamicInvoke(); } typeof(T).GetProperty(propertyName).SetValue(newEntity, propertyValue, null); propertyNameList.Add(propertyName); } try { Db.Set<T>().Attach(newEntity); } catch (Exception) { throw new Exception("本(ben)方法不(bu)能和GetModel()一起使(shi)用(yong),請使(shi)用(yong)Update(TEntity entity)方法"); } Db.Configuration.ValidateOnSaveEnabled = false; var ObjectStateEntry = ((IObjectContextAdapter)Db).ObjectContext.ObjectStateManager.GetObjectStateEntry(newEntity); propertyNameList.ForEach(x => ObjectStateEntry.SetModifiedProperty(x.Trim())); try { this.SaveChanges(); } catch (System.Data.OptimisticConcurrencyException ex)//并(bing)發(fa)沖突異常 { ((IObjectContextAdapter)Db).ObjectContext.Refresh(RefreshMode.ClientWins, newEntity); this.SaveChanges(); } } public TEntity Find(params object[] id) { return Db.Set<TEntity>().Find(id); } public IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate) { return GetModel().Where(predicate); } public TEntity Find(Expression<Func<TEntity, bool>> predicate) { return GetModel(predicate).FirstOrDefault(); } public void BulkInsert(IEnumerable<TEntity> item) { BulkInsert(item, false); } public void BulkInsert(IEnumerable<TEntity> item, bool isRemoveIdentity) { string startTag = "", endTag = ""; if (isRemoveIdentity) { startTag = "SET IDENTITY_INSERT " + typeof(TEntity).Name + " ON;"; endTag = "SET IDENTITY_INSERT " + typeof(TEntity).Name + " OFF;"; } DataPageProcess(item, (currentItems) => { ((IObjectContextAdapter)Db).ObjectContext.CommandTimeout = 0;//永不(bu)超時 Db.Database.ExecuteSqlCommand(startTag + DoSql(currentItems, SqlType.Insert) + endTag); }); } public void BulkDelete(IEnumerable<TEntity> item) { DataPageProcess(item, (currentItems) => { ((IObjectContextAdapter)Db).ObjectContext.CommandTimeout = 0;//永不(bu)超(chao)時 Db.Database.ExecuteSqlCommand(DoSql(currentItems, SqlType.Delete)); }); } public void BulkUpdate(IEnumerable<TEntity> item, params string[] fieldParams) { DataPageProcess(item, (currentItems) => { ((IObjectContextAdapter)Db).ObjectContext.CommandTimeout = 0;//永不超時 Db.Database.ExecuteSqlCommand(DoSql(currentItems, SqlType.Update, fieldParams)); }); } #endregion #region ISpecificationRepository<TEntity> 成員 public TEntity Find(ISpecification<TEntity> specification) { return GetModel(specification).FirstOrDefault(); } public IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy, ISpecification<TEntity> specification) { var linq = new Orderable<TEntity>(GetModel(specification)); orderBy(linq); return linq.Queryable; } public IQueryable<TEntity> GetModel(ISpecification<TEntity> specification) { return GetModel().Where(specification.SatisfiedBy()); } #endregion #region IOrderableRepository<TEntity>成員 public IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy) { var linq = new Orderable<TEntity>(GetModel()); orderBy(linq); return linq.Queryable; } public IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy, Expression<Func<TEntity, bool>> predicate) { var linq = new Orderable<TEntity>(GetModel(predicate)); orderBy(linq); return linq.Queryable; } #endregion #region Protected Methods /// <summary> /// 根據(ju)工作(zuo)單元的IsNotSubmit的屬性,去判斷是否提交(jiao)到(dao)數(shu)據(ju)庫 /// 一般地,在多(duo)個repository類型進行組合時,這(zhe)個IsNotSubmit都會設為true,即不(bu)馬上提交, /// 而對于(yu)單個repository操(cao)作(zuo)來說,它(ta)的值(zhi)不需要設置,使用默認的false,將直接(jie)提交到數據庫,這(zhe)也保證(zheng)了操(cao)作(zuo)的原子性。 /// </summary> protected void SaveChanges() { try { Db.SaveChanges(); } catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)//捕(bu)獲實(shi)體驗證異(yi)常 { var sb = new StringBuilder(); dbEx.EntityValidationErrors.First().ValidationErrors.ToList().ForEach(i => { sb.AppendFormat("屬性為:{0},信息(xi)為:{1}\n\r", i.PropertyName, i.ErrorMessage); }); if (Logger == null) throw new Exception(sb.ToString()); Logger(sb.ToString() + "處理(li)時間:" + DateTime.Now); } catch (System.Data.OptimisticConcurrencyException ex)//并發(fa)沖突異常 { //保(bao)持(chi)數據源中對象的現有屬性 //Db.Refresh(RefreshMode.StoreWins, person); // Db.SaveChanges(); } catch (Exception ex)//捕獲(huo)所有異常(chang) { if (Logger == null)//如果沒有(you)定義日志功能,就把異常拋出來吧 throw new Exception(ex.Message); Logger(ex.Message + "處理(li)時間:" + DateTime.Now); } } /// <summary> /// 計數更新,與SaveChange()是兩個SQL鏈接,走分布式事務 /// 子類可以根據自己的邏輯,去復寫 /// tableName:表名 /// param:索(suo)引0為主(zhu)鍵名,1表主(zhu)鍵值,2為要計(ji)數的(de)字段,3為增量 /// </summary> /// <param name="tableName">表名</param> /// <param name="param">參數列表,索引(yin)0為主鍵名,1表主鍵值,2為要計(ji)數的字(zi)段(duan),3為增量(liang)</param> protected virtual void UpdateForCount(string tableName, params object[] param) { string sql = "update [" + tableName + "] set [{2}]=ISNULL([{2}],0)+{3} where [{0}]={1}"; var listParasm = new List<object> { param[0], param[1], param[2], param[3], }; Db.Database.ExecuteSqlCommand(string.Format(sql, listParasm.ToArray())); } #endregion #region Virtual Methods /// <summary> /// Called after data saved /// </summary> /// <param name="e"></param> protected virtual void OnAfterSaved(SavedEventArgs e) { if (AfterSaved != null) { AfterSaved(e); } } /// <summary> /// Called before saved /// </summary> /// <param name="e"></param> protected virtual void OnBeforeSaved(SavedEventArgs e) { if (BeforeSaved != null) { BeforeSaved(e); } } #endregion #region Private Methods /// <summary> /// 分(fen)頁(ye)進行(xing)數據(ju)提(ti)交的邏輯 /// </summary> /// <param name="item">原(yuan)列表(biao)</param> /// <param name="method">處理方法(fa)</param> /// <param name="currentItem">要進行處(chu)理的(de)新列表</param> private void DataPageProcess(IEnumerable<TEntity> item, Action<IEnumerable<TEntity>> method) { if (item != null && item.Any()) { _dataTotalCount = item.Count(); this._dataTotalPages = item.Count() / DataPageSize; if (_dataTotalCount % DataPageSize > 0) _dataTotalPages += 1; for (int pageIndex = 1; pageIndex <= _dataTotalPages; pageIndex++) { var currentItems = item.Skip((pageIndex - 1) * DataPageSize).Take(DataPageSize).ToList(); method(currentItems); } } } private static string GetEqualStatment(string fieldName, int paramId, Type pkType) { if (pkType.IsValueType) return string.Format("{0} = {1}", fieldName, GetParamTag(paramId)); return string.Format("{0} = '{1}'", fieldName, GetParamTag(paramId)); } private static string GetParamTag(int paramId) { return "{" + paramId + "}"; } /// <summary> /// 得到實體鍵EntityKey /// </summary> /// <typeparam name="TEntity"></typeparam> /// <returns></returns> protected ReadOnlyMetadataCollection<EdmMember> GetPrimaryKey() { EntitySetBase primaryKey = ((IObjectContextAdapter)Db).ObjectContext.GetEntitySet(typeof(TEntity)); if (primaryKey == null) return null; ReadOnlyMetadataCollection<EdmMember> arr = primaryKey.ElementType.KeyMembers; return arr; } /// <summary> /// 構建Update語句串 /// 注(zhu)意:如(ru)(ru)果本方法(fa)過(guo)濾了int,decimal類型更新為0的列,如(ru)(ru)果希望更新它們需要指定FieldParams參數 /// </summary> /// <param name="entity">實(shi)體列(lie)表</param> /// <param name="fieldParams">要更(geng)新的字段</param> /// <returns></returns> private Tuple<string, object[]> CreateUpdateSql(TEntity entity, params string[] fieldParams) { if (entity == null) throw new ArgumentException("The database entity can not be null."); var pkList = GetPrimaryKey().Select(i => i.Name).ToList(); var entityType = entity.GetType(); var tableFields = new List<PropertyInfo>(); if (fieldParams != null && fieldParams.Count() > 0) { tableFields = entityType.GetProperties().Where(i => fieldParams.Contains(i.Name, new StringComparisonIgnoreCase())).ToList(); } else { tableFields = entityType.GetProperties().Where(i => !pkList.Contains(i.Name) && i.GetValue(entity, null) != null && !i.PropertyType.IsEnum && !(i.PropertyType == typeof(ValueType) && Convert.ToInt64(i.GetValue(entity, null)) == 0) && !(i.PropertyType == typeof(DateTime) && Convert.ToDateTime(i.GetValue(entity, null)) == DateTime.MinValue) && i.PropertyType != typeof(EntityState) && i.GetCustomAttributes(false).Where(j => j.GetType() == typeof(NavigationAttribute)) != null//過濾導航屬(shu)性 && (i.PropertyType.IsValueType || i.PropertyType == typeof(string)) ).ToList(); } //過濾(lv)主鍵(jian),航行(xing)屬(shu)性,狀(zhuang)態屬(shu)性等 if (pkList == null || pkList.Count == 0) throw new ArgumentException("The Table entity have not a primary key."); var arguments = new List<object>(); var builder = new StringBuilder(); foreach (var change in tableFields) { if (pkList.Contains(change.Name)) continue; if (arguments.Count != 0) builder.Append(", "); builder.Append(change.Name + " = {" + arguments.Count + "}"); if (change.PropertyType == typeof(string) || change.PropertyType == typeof(DateTime) || change.PropertyType == typeof(DateTime?) || change.PropertyType == typeof(bool?) || change.PropertyType == typeof(bool)) arguments.Add("'" + change.GetValue(entity, null).ToString().Replace("'", "char(39)") + "'"); else arguments.Add(change.GetValue(entity, null)); } if (builder.Length == 0) throw new Exception("沒有任(ren)何屬性進行更新"); builder.Insert(0, " UPDATE " + string.Format("[{0}]", entityType.Name) + " SET "); builder.Append(" WHERE "); bool firstPrimaryKey = true; foreach (var primaryField in pkList) { if (firstPrimaryKey) firstPrimaryKey = false; else builder.Append(" AND "); object val = entityType.GetProperty(primaryField).GetValue(entity, null); Type pkType = entityType.GetProperty(primaryField).GetType(); builder.Append(GetEqualStatment(primaryField, arguments.Count, pkType)); arguments.Add(val); } return new Tuple<string, object[]>(builder.ToString(), arguments.ToArray()); } /// <summary> /// 構建Delete語句串 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="entity"></param> /// <returns></returns> private Tuple<string, object[]> CreateDeleteSql(TEntity entity) { if (entity == null) throw new ArgumentException("The database entity can not be null."); Type entityType = entity.GetType(); List<string> pkList = GetPrimaryKey().Select(i => i.Name).ToList(); if (pkList == null || pkList.Count == 0) throw new ArgumentException("The Table entity have not a primary key."); var arguments = new List<object>(); var builder = new StringBuilder(); builder.Append(" Delete from " + string.Format("[{0}]", entityType.Name)); builder.Append(" WHERE "); bool firstPrimaryKey = true; foreach (var primaryField in pkList) { if (firstPrimaryKey) firstPrimaryKey = false; else builder.Append(" AND "); Type pkType = entityType.GetProperty(primaryField).GetType(); object val = entityType.GetProperty(primaryField).GetValue(entity, null); builder.Append(GetEqualStatment(primaryField, arguments.Count, pkType)); arguments.Add(val); } return new Tuple<string, object[]>(builder.ToString(), arguments.ToArray()); } /// <summary> /// 構建Insert語句串 /// 主(zhu)鍵(jian)為自增時,如果主(zhu)鍵(jian)值為0,我們將(jiang)主(zhu)鍵(jian)插入到(dao)SQL串(chuan)中 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="entity"></param> /// <returns></returns> private Tuple<string, object[]> CreateInsertSql(TEntity entity) { if (entity == null) throw new ArgumentException("The database entity can not be null."); Type entityType = entity.GetType(); var table = entityType.GetProperties().Where(i => i.PropertyType != typeof(EntityKey) && i.PropertyType != typeof(EntityState) && i.Name != "IsValid" && i.GetValue(entity, null) != null && !i.PropertyType.IsEnum && i.GetCustomAttributes(false).Where(j => j.GetType() == typeof(NavigationAttribute)) != null && (i.PropertyType.IsValueType || i.PropertyType == typeof(string))).ToArray();//過濾主鍵,航行屬性(xing),狀態屬性(xing)等 var pkList = new List<string>(); if (GetPrimaryKey() != null)//有(you)(you)時主(zhu)鍵可能沒有(you)(you)設(she)計,這對于添加(jia)操作是(shi)可以的 pkList = GetPrimaryKey().Select(i => i.Name).ToList(); var arguments = new List<object>(); var fieldbuilder = new StringBuilder(); var valuebuilder = new StringBuilder(); fieldbuilder.Append(" INSERT INTO " + string.Format("[{0}]", entityType.Name) + " ("); foreach (var member in table) { if (pkList.Contains(member.Name) && Convert.ToString(member.GetValue(entity, null)) == "0") continue; object value = member.GetValue(entity, null); if (value != null) { if (arguments.Count != 0) { fieldbuilder.Append(", "); valuebuilder.Append(", "); } fieldbuilder.Append(member.Name); if (member.PropertyType == typeof(string) || member.PropertyType == typeof(DateTime) || member.PropertyType == typeof(DateTime?) || member.PropertyType == typeof(Boolean?) || member.PropertyType == typeof(Boolean) ) valuebuilder.Append("'{" + arguments.Count + "}'"); else valuebuilder.Append("{" + arguments.Count + "}"); if (value is string) value = value.ToString().Replace("'", "char(39)"); arguments.Add(value); } } fieldbuilder.Append(") Values ("); fieldbuilder.Append(valuebuilder.ToString()); fieldbuilder.Append(");"); return new Tuple<string, object[]>(fieldbuilder.ToString(), arguments.ToArray()); } /// <summary> /// /// <summary> /// 執(zhi)行SQL,根據SQL操作的(de)類型 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="list"></param> /// <param name="sqlType"></param> /// <returns></returns> /// </summary> /// <param name="list"></param> /// <param name="sqlType"></param> /// <returns></returns> private string DoSql(IEnumerable<TEntity> list, SqlType sqlType) { return DoSql(list, sqlType, null); } /// <summary> /// 執行SQL,根據SQL操作的類型 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="list"></param> /// <param name="sqlType"></param> /// <returns></returns> private string DoSql(IEnumerable<TEntity> list, SqlType sqlType, params string[] fieldParams) { var sqlstr = new StringBuilder(); switch (sqlType) { case SqlType.Insert: list.ToList().ForEach(i => { Tuple<string, object[]> sql = CreateInsertSql(i); sqlstr.AppendFormat(sql.Item1, sql.Item2); }); break; case SqlType.Update: list.ToList().ForEach(i => { Tuple<string, object[]> sql = CreateUpdateSql(i, fieldParams); sqlstr.AppendFormat(sql.Item1, sql.Item2); }); break; case SqlType.Delete: list.ToList().ForEach(i => { Tuple<string, object[]> sql = CreateDeleteSql(i); sqlstr.AppendFormat(sql.Item1, sql.Item2); }); break; default: throw new ArgumentException("請(qing)輸入正確(que)的參數(shu)"); } return sqlstr.ToString(); } /// <summary> /// SQL操作(zuo)類(lei)型 /// </summary> protected enum SqlType { Insert, Update, Delete, } #endregion }
以上六大部分就是我最新的EF架構的核心了,事實上,EF只是實現數據持久化的一種方式,在我的架構中還提到了XmlRepository,RedisRepository,Linq2SqlRepository等等,對于倉儲(chu)這(zhe)塊感(gan)興趣的(de)(de)同學,可以與我(wo)一起(qi)去(qu)討(tao)論!我(wo)很希望(wang)有一天(tian),我(wo)的(de)(de)底層(ceng)
架構有(you)這樣(yang)一個功能(neng),那就是自(zi)動去選(xuan)擇我(wo)的數(shu)據(ju)(ju)庫,如我(wo)的數(shu)據(ju)(ju)庫有(you)db1,db2.....dbN,它們之(zhi)間的數(shu)據(ju)(ju)是同步的(集群),我(wo)能(neng)通過EF來實現(xian)我(wo)用哪臺數(shu)據(ju)(ju)服務器(qi),想想就很美(mei),哈哈!