动态linq:有没有办法通过索引访问对象数据?

时间:2012-01-28 02:59:44

标签: c# casting dynamic-linq indexer

我需要使用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);

有没有办法做到这一点?

3 个答案:

答案 0 :(得分:2)

我有一些消息给你。

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

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

但是...索引器的返回数据类型是object,因此您将无法编写[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分钟内到达某个地方,所以我们希望以后能够采取更好的解决方案!

将自己传送到会议

<强>更新

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

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

所以,我在IEqualitySignatures中添加了(例如):

void F(Object x, Int32 y);

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

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

我们当然可以使用扩展方法。

或者......我们可以使用类似的方法将该方法放在DLINQ lib中(尽管不是说这个好主意):

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'")
好吧,也许在那里拥有那些东西并不好,但你明白了。你可以有! :)

答案 1 :(得分:1)

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

答案 2 :(得分:1)

您只需要在索引器前加上it,它在DynamicLinq语言中与C#中的this相同。

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

但是,由于您的索引器返回object,因此会出现更多复杂情况。对于班级类型(包括string)你很好,DLinq将根据需要推广所有内容。

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