EF架構~基於EF資料層的實現

weixin_34126215發表於2014-10-21

回到目錄

之前寫過關於實現一個完整的EF架構的文章,文章的閱讀量也是滿大的,自己很欣慰,但是,那篇文章是我2011年寫的,所以,技術有些不成熟,所以今天把我的2014年寫的EF底層架構公開一下,這個架構比2011年的有了很大程度的提高,主要在介面規範,查詢規範上,並引入了排序功能,兩步對完善了EF對資料的批量操作,可以說,這次的架構是很有看點的。

一 一個基礎操作介面

    /// <summary>
    /// 基礎的資料操作規範
    /// 與ORM架構無關
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public interface IRepository<TEntity>
           where TEntity : class
    {
        /// <summary>
        /// 設定資料上下文,它一般由構架方法注入
        /// </summary>
        /// <param name="unitOfWork"></param>
        void SetDbContext(IUnitOfWork unitOfWork);

        /// <summary>
        /// 新增實體並提交到資料伺服器
        /// </summary>
        /// <param name="item">Item to add to repository</param>
        void Insert(TEntity item);

        /// <summary>
        /// 移除實體並提交到資料伺服器
        /// 如果表存在約束,需要先刪除子表資訊
        /// </summary>
        /// <param name="item">Item to delete</param>
        void Delete(TEntity item);

        /// <summary>
        /// 修改實體並提交到資料伺服器
        /// </summary>
        /// <param name="item"></param>
        void Update(TEntity item);

        /// <summary>
        /// 得到指定的實體集合(延時結果集)
        /// Get all elements of type {T} in repository
        /// </summary>
        /// <returns>List of selected elements</returns>
        IQueryable<TEntity> GetModel();

        /// <summary>
        /// 根據主鍵得到實體
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        TEntity Find(params object[] id);
    }
View Code

二 一個擴充套件操作介面

    /// <summary>
    /// 擴充套件的Repository操作規範
    /// </summary>
    public interface IExtensionRepository<TEntity> :
        IRepository<TEntity>,
        IOrderableRepository<TEntity>
        where TEntity : class
    {
        /// <summary>
        /// 新增集合[集合數目不大時用此方法,超大集合使用BulkInsert]
        /// </summary>
        /// <param name="item"></param>
        void Insert(IEnumerable<TEntity> item);

        /// <summary>
        /// 修改集合[集合數目不大時用此方法,超大集合使用BulkUpdate]
        /// </summary>
        /// <param name="item"></param>
        void Update(IEnumerable<TEntity> item);

        /// <summary>
        /// 刪除集合[集合數目不大時用此方法,超大集合使用批量刪除]
        /// </summary>
        /// <param name="item"></param>
        void Delete(IEnumerable<TEntity> item);

        /// <summary>
        /// 擴充套件更新方法,只對EF支援
        /// 注意本方法不能和GetModel()一起使用,它的表主鍵可以通過post或get方式獲取
        /// </summary>
        /// <param name="entity"></param>
        void Update<T>(Expression<Action<T>> entity) where T : class;

        /// <summary>
        /// 根據指定lambda表示式,得到延時結果集
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 根據指定lambda表示式,得到第一個實體
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        TEntity Find(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 批量新增,新增之前可以去除自增屬性,預設不去除
        /// </summary>
        /// <param name="item"></param>
        /// <param name="isRemoveIdentity"></param>
        void BulkInsert(IEnumerable<TEntity> item, bool isRemoveIdentity);

        /// <summary>
        /// 批量新增
        /// </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>
        /// 批量刪除
        /// </summary>
        /// <param name="item"></param>
        void BulkDelete(IEnumerable<TEntity> item);


    }
View Code

三 一個排序操作介面

    /// <summary>
    /// 提供排序功能的規範
    /// </summary>
    public interface IOrderableRepository<TEntity> where TEntity : class
    {
        /// <summary>
        /// 帶排序的結果集
        /// </summary>
        /// <param name="orderBy"></param>
        /// <returns></returns>
        IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy);


        /// <summary>
        /// 根據指定lambda表示式和排序方式,得到延時結果集
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        IQueryable<TEntity> GetModel(Action<IOrderable<TEntity>> orderBy, Expression<Func<TEntity, bool>> predicate);
    }
View Code

四 基於ef架構的規約查詢介面

/// <summary>
    /// EF底層構架,關於規約功能的倉儲介面
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public interface ISpecificationRepository<TEntity> :
        IExtensionRepository<TEntity>
        where TEntity : class
    {
        /// <summary>
        /// 根據指定規約,得到延時結果集
        /// </summary>
        /// <param name="specification"></param>
        /// <returns></returns>
        IQueryable<TEntity> GetModel(ISpecification<TEntity> specification);

        /// <summary>
        /// 根據指定規約,得到第一個實體
        /// </summary>
        /// <param name="specification"></param>
        /// <returns></returns>
        TEntity Find(ISpecification<TEntity> specification);

        /// <summary>
        /// 帶排序功能的,根據指定規約,得到結果集
        /// </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>
        /// 儲存之後觸發
        /// Occurs after data saved
        /// </summary>
        event Action<SavedEventArgs> AfterSaved;

        /// <summary>
        /// 儲存之前觸發
        /// Occurs before data saved
        /// </summary>
        event Action<SavedEventArgs> BeforeSaved;
    }
View Code

五 基於工作單元的標識介面

/// <summary>
    /// 資料上下文標識介面,它對於業務層應該是公開的
    /// 它對於實現上下文的方法,它並不關心,可以是linq2sql,ef,ado.net,nhibernate,memory,nosql等
    /// </summary>
    public interface IUnitOfWork
    {
    }
View Code

六 基於ef的DbContext上下文的倉儲的實現

 /// <summary>
    /// DbContext上下文倉儲功能類,領域上下文可以直接繼承它
    /// 生命週期:資料上下文的生命週期為一個HTTP請求的結束
    /// 相關說明:
    /// 1 領域物件使用宣告IRepository和IExtensionRepository介面得到不同的操作規範
    /// 2 可以直接為上下注入Action<string>的委託例項,用來記錄savechanges產生的異常
    /// 3 可以訂閱BeforeSaved和AfterSaved兩個事件,用來在方法提交前與提交後實現程式碼注入
    /// 4 所有領域db上下文都要繼承iUnitWork介面,用來實現工作單元,這對於提升程式效能與為重要
    /// </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>
        /// 資料上下文
        /// </summary>
        protected DbContext Db { get; private set; }

        /// <summary>
        /// 工作單元上下文,子類可以直接使用它
        /// </summary>
        protected IUnitOfWork UnitWork { get; set; }

        /// <summary>
        /// Action委託事例,在派生類可以操作它
        /// </summary>
        protected Action<string> Logger { get; private set; }
        #endregion

        #region Fields
        /// <summary>
        /// 資料總數
        /// </summary>
        int _dataTotalCount = 0;

        /// <summary>
        /// 資料總頁數
        /// </summary>
        int _dataTotalPages = 0;

        /// <summary>
        /// 資料頁面大小(每次向資料庫提交的記錄數)
        /// </summary>
        private const int DataPageSize = 10000;

        #endregion

        #region Delegates & Event
        /// <summary>
        /// 儲存之後
        /// </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)//併發衝突異常
            {

                ((IObjectContextAdapter)Db).ObjectContext.Refresh(RefreshMode.ClientWins, item);
                this.SaveChanges();
            }

            OnAfterSaved(new SavedEventArgs(item, SaveAction.Update));
        }

        /// <summary>
        /// 子類在實現時,可以重寫,加一些狀態過濾
        /// </summary>
        /// <returns></returns>
        public virtual IQueryable<TEntity> GetModel()
        {
            //  return Db.Set<TEntity>().AsNoTracking();//物件無法自動新增到上下文中,因為它是使用 NoTracking 合併選項檢索的。請在定義此關係之前,將該實體顯式附加到 ObjectContext。
            return Db.Set<TEntity>();////ObjectStateManager 中已存在具有同一鍵的物件。ObjectStateManager 無法跟蹤具有相同鍵的多個物件。
        }
        /// <summary>
        /// 得到原生態結果集
        /// </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)//併發衝突異常
            {

                ((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;//建立指定型別的例項
            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("本方法不能和GetModel()一起使用,請使用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)//併發衝突異常
            {

                ((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;//永不超時
                Db.Database.ExecuteSqlCommand(startTag
                    + DoSql(currentItems, SqlType.Insert)
                    + endTag);
            });
        }

        public void BulkDelete(IEnumerable<TEntity> item)
        {
            DataPageProcess(item, (currentItems) =>
            {
                ((IObjectContextAdapter)Db).ObjectContext.CommandTimeout = 0;//永不超時
                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>
        /// 根據工作單元的IsNotSubmit的屬性,去判斷是否提交到資料庫
        /// 一般地,在多個repository型別進行組合時,這個IsNotSubmit都會設為true,即不馬上提交,
        /// 而對於單個repository操作來說,它的值不需要設定,使用預設的false,將直接提交到資料庫,這也保證了操作的原子性。
        /// </summary>
        protected void SaveChanges()
        {
            try
            {
                Db.SaveChanges();
            }
            catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)//捕獲實體驗證異常
            {
                var sb = new StringBuilder();
                dbEx.EntityValidationErrors.First().ValidationErrors.ToList().ForEach(i =>
                {
                    sb.AppendFormat("屬性為:{0},資訊為:{1}\n\r", i.PropertyName, i.ErrorMessage);
                });
                if (Logger == null)
                    throw new Exception(sb.ToString());
                Logger(sb.ToString() + "處理時間:" + DateTime.Now);

            }
            catch (System.Data.OptimisticConcurrencyException ex)//併發衝突異常
            {
                //保持資料來源中物件的現有屬性
                //Db.Refresh(RefreshMode.StoreWins, person);
                //  Db.SaveChanges();
            }
            catch (Exception ex)//捕獲所有異常
            {
                if (Logger == null)//如果沒有定義日誌功能,就把異常丟擲來吧
                    throw new Exception(ex.Message);
                Logger(ex.Message + "處理時間:" + DateTime.Now);
            }

        }

        /// <summary>
        ///  計數更新,與SaveChange()是兩個SQL連結,走分散式事務
        ///  子類可以根據自己的邏輯,去複寫
        ///  tableName:表名
        ///  param:索引0為主鍵名,1表主鍵值,2為要計數的欄位,3為增量
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="param">引數列表,索引0為主鍵名,1表主鍵值,2為要計數的欄位,3為增量</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>
        /// 分頁進行資料提交的邏輯
        /// </summary>
        /// <param name="item">原列表</param>
        /// <param name="method">處理方法</param>
        /// <param name="currentItem">要進行處理的新列表</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語句串
        /// 注意:如果本方法過濾了int,decimal型別更新為0的列,如果希望更新它們需要指定FieldParams引數
        /// </summary>
        /// <param name="entity">實體列表</param>
        /// <param name="fieldParams">要更新的欄位</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//過濾導航屬性
                              && (i.PropertyType.IsValueType || i.PropertyType == typeof(string))
                              ).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();

            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("沒有任何屬性進行更新");

            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語句串
        /// 主鍵為自增時,如果主鍵值為0,我們將主鍵插入到SQL串中
        /// </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();//過濾主鍵,航行屬性,狀態屬性等

            var pkList = new List<string>();
            if (GetPrimaryKey() != null)//有時主鍵可能沒有設計,這對於新增操作是可以的
                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>
        /// 執行SQL,根據SQL操作的型別
        /// </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("請輸入正確的引數");
            }
            return sqlstr.ToString();
        }

        /// <summary>
        /// SQL操作型別
        /// </summary>
        protected enum SqlType
        {
            Insert,
            Update,
            Delete,
        }
        #endregion

    }
View Code

以上六大部分就是我最新的EF架構的核心了,事實上,EF只是實現資料持久化的一種方式,在我的架構中還提到了XmlRepository,RedisRepository,Linq2SqlRepository等等,對於倉儲這塊感興趣的同學,可以與我一起去討論!我很希望有一天,我的底層

架構有這樣一個功能,那就是自動去選擇我的資料庫,如我的資料庫有db1,db2.....dbN,它們之間的資料是同步的(叢集),我能通過EF來實現我用哪臺資料伺服器,想想就很美,哈哈!

回到目錄

相關文章