简体   繁体   中英

User defined filters/queries?

I'm trying to figure out a way to allow users of my application to define their own queries or filters that can be applied to a collection. I want to eventually provide an intuitive user interface to create these filters (see image below), but initial it would be OK if a user had to type of a text query string. I'll also need to be able to serialized these user defined queries to a string so I can save them with the rest of the project information.

Example UI alt text http://dl.dropbox.com/u/113068/filterUI.jpg

I'm looking for the type of functionality you would get out of something like a SQL query such as numeric conditions (less than, greater than), string condition (contains, starts with, ends with), or boolean condition (true or false). I'd also like to be able to group condition using boolean logic like OR, AND, and NOT.

I started drawing out how I would do this from scratch using classes such as NodeFilter, AbsNodeCondition, NodeStringCondition, NodeConditionOrJoin etc.. etc.. But I feel like I'm reinventing the wheel, especially when there's something like Linq available, which I haven't had a chance to spend much time with.

Could I somehow allow a user to enter a Linq to objects query in a text box, and then programaticly turn the string into a real Linq query that can be applied to my collection? Or is there some other way I could allow the user to create and save a query? I'll also need a way to serialize the filter/query to a string so I can save it with the rest of the program information.

This is not necessarily an ideal solution, but the MetaLinq project provides serializable wrappers for the LINQ expression classes.

Here is an example of how you can recreate and execute an expression after round-trip serializing it.

First we serialize an existing lamba expression into an xml document.

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();
}

Then we deserialize, unwrap and compile it back into a delegate, ready to be executed.

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);

This is what the serialized expression looks like.

<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>

System.Linq.Expressions is unfortunately not serializable, not even in 4.0. You'll have to resort to write your own expression trees and visitors to generate queries from them if you have to cross appdomain boundaries. Otherwise, in box expression trees will be fine.

Apart from that, you may find dynamic linq suiting to your needs somewhat.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM