3

我需要使用 Dynamic Linq 进行一些内存过滤。我的对象只有一个索引器:

public  object this[int index] { }

访问我的数据是这样的:object[0], object[1],...

所以我的查询是这样的:

// get FilterText from user at runtime
// eg. filterText can be: [0] > 100 and [1] = "wpf"
collection.AsQueryable().where(filterText);

有没有办法做到这一点?

4

3 回答 3

2

我有一些消息要告诉你。

这实际上是可能的......但是!(是的,有一个但是)。

我假设您使用的是动态 Linq 库。然后,您必须使用“it”关键字来指定您希望对其应用索引操作的当前对象。

但是...索引器的返回数据类型是对象,因此由于数据类型不匹配,您将无法写入 [0] > 100 和 [1] = "wpf"。

此外,即使您从 DynamicObject 派生并在运行时添加属性,这些属性也不会在运行时由处于当前状态的动态 linq 库解析。您只会得到类型 xxx 中不存在的字段或属性。

有几种解决方案,您可能会接受其中一些作为解决方案。

  • 一种丑陋的解决方案,如果您的数据类型数量有限,例如 n(其中 n < .NET 中的类型数量),您可以使用 n 个索引器进行参数匹配并获得您想要的数据类型。例如,如果您主要有整数和一些字符串:

    it[0] > 100 AND it[1, "dummy"] = "wpf" //The dummy parameter allows you to specify return type.
    
  • 另一个丑陋的解决方案,Dynamic Linq 支持使用 ToString() 和 Convert-methods,因此,您可以例如编写与上述相同的查询:

    Convert.ToDouble(it[0]) > 100 AND it[1].ToString() = "wpf".
    
  • 第三种丑陋的解决方案,您可以使用一种约定,以一种告诉您如何转换数据的方式包装语句。例如,您可以编写:

    it[0] > 100 AND it{1} = "wpf"
    

    并将 "it[" 替换为 "Convert.ToDouble(it[" 等等...

如果我是正确的,我认为您也不能在库中使用通用索引器。Convert.ChangeType 在这种情况下对你没有好处,因为返回类型仍然是对象。

也许我或其他人会在一段时间内重写库以支持这些事情,但在不久的将来(几周)我没有时间这样做。

好吧,我很抱歉,但我必须在 15 分钟内到达某个地方,所以我希望以后我们必须采取更好的解决方案!

将自己传送到会议室

更新:

我想我可能已经找到了解决您(和我)问题的方法!

在 LINQ 库中,您可以在 IxxxSignatures 接口中为您希望能够使用的类型添加一个成员。

因此,我在 IEqualitySignatures 中添加了(例如):

void F(Object x, Int32 y);

并像这样修改了 else 块中的(在这种情况下)ParseComparison 方法。

left = Expression.Convert(left, right.Type);

而且,不管你信不信,它奏效了:)

我没有测试所有类型的操作,因为我没有添加其他类型和操作的签名,但是应该很简单!

更新

更新了上面的一些小东西..

我正在对此进行更多试验,虽然它可能也不是最漂亮的解决方案,但您可以执行以下操作(DataObject 只是带有索引器的 DynamicObject):

    [TestMethod]
    public void DynamicTest()
    {
        List<DataObject> dataObjects = new List<DataObject>();

        dynamic firstObject = new DataObject();
        dynamic secondObject = new DataObject();

        firstObject.dblProp = 10.0;
        firstObject.intProp = 8;
        firstObject.strProp = "Hello";

        secondObject.dblProp = 8.0;
        secondObject.intProp = 8;
        secondObject.strProp = "World";

        dataObjects.Add(firstObject);
        dataObjects.Add(secondObject);

        /* Notice the different types */
        string newQuery = FormatQuery("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'");

        var result = dataObjects.Where(newQuery);

        Assert.AreEqual(result.Count(), 1);
        Assert.AreEqual(result.First(), firstObject);
    }

还有某种格式方法,比如(假设我写了一个完整的方法):

    public string FormatQuery(string query)
    {
        query = query.Replace('\'', '\"');

        string[] operators = new string[] { "<", ">", "!=", "<=", ">=", "<>", "=" };

        string[] parts = query.Split();

        for (int i = 0; i < parts.Length; i++)
        {
            if (operators.Contains(parts[i]))
            {
                parts[i - 1] = "it[\"" + parts[i - 1] + "\"]";
            }
        }

        return String.Join(" ", parts);
    }

我们当然可以有一个扩展方法。

或者......我们可以使用类似的方法将方法放入 LINQ 库中(虽然不是一个好主意):

if (typeof(T).GetInterface("IDynamicMetaObjectProvider", true) != null)
    whereClause = whereClause.FormatQuery();

并且,检查该类型是否当然实现了字符串索引器,例如(在此处忽略 IndexerName 属性):

if (t.GetType().GetProperty("Item") != null)

这将使“普通用户”能够编写:

data.Where("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'")

好吧,也许把那些东西放在那里并不好,但你明白了。你可以有!:)

于 2012-01-28T10:49:34.140 回答
1

您只需要在您的索引器前面加上在 DynamicLinq 语言中与 C#it中等效的索引器。this

所以,你只需要it[1] == "wpf".

但是,由于您的索引器返回object. 对于类类型(包括string),你很好,DLinq 将根据需要提升所有内容。

但是,对于像 s 这样的值类型int,您必须执行Int32(it[0]) > 10.

于 2012-01-28T14:43:17.003 回答
1

您可以使用 DynamicLinq http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx或者构建您的表达式使用反射的树。很多人为此更喜欢 Dynamic Linq 库;我自己从未使用过它,我采用了反射方法。

于 2012-01-28T03:36:37.737 回答