Entity Framework 實體框架的形成之旅--界面操作的幾個典型的處理(8)


在上篇隨筆《Entity Framework 實體框架的形成之旅--數據傳輸模型DTO和實體模型Entity的分離與聯合》里面,介紹了在Entity Framework 實體框架里面引入了DTO的對象,通過數據傳輸模型DTO和實體模型Entity的分離與聯合,很好的隔離了它們的關系,使得即使是復雜的實體模型Entity,也不會影響WCF接口數據的傳輸和處理。本文主要介紹在基於這個分離模型的基礎上,如何在界面實現多種常規的處理操作。

1、常規業務的增加、更新操作

對於業務對象的增加,由於我們引入了DTO對象,因此在界面的處理端,肯定也是利用了DTO對象進行的,如下代碼是增加、修改的處理操作處理。

       public bool SaveAddNew()
        {
            DictDataInfo info = new DictDataInfo();
            SetInfo(info);

            try
            {
                bool succeed = CallerFactory<IDictDataService>.Instance.Insert(info);
                if (succeed)
                {
                    int intSeq = 0;
                    string seqValue = this.txtSeq.Text;
                    if (int.TryParse(seqValue, out intSeq))
                    {
                        this.txtSeq.Text = (intSeq + 1).ToString().PadLeft(seqValue.Trim().Length, '0');
                    }
                    this.txtName.Focus();
                    this.txtName.SelectAll();
                }
                return succeed;
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(ex);
                MessageDxUtil.ShowError(ex.Message);
            }
            return false;
        }

        public override bool SaveUpdated()
        {
            DictDataInfo info = CallerFactory<IDictDataService>.Instance.FindByID(ID);

            if (info != null)
            {
                SetInfo(info);

                try
                {
                    bool succeed = CallerFactory<IDictDataService>.Instance.Update(info, info.ID.ToString());
                    return succeed;
                }
                catch (Exception ex)
                {
                    LogTextHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.Message);
                }
            }

            return false;
        }

上面的操作,和我之前的混合框架的使用代碼是差不多的,原來的基於EnterpriseLibrary架構的框架,實體類采用的就是 "表名+Info" 的方式,雖然這里的**Info代表DTO對象,是實體框架的Entity對象的映射類,不過總體業務上的處理代碼是差不多的了,這也是我希望看到比較平滑過渡和容易理解的改變之一。

2、基於DTO表達式的查詢處理

如果對於查詢,我們知道,如果使用字符串的條件表達式,一般也是可以實現處理操作的,不過就是需要硬編碼SQL語句,對於一些安全性高一點的處理,可能不太好,由於實體框架可以采用Lamda表達式來進行查詢,那么我們是否也可以在界面采用Lamda表達式來替代條件的SQL語句呢?

我們知道,上篇隨筆已經介紹了引入DTO對象,用來解耦實體框架的對象模型,如下所示的模塊分層場景。

這樣我們在設計BLL業務邏輯層的時候,肯定還是可以使用實體框架的Expression<Func<T, bool>>表達式的,如IBLL層的接口定義對於Expression表達式的使用接口如下所示。

        /// <summary>
        /// 根據條件查詢數據庫,並返回對象集合
        /// </summary>
        /// <param name="match">條件表達式</param>
        /// <returns></returns>
        IList<T> Find(Expression<Func<T, bool>> match);

        /// <summary>
        /// 根據條件表達式返回可查詢的記錄源
        /// </summary>
        /// <param name="match">查詢條件</param>
        /// <param name="sortPropertyName">排序屬性名稱</param>
        /// <param name="isDescending">如果為true則為降序,否則為升序</param>
        /// <returns></returns>
        IQueryable<T> GetQueryable(Expression<Func<T, bool>> match, string sortPropertyName, bool isDescending = true);

不過在門面層Facade層就不能繼續使用了這種Expression<Func<T, bool>>表達式的了,同時也不能在Facade層使用IQueryable<T>接口,因為WCF服務無法序列化這個接口的。

那基於這個原因,我們應該如何傳遞Expression<Func<T, bool>> match這個條件參數的表達式呢,答案是引入Serialize.Linq組件,使用ExpressionNode對象進行承載,最后再把它解析為Expression<Func<T, bool>> match進行處理就可以了。

        /// <summary>
        /// 根據條件查詢數據庫,並返回對象集合
        /// </summary>
        /// <param name="match">條件表達式</param>
        /// <returns></returns>
        [OperationContract(Name = "Find")]
        IList<DTO> Find(ExpressionNode match);

        /// <summary>
        /// 根據條件查詢數據庫,並返回對象集合(異步)
        /// </summary>
        /// <param name="match">條件表達式</param>
        /// <returns></returns>
        [OperationContract(Name = "FindAsync")]
        Task<IList<DTO>> FindAsync(ExpressionNode match);

我們在客戶端界面里面處理的話,就需要構建一個ExpressionNode對象,查詢處理代碼如下所示。

這里主要需要先從Expression<Function<T,boo>>到ExpressionNode,通過調用expression.ToExpressionNode();進行處理得到,如下代碼所示。

        private ExpressionNode GetCondtionSql()
        {
 Expression<Func<DictDataInfo, bool>> expression = p => p.DictType_ID == this.lblDictType.Tag.ToString();
            var queryNode = expression.ToExpressionNode();
            return queryNode;
        }

        private void BindData()
        {
            #region 添加別名解析
            this.winGridViewPager1.DisplayColumns = "Name,Value,Seq,Remark,EditTime";
            this.winGridViewPager1.AddColumnAlias("ID", "編號");
            this.winGridViewPager1.AddColumnAlias("DictType_ID", "字典大類");
            this.winGridViewPager1.AddColumnAlias("Name", "項目名稱");
            this.winGridViewPager1.AddColumnAlias("Value", "項目值");
            this.winGridViewPager1.AddColumnAlias("Seq", "字典排序");
            this.winGridViewPager1.AddColumnAlias("Remark", "備注");
            this.winGridViewPager1.AddColumnAlias("Editor", "修改用戶");
            this.winGridViewPager1.AddColumnAlias("EditTime", "更新日期");
            #endregion

            if (this.lblDictType.Tag != null)
            {
                ExpressionNode condition = GetCondtionSql();
                WHC.Pager.Entity.PagerInfo pagerInfo = this.winGridViewPager1.PagerInfo;
                IList<DictDataInfo> list = CallerFactory<IDictDataService>.Instance.FindWithPager(condition, ref pagerInfo);
                //this.winGridViewPager1.PagerInfo.RecordCount = pagerInfo.RecordCount;
                this.winGridViewPager1.DataSource = new WHC.Pager.WinControl.SortableBindingList<DictDataInfo>(list);
            }
        }

我們在Facade接口實現端,就需要把ExpressionNode反過來變成Expression<Function<T,boo>>對象。

        /// <summary>
        /// 根據條件查詢數據庫,並返回對象集合
        /// </summary>
        /// <param name="match">條件表達式</param>
        /// <returns></returns>
        public virtual IList<DTO> Find(ExpressionNode match)
        {
            Expression<Func<Entity, bool>> mappedSelector = ConvertExpression(match);

            IList<Entity> tList = baseBLL.Find(mappedSelector);
            return tList.MapToList<Entity, DTO>();
        }

        /// <summary>
        /// 根據條件查詢數據庫,並返回對象集合(異步)
        /// </summary>
        /// <param name="match">條件表達式</param>
        /// <returns></returns>
        public virtual async Task<IList<DTO>> FindAsync(ExpressionNode match)
        {
            Expression<Func<Entity, bool>> mappedSelector = ConvertExpression(match);

            IList<Entity> tList = await baseBLL.FindAsync(mappedSelector);

            IList<DTO> collection = tList.MapToList<Entity, DTO>();
            return await Task<IList<DTO>>.FromResult(collection);
        }

這樣我們就可以很好利用Entity Framework 實體框架的LINQ表達式進行查詢了。

3、多條件的處理方式

上面的查詢代碼里面,我們注意到了,條件里面只有一個條件,如下代碼。

        private ExpressionNode GetCondtionSql()
        {
            Expression<Func<DictDataInfo, bool>> expression = p => p.DictType_ID == this.lblDictType.Tag.ToString();
            var queryNode = expression.ToExpressionNode();
            return queryNode;
        }

那么對於有多個條件的話,處理就需要特殊處理了,否則就沒法組合多個條件進行查詢了,多個條件的處理是如何的呢?

如對於日志查詢界面來說,如果是采用條件語句的方式,需要使用下面的代碼組裝語句,然后通過接口方法進行獲取數據。

        /// <summary>
        /// 根據查詢條件構造查詢語句
        /// </summary> 
        private string GetConditionSql()
        {
            SearchCondition condition = new SearchCondition();
            condition.AddCondition("LoginName", this.txtLoginName.Text, SqlOperator.Like);
            condition.AddCondition("FullName", this.txtRealName.Text, SqlOperator.Like);
            condition.AddCondition("Note", this.txtNote.Text, SqlOperator.Like);
            condition.AddCondition("IPAddress", this.txtIPAddress.Text, SqlOperator.Like);
            condition.AddCondition("MacAddress", this.txtMacAddress.Text, SqlOperator.Like);

            if (dateTimePicker1.Text.Length > 0)
            {
                condition.AddCondition("LastUpdated", Convert.ToDateTime(dateTimePicker1.DateTime.ToString("yyyy-MM-dd")), SqlOperator.MoreThanOrEqual);
            }
            if (dateTimePicker2.Text.Length > 0)
            {
                condition.AddCondition("LastUpdated", Convert.ToDateTime(dateTimePicker2.DateTime.AddDays(1).ToString("yyyy-MM-dd")), SqlOperator.LessThanOrEqual);
            }

            string systemType = this.txtSystemType.GetComboBoxValue();
            if (!string.IsNullOrEmpty(systemType))
            {
                condition.AddCondition("SystemType_ID", systemType, SqlOperator.Equal);
            }

            //如果是公司管理員,增加公司標識
            if (Portal.gc.UserInRole(RoleInfo.CompanyAdminName))
            {
                condition.AddCondition("Company_ID", Portal.gc.UserInfo.Company_ID, SqlOperator.Equal);
            }

            string where = condition.BuildConditionSql().Replace("Where", "");
            //如果是單擊節點得到的條件,則使用樹列表的,否則使用查詢條件的
            if (!string.IsNullOrEmpty(treeConditionSql))
            {
                where = treeConditionSql;
            }
            return where;
        }

