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模式里面,使用实体框架的方法有所帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容