65.9K
CodeProject 正在变化。 阅读更多。
Home

动态 Linq 的可视化表达式生成器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (13投票s)

2010年7月1日

CPOL

4分钟阅读

viewsIcon

138450

downloadIcon

2471

让用户使用您可以在应用程序中包含的 MVVM WPF 工具创建自己的 Linq 过滤器

已更新!源代码已更新,使动态类型更加健壮!

目录

ScreenShot.png

引言

LINQ 是一种以类型安全、直观且极具表达力的方式声明性地过滤和查询数据的绝佳方法。但是,您的用户不应该每次有新的过滤或查询数据的方式时都必须联系您。

因此,这里提供了一种让您的用户过滤他们数据的方法,无论数据来自何处。无论是 Linq2SQL 还是内存结构,现在您的用户都可以拥有这种能力。

这是您可以为用户添加的最有价值的功能,只需 **4 行代码**。
我还建议您实际阅读代码。整个项目只有 233 行代码,包括 XAML。

Using the Code

真正的功能位于 ViewModel 中的两个类中。UserControl 只是 ExpressionList 的一个经过美化的 DataTemplate

ClassDiagram.png

我知道你们大多数人都想知道这个好东西是如何工作的,以便你们可以在自己的解决方案中使用相同的技术。但是,您可以直接将 UserControl 添加到 XAML 中,并将其绑定到 ExpressionList 来使用它,所以我就从这里开始。

<uc:FilterUserControl DataContext="{Binding ExpressionList1}" />

在您的 ViewModel 的某个地方

public     ExpressionList ExpressionList1    { get; set; } 
ExpressionList1 = new ExpressionList() {Type=typeof(Contact)} ; 

然后,一旦您的用户选择了所有过滤器,您就可以这样获得最终查询:

 var filteredContacts = 
    contacts.Where(ExpressionList1.GetCompleteExpression<Contact>()); 

背景

我首先尝试了许多其他动态 LINQ 解决方案。有一个“DynamicLinq”项目,在 ScottGu 的博客 中进行了介绍,该项目允许您解析字符串中的表达式。因此,您的用户可以在文本框中编写自己的表达式,然后您可以将其应用于您的 LINQ。我从他们的实现中学到了很多关于表达式的知识。

然后,有一个我差点使用的解决方案 - VB 工具中的动态 LINQ 工具。 它几乎完全满足了我的需求,您会注意到它与我今天展示的工具非常相似。最大的问题是它为 WinForms 编写的,并且功能与表示层过度耦合。我实际上需要 MVVM(之前是 MVP)的一个主要好处——在 UI 消失时仍然能够持久化状态,并且可以轻松创建默认和已保存的过滤器集。

究竟发生了什么?

正如我之前所说,真正的功能位于 ViewModel 中的两个类中(我将内置的 Expression 类视为我的模型)

  • ExpressionVM 包含创建具有一个比较的表达式所需的数据和功能。
  • ExpressionListExpressionVM 的一个可观察集合,其中有一个属性用于从所有子表达式创建 lambda 表达式。

ExpressionList 仅仅是一个 ObservableCollection<ExpressionVM>,并带有两个重要属性:

  • Type Type - 用于生成表达式,并用于填充 ExpressionVM 中的 AvailableProperties
  • Expression CompleteExpression - 将所有 ExpressionVM 中的 Expressions 组合成一个 lambda 表达式。

ExpressionVM 的成员是:

  • Type ObjectType - 被过滤的类型(通常从 ExpressionList 设置)
  • PropertyInfo PropertyInfo - 关于用户选择在此行中进行比较的属性的信息。
  • string PropertyName - (只读) 来自 PropertyInfo
  • Type PropertyType - (只读) 来自 PropertyInfo
  • CombineOperator - 由用户选择,用于确定如何将此行与上一行组合。
  • CompareOperator - 由用户选择,用于确定如何将属性与常量进行比较。
  • object Value - 用于比较的常量。
  • AvailableCombineOperators, AvailableCompareOperators, AvailableProperties - 用于填充 ComboBoxes 的查找列表。
  • GetSupportedTypes - 因此我们不会给用户过滤图像的选项 :)
  • MakeExpression - 根据用户的选择创建表达式。

解决方案的核心内容

最有趣的代码包含在两个函数 MakeExpressionCompleteExpression 的 getter 中。

public LambdaExpression MakeExpression(ParameterExpression paramExpr = null)
{
    if(paramExpr == null) paramExpr = Expression.Parameter(ObjectType, "left");
            
    var callExpr = Expression.MakeMemberAccess(paramExpr, PropertyInfo);
    var valueExpr = Expression.Constant(Value, PropertyType);
    var expr = Expression.MakeBinary((ExpressionType)CompareOperator, 
        callExpr, valueExpr);
            
    return Expression.Lambda( expr, paramExpr);
}
public Expression CompleteExpression
{
    get
    {
        var paramExp = Expression.Parameter(Type, "left");
        if (this.Count == 0) return Expression.Lambda
        (Expression.Constant(true), paramExp);
        LambdaExpression lambda1 = this.First().MakeExpression(paramExp);
        var ret = lambda1.Body;
        foreach (var c in this.Skip(1))
            ret = Expression.MakeBinary((ExpressionType)c.CombineOperator, 
        ret, c.MakeExpression(paramExp).Body);
        return Expression.Lambda(ret, paramExp);
    }
} 

CompleteExpression 使用 ObjectType 创建一个输入参数,并使用第一个 ExpressionVM 创建一个 lambda。然后它遍历其余的 ExpressionVM,根据它们的 CombineOperator 将它们从 MakeExpression 获取的表达式附加到 lambda。

其他有趣的代码片段

我为每个 ComboBox 提供了查找列表。

每次 ObjectType 更改时,AvailableProperties 都会被填充。

set { 
        _ObjectType = value;
        AvailableProperties = from p in value.GetProperties()
                    where GetSupportedTypes().Contains(p.PropertyType)
                    select p;
        OnPropertyChanged("ObjectType");
    }

CombineOperatorCompareOperator 都是 enum。因此,它们的列表会像这样即时生成:

public Array AvailableCompareOperators
{
    get { return Enum.GetValues(typeof(ComparisonOperators)); }
}

待办事项

是的,还有很多工作要做,并且还有很大的改进空间。
这里有一个我想到的小列表:

  • 添加更多运算符
  • 允许 AND 和 OR 嵌套
  • 允许选择子属性
  • 让 WPF 根据 PropertyType 选择专门的模板
© . All rights reserved.