這里有很多條件,通過 SearchCondition 對象,我們能夠很方便組合多個條件的查詢,然后生成所需的條件語句就可以了,那么對於實體框架里面,我們需要采用Lamda表達式的話,應該如何構建對象並傳入給接口方法呢,代碼如下所示。

        /// <summary>
        /// 根據查詢條件構造查詢語句
        /// </summary> 
        private ExpressionNode GetConditionSql()
        {
            Expression<Func<LoginLogInfo, bool>> expression = p => true;
            if (!string.IsNullOrEmpty(this.txtLoginName.Text))
            {
                expression = expression.And(x => x.LoginName.Contains(this.txtLoginName.Text));
            }
            if (!string.IsNullOrEmpty(this.txtRealName.Text))
            {
                expression = expression.And(x => x.FullName.Contains(this.txtRealName.Text));
            }
            if (!string.IsNullOrEmpty(this.txtNote.Text))
            {
                expression = expression.And(x => x.Note.Contains(this.txtNote.Text));
            }
            if (!string.IsNullOrEmpty(this.txtIPAddress.Text))
            {
                expression = expression.And(x => x.IPAddress.Contains(this.txtIPAddress.Text));
            }
            if (!string.IsNullOrEmpty(this.txtMacAddress.Text))
            {
                expression = expression.And(x => x.MacAddress.Contains(this.txtMacAddress.Text));
            }

            if (dateTimePicker1.Text.Length > 0)
            {
                expression = expression.And(x => x.LastUpdated >= Convert.ToDateTime(dateTimePicker1.DateTime.ToString("yyyy-MM-dd")));
            }
            if (dateTimePicker2.Text.Length > 0)
            {
                expression = expression.And(x => x.LastUpdated <= Convert.ToDateTime(dateTimePicker2.DateTime.AddDays(1).ToString("yyyy-MM-dd")));
            }

            string systemType = this.txtSystemType.GetComboBoxValue();
            if (!string.IsNullOrEmpty(systemType))
            {
                expression = expression.And(x => x.SystemType_ID == systemType);
            }

            //如果是公司管理員,增加公司標識
            if (Portal.gc.UserInRole(RoleInfo.CompanyAdminName))
            {
                expression = expression.And(x => x.Company_ID == Portal.gc.UserInfo.Company_ID);
            }

            //如果是單擊節點得到的條件,則使用樹列表的,否則使用查詢條件的
            if (treeCondition != null)
            {
                expression = treeCondition;
            }
            return expression.ToExpressionNode();
        }

這里我們注意到expression.And或者expression.Or函數,它不是這個expression對象的方法的,是我們針對這個做的一個擴展類函數,它專門處理 Lamda-Expression表達式的擴展,方便組合多個條件,如兩個表達式條件可以組合為AND或者OR條件方式。

這樣我們在界面處理的時候,綁定數據的處理方法就可以如下所示了。

        public void BindData()
        {
            #region 添加別名解析
            this.winGridViewPager1.DisplayColumns = "ID,User_ID,LoginName,FullName,Company_ID,CompanyName,Note,IPAddress,MacAddress,SystemType_ID,LastUpdated";
            this.winGridViewPager1.ColumnNameAlias = CallerFactory<ILoginLogService>.Instance.GetColumnNameAlias();//字段列顯示名稱轉義

            #endregion

            ExpressionNode where = GetConditionSql();
            PagerInfo PagerInfo = this.winGridViewPager1.PagerInfo;
            IList<LoginLogInfo> list = CallerFactory<ILoginLogService>.Instance.FindWithPager(where, ref PagerInfo);
            this.winGridViewPager1.DataSource = new WHC.Pager.WinControl.SortableBindingList<LoginLogInfo>(list);
        }

以上就是我對於混合型的Entity Framework 實體框架的界面操作,總結的幾種分析場景,希望對大家理解在WCF模式里面,使用實體框架的方法有所幫助。

這個系列文章如下所示:

Entity Framework 實體框架的形成之旅--基於泛型的倉儲模式的實體框架(1)

Entity Framework 實體框架的形成之旅--利用Unity對象依賴注入優化實體框架(2) 

Entity Framework 實體框架的形成之旅--基類接口的統一和異步操作的實現(3)

Entity Framework 實體框架的形成之旅--實體數據模型 (EDM)的處理(4)

Entity Framework 實體框架的形成之旅--Code First的框架設計(5) 

Entity Framework 實體框架的形成之旅--Code First模式中使用 Fluent API 配置(6)

Entity Framework 實體框架的形成之旅--數據傳輸模型DTO和實體模型Entity的分離與聯合

Entity Framework 實體框架的形成之旅--界面操作的幾個典型的處理(8)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM