天天看點

EF Core 自定義擴充三:資料新增、更新、删除、查詢

作者:宜城公子羽

先建立資料操作類接口IRepository,定義資料操作方法

/// <summary>
    /// Represents an entity repository
    /// </summary>
    /// <typeparam name="TEntity">Entity type</typeparam>
    public partial interface IRepository<TEntity> where TEntity : class
    {
        #region Methods
        /// <summary>
        /// 根據id查找實列
        /// </summary>
        /// <param name="id">Identifier</param>
        /// <returns>Entity</returns>
        TEntity GetById(object[] id);
        /// <summary>
        /// 插入單條記錄
        /// </summary>
        /// <param name="entity">Entity</param>
        TEntity Insert(TEntity entity);
        /// <summary>
        /// 批量插入記錄
        /// </summary>
        /// <param name="entities">Entities</param>
        void Insert(IEnumerable<TEntity> entities);
        /// <summary>
        /// 更新單條記錄
        /// </summary>
        /// <param name="entity">Entity</param>
        bool Update(TEntity entity);
        /// <summary>
        /// 批量更新記錄
        /// </summary>
        /// <param name="entities">Entities</param>
        bool Update(IEnumerable<TEntity> entities);
        /// <summary>
        /// 智能更新單條記錄
        /// </summary>
        /// <param name="entity">Entity</param>
        bool SmartUpdate(TEntity entity);
        /// <summary>
        /// 删除單條記錄
        /// </summary>
        /// <param name="entity">Entity</param>
        void Delete(TEntity entity);
        /// <summary>
        /// 批量删除記錄
        /// </summary>
        /// <param name="entities">Entities</param>
        void Delete(IEnumerable<TEntity> entities);
        #endregion
        #region Properties
        /// <summary>
        /// Gets a table
        /// </summary>
        IQueryable<TEntity> Table { get; }
        /// <summary>
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// </summary>
        IQueryable<TEntity> TableNoTracking { get; }
        #endregion
    }           

接下來實作資料新增、删除、修改的操作和查詢方法,

public partial class EfRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {  
        private readonly IDbContext _context;
        private DbSet<TEntity> _entities;
        public EfRepository(IDbContext context)
        {
            this._context = context;
        }
        public EfRepository()
        {
            this._context = new ExtObjectContext();
        }
        #region Properties
        /// <summary>
        /// 數量保留小數位數
        /// </summary>
        protected int QuantityDecimals { get; set; } = 4;
        /// <summary>
        /// 單價保留小數位數
        /// </summary>
        protected int PriceDecimals { get; set; } = 4;
        /// <summary>
        /// 金額保留小數位數
        /// </summary>
        protected int AmountDecimals { get; set; } = 2;
        /// <summary>
        /// 系統登陸使用者資訊
        /// </summary>
        protected LoginUserInfo LoginUserInfo { get; set; }

        /// <summary>
        /// Gets a table
        /// </summary>
        public virtual IQueryable<TEntity> Table => Entities;
        /// <summary>
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// </summary>
        public virtual IQueryable<TEntity> TableNoTracking => Entities.AsNoTracking();
        /// <summary>
        /// Gets an entity set
        /// </summary>
        protected virtual DbSet<TEntity> Entities
        {
            get
            {
                if (_entities == null)
                {
                    _entities = _context.Set<TEntity>();
                }
                return _entities;
            }
        }
        /// <summary>
        /// 依賴加載表達式
        /// </summary>
        public virtual List<Expression<Func<TEntity, object>>> IncludeExpression { get; set; }
        /// <summary>
        /// 更新字段表達式
        /// </summary>
        public virtual List<Expression<Func<TEntity, object>>> PropertyExpressions { get; set; }
        #endregion
        #region Utilities
        /// <summary>
        /// Rollback of entity changes and return full error message
        /// </summary>
        /// <param name="exception">Exception</param>
        /// <returns>Error message</returns>
        protected string GetFullErrorTextAndRollbackEntityChanges(DbUpdateException exception)
        {
            //rollback entity changes
            if (_context is DbContext dbContext)
            {
                var entries = dbContext.ChangeTracker.Entries()
                    .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified).ToList();

                entries.ForEach(entry => entry.State = EntityState.Unchanged);
            }

            _context.SaveChanges();
            return exception.ToString();
        }
        #endregion
        #region Methods
        /// <summary>
        /// 設定緩存
        /// </summary>
        protected void SetMemoryCache()
        {
            Dictionary<string, string> SystemOptionDict = MemoryCacheHelper.GetItem(CacheConst.SystemOptionDict) as Dictionary<string, string>;
            if (SystemOptionDict != null)
            {
                this.PriceDecimals = SystemOptionDict.ContainsKey(CacheConst.Price_Decimals) ? SystemOptionDict[CacheConst.Price_Decimals].ToInt32(4) : 4;
                this.QuantityDecimals = SystemOptionDict.ContainsKey(CacheConst.Qty_Decimals) ? SystemOptionDict[CacheConst.Qty_Decimals].ToInt32(4) : 4;
                this.AmountDecimals = SystemOptionDict.ContainsKey(CacheConst.Amount_Decimals) ? SystemOptionDict[CacheConst.Amount_Decimals].ToInt32(2) : 2;
            }
            this.LoginUserInfo = MemoryCacheHelper.GetItem(CacheConst.LoginUserInfo) as LoginUserInfo;
        }
        /// <summary>
        /// 複制Entity
        /// </summary>
        /// <param name="entity">要複制對象</param>
        /// <param name="reversal">是非沖銷</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public virtual TEntity Copy(TEntity entity, bool reversal = false)
        {
            this._context.Detach(entity);
            try
            {
                TEntity Clone = Activator.CreateInstance<TEntity>();
                Clone = entity;
                Type Ts = entity.GetType();
                PropertyInfo info = Ts.GetProperty("ID");
                if (info != null)
                {
                    info.SetValue(entity, 0, null);
                }
                return this.Insert(Clone);
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
        }
        /// <summary>
        /// 複制Entity
        /// </summary>
        /// <param name="id">要複制對象ID</param>
        /// <param name="reversal">是非沖銷</param>
        /// <returns></returns>
        public virtual TEntity Copy(int id, bool reversal = false)
        {
            TEntity entity = this.GetById(id);
            return this.Copy(entity, reversal);
        }
        public virtual void Detach(TEntity entity)
        {
            _context.Detach(entity);
        }
        /// <summary>
        /// Insert entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual TEntity Insert(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));

            try
            {
                this.Entities.Add(entity);
                _context.SaveChanges();
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
            return entity;
        }
        /// <summary>
        /// Insert entities
        /// </summary>
        /// <param name="entities">Entities</param>
        public virtual void Insert(IEnumerable<TEntity> entities)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));

            try
            {
                Entities.AddRange(entities);
                _context.SaveChanges();
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                LogTextHelper.Error(exception.Message);

                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
        }
        /// <summary>
        ///  更新實體
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual bool Update(TEntity entity)
        {
            bool succeed = false;
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            try
            {
                Entities.Update(entity);
                _context.SaveChanges();
                succeed = true;
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
            return succeed;
        }
        /// <summary>
        /// 批量更新實體
        /// </summary>
        /// <param name="entities">Entities</param>
        public virtual bool Update(IEnumerable<TEntity> entities)
        {
            bool succeed = false;
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));

            try
            {
                Entities.UpdateRange(entities);
                _context.SaveChanges();
                succeed = true;
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
            return succeed;
        }
        public virtual bool SmartUpdate(TEntity entity)
        {
            if (this.PropertyExpressions.Count > 0)
                return this.SmartUpdate(entity, this.PropertyExpressions);
            else
                return this.Update(entity);
        }
        /// <summary>
        /// 部分字段更新
        /// </summary>
        /// <param name="entity">Entity</param>
        /// <param name="PropertyExpressions">要更新字段表達式</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public virtual bool SmartUpdate(TEntity entity, List<Expression<Func<TEntity, object>>> PropertyExpressions)
        {
            using (var dbContext = new ExtObjectContext())
            {
                bool succeed = false;
                if (entity == null)
                    throw new ArgumentNullException(nameof(entity));
                try
                {
                    dbContext.Attach(entity);
                    foreach (var p in PropertyExpressions)
                    {
                        dbContext.Entry(entity).Property(p).IsModified = true;
                    }
                    dbContext.SaveChanges();
                    succeed = true;
                }
                catch (DbUpdateException exception)
                {
                    throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
                }
                return succeed;

            }
        }
        /// <summary>
        /// 部分字段更新
        /// </summary>
        /// <param name="entity">要更新的實體</param>
        /// <param name="PropertyExpression">要更新的字段表達式</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">空值錯誤</exception>
        /// <exception cref="Exception"></exception>
        public virtual int SmartUpdate(TEntity entity, Expression<Func<TEntity, object>> PropertyExpression)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));

            using (var dbContext = new ExtObjectContext())
            {
                try
                {
                    if (entity == null)
                        throw new ArgumentNullException(nameof(entity));

                    dbContext.Attach(entity);
                    dbContext.Entry(entity).Property(PropertyExpression).IsModified = true;
                    return dbContext.SaveChanges();
                }
                catch (DbUpdateException exception)
                {
                    //ensure that the detailed error text is saved in the Log
                    throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
                }
            }
        }
        /// <summary>
        /// 删除記錄
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void Delete(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));

            try
            {
                Entities.Remove(entity);
                _context.SaveChanges();
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
        }
        /// <summary>
        /// 根據實體ID删除記錄
        /// </summary>
        /// <param name="ID">實體ID</param>
        public virtual void Delete(int ID)
        {
            this.Delete(this.GetById(ID));
        }
        /// <summary>
        /// 批量删除記錄
        /// </summary>
        /// <param name="entities">Entities</param>
        public virtual void Delete(IEnumerable<TEntity> entities)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));

            try
            {
                Entities.RemoveRange(entities);
                _context.SaveChanges();
            }
            catch (DbUpdateException exception)
            {
                //ensure that the detailed error text is saved in the Log
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }
        }
        /// <summary>
        /// 稽核
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void Audit(TEntity entity) { }
        /// <summary>
        /// 儲存記錄
        /// </summary>
        /// <param name="entity">Entity</param>
        /// <returns></returns>
        public virtual int Save(TEntity entity)
        {
            Type Ts = entity.GetType();

            PropertyInfo? info = Ts.GetProperty("ID");
            if (info != null)
            {
                int ID = info.GetValue(entity, null).ToInt32();
                if (ID > 0)
                    this.Update(entity);
                else
                    entity = this.Insert(entity);
                return info.GetValue(entity, null).ToInt32();
            }
            else
                return -1;

        }
        #endregion
    }           

查詢方法

public partial class EfRepository<TEntity> where TEntity : class
    {
        /// <summary>
        /// 根據ID查找
        /// </summary>
        /// <param name="ids">Identifier</param>
        /// <returns>Entity</returns>
        public virtual TEntity GetById(object[] ids)
        {
            return Entities.Find(ids);
        }

        /// <summary>
        /// 根據ID查找
        /// </summary>
        /// <param name="id">Identifier</param>
        /// <returns>Entity</returns>
        public virtual TEntity GetById(int id)
        {
            return Entities.Find(id);
        }

        /// <summary>
        /// 查找所有記錄
        /// </summary>
        /// <param name="NoTrack"></param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> GetAll(bool NoTrack = false)
        {
            IQueryable<TEntity> query = NoTrack ? this.TableNoTracking : this.Table;
            if (this.IncludeExpression != null)
            {
                foreach (var p in IncludeExpression)
                {
                    query = query.Include(p);
                }
            }
            return query;
        }

        public virtual List<TEntity> GetList4Tree(bool NoTrack = false)
        {
            IQueryable<TEntity> query = NoTrack ? this.TableNoTracking:this.Table;
            return query.ToList();
        }
        /// <summary>
        /// 擷取記錄供下拉清單使用
        /// </summary>
        /// <param name="NoTrack"></param>
        /// <returns></returns>
        public virtual List<TEntity> GetEntityList4Cache()
        {
            string key = typeof(TEntity).Name + "_LookupList";
            List<TEntity> list = MemoryCacheHelper.GetCacheItem<List<TEntity>>(key,
                delegate () { return this.TableNoTracking.ToList(); },
                new TimeSpan(0, 5, 0));//5分鐘過期
            return list;
        }

        /// <summary>
        /// 根據條件分頁查找并排序
        /// </summary>
        /// <param name="filter">過濾條件</param>
        /// <param name="pagerInfo">分頁條件</param>
        /// <param name="orderByExpression">排序條件</param>
        /// <returns></returns>
        public virtual List<TEntity> QueryWithPager(Expression<Func<TEntity, bool>> filter, PagerInfo pagerInfo, params OrderModelField[] orderByExpression)
        {
            List<TEntity> list = this.QueryWithPager(filter, pagerInfo.CurrenetPageIndex, pagerInfo.PageSize, out int rowCount, orderByExpression).ToList();
            pagerInfo.RecordCount = rowCount;
            return list;
        }

        /// <summary>
        /// 根據條件分頁查找并排序
        /// </summary>
        /// <param name="filter">過濾條件</param>
        /// <param name="pageIndex">目前頁索引</param>
        /// <param name="pageSize">每頁記錄數</param>
        /// <param name="total">合計記錄數</param>
        /// <param name="OrderExpression">排序條件</param>
        /// <returns></returns>
        public virtual IEnumerable<TEntity> QueryWithPager(Expression<Func<TEntity, bool>> filter, int pageIndex, int pageSize, out int total, params OrderModelField[] OrderExpression)
        {
            //條件過濾
            IQueryable<TEntity> query = this.Table.AsNoTracking();
            if (this.IncludeExpression != null)
            {
                foreach (var p in this.IncludeExpression)
                {
                    query = query.Include(p);
                }
            }
            if (filter != null)
            {
                query = query.Where(filter);
            }

            if (OrderExpression != null && OrderExpression.Length > 0)
            {
                //建立表達式變量參數
                var parameter = Expression.Parameter(typeof(TEntity), "o");
                for (int i = 0; i < OrderExpression.Length; i++)
                {
                    //根據屬性名擷取屬性
                    var property = typeof(TEntity).GetProperty(OrderExpression[i].PropertyName);
                    //建立一個通路屬性的表達式
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);

                    string OrderName = "";
                    if (i > 0)
                        OrderName = OrderExpression[i].IsDESC ? "ThenByDescending" : "ThenBy";
                    else
                        OrderName = OrderExpression[i].IsDESC ? "OrderByDescending" : "OrderBy";
                    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), OrderName, new Type[] { typeof(TEntity), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));

                    query = query.Provider.CreateQuery<TEntity>(resultExp);
                }

            }
            total = query.Count();
            return query.Skip((pageIndex - 1) * pageSize).Take(pageSize);
        }
        /// <summary>
        ///  根據條件查找并排序
        /// </summary>
        /// <param name="filter">過濾條件表達式</param>
        /// <returns>結果清單</returns>    
        public virtual IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> filter, params OrderModelField[] OrderExpression)
        {
            IQueryable<TEntity> query = this.Table;
            if (this.IncludeExpression != null)
            {
                foreach (var p in this.IncludeExpression)
                {
                    query = query.Include(p);
                }
            }
            if (filter != null)
                query = query.Where(filter);

            if (OrderExpression != null && OrderExpression.Length > 0)
            {
                //建立表達式變量參數
                var parameter = Expression.Parameter(typeof(TEntity), "o");
                for (int i = 0; i < OrderExpression.Length; i++)
                {
                    //根據屬性名擷取屬性
                    var property = typeof(TEntity).GetProperty(OrderExpression[i].PropertyName);
                    //建立一個通路屬性的表達式
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);

                    string OrderName = "";
                    if (i > 0)
                        OrderName = OrderExpression[i].IsDESC ? "ThenByDescending" : "ThenBy";
                    else
                        OrderName = OrderExpression[i].IsDESC ? "OrderByDescending" : "OrderBy";


                    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), OrderName, new Type[] { typeof(TEntity), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));

                    query = query.Provider.CreateQuery<TEntity>(resultExp);
                }
            }
            return query;
        }
        /// <summary>
        /// Executes the given SQL against the database
        /// </summary>
        /// <param name="sql">The SQL to execute</param>
        /// <param name="doNotEnsureTransaction">true - the transaction creation is not ensured; false - the transaction creation is ensured.</param>
        /// <param name="timeout">The timeout to use for command. Note that the command timeout is distinct from the connection timeout, which is commonly set on the database connection string</param>
        /// <param name="parameters">Parameters to use with the SQL</param>
        /// <returns>The number of rows affected</returns>
        public virtual int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
        {
            return this._context.ExecuteSqlCommand(sql, doNotEnsureTransaction, timeout, parameters);
        }
        /// <summary>
        /// Creates a LINQ query for the entity based on a raw SQL query
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        /// <param name="sql">The raw SQL query</param>
        /// <param name="parameters">The values to be assigned to parameters</param>
        /// <returns>An IQueryable representing the raw SQL query</returns>  
        public virtual IQueryable<TEntity> EntityFromSql(string sql, params object[] parameters)
        {
            return this._context.EntityFromSql<TEntity>(sql, parameters);
        }
        public virtual IQueryable<TEntity> EntityFromSql(string sql)
        {
            return this._context.EntityFromSql<TEntity>(sql);
        }
    }           

分頁類PagerInfo

public delegate void PageInfoChanged(PagerInfo info);

    public class PagerInfo
    {
        public event PageInfoChanged OnPageInfoChanged;

        private int currenetPageIndex; //目前頁碼
        private int pageSize;//每頁顯示的記錄
        private int recordCount;//記錄總數

        #region 屬性變量

        /// <summary>
        /// 擷取或設定目前頁碼
        /// </summary>
        public int CurrenetPageIndex
        {
            get { return currenetPageIndex; }
            set
            {
                currenetPageIndex = value;

                if (OnPageInfoChanged != null)
                {
                    OnPageInfoChanged(this);
                }
            }
        }

        /// <summary>
        /// 擷取或設定每頁顯示的記錄
        /// </summary>
        public int PageSize
        {
            get { return pageSize; }
            set
            {
                pageSize = value;
                if (OnPageInfoChanged != null)
                {
                    OnPageInfoChanged(this);
                }
            }
        }

        /// <summary>
        /// 擷取或設定記錄總數
        /// </summary>
        public int RecordCount
        {
            get { return recordCount; }
            set
            {
                recordCount = value;
                if (OnPageInfoChanged != null)
                {
                    OnPageInfoChanged(this);
                }
            }
        }

        #endregion
    }           
public struct OrderModelField
    {
        /// <summary>
        /// 排序字段名稱
        /// </summary>
        public string PropertyName { get; set; }
        /// <summary>
        /// 是否倒序
        /// </summary>
        public bool IsDESC { get; set; }
    }           

繼續閱讀