電商系統架構總結1(EF)

         最近主導了一個電商系統的設計開發過程,包括前期分析設計,框架搭建,功能模塊的具體開發(主要負責在線支付部分),成功上線後的部署維護,運維策略等等全過程。前端

雖然這個系統不是什麼超大型的電商系統 數億計的併發,但團隊裏的主要成員都有多年的開發經驗以及電商的經驗,系統設計方面仍是麻雀雖小,但五臟俱全。android

系統客戶端有 ios , android,H5,微信小程序,後臺方面用.net web api + sql server,圖片資源的讀寫使用阿里雲,緩存則本身搭建redis,支付方面既有使用一些第三方支付平臺,也有和支付寶微信等。。。。  技術點不少,說實話,基本上通常應用系統開發用到的技術基本都用到了,也遇到了不少的坑。因此以爲把裏面的代碼整理概括出來頗有必要,對於之後的系統開發,特別同類項目的開發,頗有借鑑意義。因爲我沒有參與前端的開發,因此這裏主要以.nt 後臺架構的講述爲主。ios

        因爲是.nt  mvc web api,因此選擇了 EF做爲orm組件。程序分層架構分爲: 數據層 DAL和 IDAL,業務層BLL和IBLL,Entity(實體定義),WebAPI,另外還建了一個Utility項目,用於容納一些與業務無關而且能夠複用的功能性代碼以及第三方開源代碼,另外還有兩個單獨的解決方案,一個名爲thirdparty的web api項目, 用於和第三方平臺的接口交互,包括向短信平臺發送短信驗證碼,向阿里雲上傳圖片,查詢 快遞單物流狀態,向推送平臺(極光,微信公衆號)推送消息。另外一個爲windows服務程序,用於執行自動化工做任務。web

下面分別講述。redis

1、DAL。主要負責封裝對EF的底層數據操做。其中有一段對EF 的DBContext 實現單例模式的代碼,是我當時從網上摘抄下來的,代碼以下sql

 public class DbContextFactory
    {
        public static WinLinkContext Create()
        {
            WinLinkContext dbContext = CallContext.GetData("DbContext") as WinLinkContext;
            if (dbContext == null)
            {
                dbContext = new WinLinkContext();
                CallContext.SetData("DbContext", dbContext);
            }
            return dbContext;
        }
    }

  其實我最初使用EF時,我記得官方文檔都是提倡 dbcontext 是即用即銷的,形如   (using WinLinkContext = new WinLinkContext() ){...} 那樣,有必要把dbcontext只實例化一個並緩存下來,這樣能提升性能和節省資源嗎? 開始因爲項目工期緊張,沒有考慮太多,想着這是別人使用過的成熟代碼,仍是某大咖的博文例子,應該錯不了。結果後來仍是出現了問題。某同事a寫日誌處理模塊的時候,也調用了這個 DbContextFactory.Create() ,本意是想把異常發生時的信息記錄到數據庫,但因爲和業務代碼公用一個dbcontext,問題就來了。好比某同事b寫了幾行  add/update EF的代碼,接着又寫了幾行其餘的代碼, 還沒執行 savechanges,但這幾行其餘代碼出現bug,引起異常,因而日誌捕捉到了,日誌模塊就進行使用ef進行插入日誌信息,執行savechanges,這時重點來了 , 這個savechanges就會把整個dbcontext的更新內容提交到數據庫了!因而連日誌模塊都報錯了,並且錯誤提示是同事b的代碼內容。固然也有讀者會說日誌模塊應該獨立開不該該和業務混在一塊兒,確實後來咱們也把它獨立開了。但這個例子代表 dbcontext使用單例模式有很大問題,我認爲仍是應該遵循微軟官方示例,即用即銷,就算要緩存,至少每一個模塊獨立開,不能整個應用使用惟一的一個。好比把上面的代碼稍加變化,根據名稱讀寫不一樣的dbcontext。數據庫

 public class DbContextFactory
    {
        public static WinLinkContext Create(string dbName)
        {
            WinLinkContext dbContext = CallContext.GetData(dbName) as WinLinkContext;
            if (dbContext == null)
            {
                dbContext = new WinLinkContext();
                CallContext.SetData(dbName, dbContext);
            }
            return dbContext;
        }
    }

另外還有一個BaseDAL的類,用於封裝常規的增刪改查以及分頁等方法,代碼大體以下(裏面的db對象就是上文提到dbcontext):小程序

  public T Add(T t) 
        {
            return db.Set<T>().Add(t);
        }

        public void Update(T t) 
        {
            db.Set<T>().AddOrUpdate(t);
        }

        public void Delete(T t) 
        {
            db.Set<T>().Remove(t);
        }
        public bool Delete(string id)
        {
            var entity = db.Set<T>().Find(id);
            if (entity == null)
            {
                return false;
            }
            db.Set<T>().Remove(entity);
            return true;
        }

        public int Delete(string[] ids)
        {
            foreach (var item in ids)
            {
                var entity = db.Set<T>().Find(item);//若是實體已經在內存中,那麼就直接從內存拿,若是內存中跟蹤實體沒有,那麼才查詢數據庫。
                db.Set<T>().Remove(entity);
            }
            return ids.Count();
        }

        public T Find(Expression<Func<T, bool>> findLambda)
        {
            return db.Set<T>().FirstOrDefault(findLambda);
        }

        public T Find(string id)
        {
            return db.Set<T>().Find(id);
        }

        public IQueryable<T> Where(Expression<Func<T, bool>> whereLambda)
        {
            return db.Set<T>().Where(whereLambda);
        }

        public IQueryable<T> GetByPage<Type>(int pageSize, int pageIndex, out int total, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Type>> orderByLambda, bool isAsc)
        {
            var queryData = db.Set<T>().Where(whereLambda);

            //是否升序
            if (isAsc)
            {
                queryData = queryData.OrderBy(orderByLambda);
            }
            else
            {
                queryData = queryData.OrderByDescending(orderByLambda);
            }

            total = queryData.Count();
            if (total > 0)
            {
                if (pageIndex <= 1)
                {
                    queryData = queryData.Take(pageSize);
                }
                else
                {
                    queryData = queryData.Skip((pageIndex - 1) * pageSize).Take(pageSize);
                }

            }

            return queryData;
        }
相關文章
相關標籤/搜索