用户定义过滤器/查询?

时间:2009-12-18 04:38:58

标签: c# linq collections filtering

我正试图想办法让我的应用程序的用户定义他们自己的查询或可以应用于集合的过滤器。我想最终提供一个直观的用户界面来创建这些过滤器(见下图),但如果用户必须输入文本查询字符串,那么初始就可以了。我还需要能够将这些用户定义的查询序列化为字符串,以便我可以使用其余的项目信息保存它们。

示例用户界面 alt text http://dl.dropbox.com/u/113068/filterUI.jpg

我正在寻找一种类似于SQL查询的功能,例如数字条件(小于,大于),字符串条件(包含,开头,结束)或布尔条件(对或错)。我还希望能够使用像OR,AND和NOT这样的布尔逻辑对条件进行分组。

我开始绘制如何使用NodeFilter,AbsNodeCondition,NodeStringCondition,NodeConditionOrJoin等等从头开始这样做。但我觉得我正在重新发明轮子,特别是当Linq可用时,我没有机会花太多时间。

我可以以某种方式允许用户在文本框中输入Linq到对象查询,然后以编程方式将字符串转换为可应用于我的集合的真实Linq查询吗?或者是否有其他方式我可以允许用户创建和保存查询?我还需要一种方法将过滤器/查询序列化为一个字符串,这样我就可以用其余的程序信息保存它。

3 个答案:

答案 0 :(得分:1)

这不一定是理想的解决方案,但MetaLinq项目为LINQ表达式类提供了可序列化的包装。

以下是如何在往返序列化后重新创建和执行表达式的示例。

首先,我们将现有的lamba表达式序列化为xml文档。

var originalExpr = EditableExpression.CreateEditableExpression<string, bool>(
    str => str.Length > 3);
var serializer = new XmlSerializer(originalExpr.GetType());
string xml;

using (var writer = new StringWriter())
{
    serializer.Serialize(writer, originalExpr);
    xml = writer.ToString();
}

然后我们反序列化,解包并将其编译回委托,准备好执行。

EditableExpression newExpr;

using (var reader = new StringReader(xml))
{
    newExpr = (EditableExpression) serializer.Deserialize(reader);
}

var expr = (Expression<Func<string, bool>>) newExpr.ToExpression();
var items = new[] {"one", "two", "three"};
var result = items.Count(expr.Compile());
Debug.Assert(result == 1);

这就是序列化表达式的样子。

<EditableLambdaExpression xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <NodeType>Lambda</NodeType>
  <TypeName>System.Func`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</TypeName>
  <Body xsi:type="EditableBinaryExpression">
    <NodeType>GreaterThan</NodeType>
    <TypeName>System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</TypeName>
    <Left xsi:type="EditableMemberExpression">
      <NodeType>MemberAccess</NodeType>
      <Expression xsi:type="EditableParameterExpression">
        <NodeType>Parameter</NodeType>
        <TypeName>System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</TypeName>
        <Name>str</Name>
      </Expression>
      <MemberName>System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Int32 Length</MemberName>
    </Left>
    <Right xsi:type="EditableConstantExpression">
      <NodeType>Constant</NodeType>
      <Value xsi:type="xsd:int">3</Value>
    </Right>
  </Body>
  <Parameters>
    <EditableExpression xsi:type="EditableParameterExpression">
      <NodeType>Parameter</NodeType>
      <TypeName>System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</TypeName>
      <Name>str</Name>
    </EditableExpression>
  </Parameters>
</EditableLambdaExpression>

答案 1 :(得分:0)

请参阅my answer to a similar question,其中介绍了如何过滤从下拉列表中选择的属性。

答案 2 :(得分:0)

很遗憾,System.Linq.Expressions不可序列化,即使在4.0中也是如此。如果必须跨越appdomain边界,则必须编写自己的表达式树和访问者来生成查询。否则,在框表达式树中就可以了。

除此之外,您可能会在某种程度上找到符合您需求的动态linq。