动态linq:有没有一种方法可以通过索引访问对象数据

本文关键字:可以通过 方法 索引 访问 数据 对象 一种 linq 有没有 动态 | 更新日期: 2023-09-27 18:21:35

我需要一些动态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);

有办法做到这一点吗?

动态linq:有没有一种方法可以通过索引访问对象数据

我有一些消息要告诉你。

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

我假设您使用的是动态Linq库。然后必须使用"it"-关键字才能指定要应用索引操作的当前对象。

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

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

对此有几种解决方案,其中一些您可以接受为解决方案。

  • 一个丑陋的解决方案,如果你有有限数量的数据类型,比如n(其中n<.NET中的类型数),可以使用n具有参数匹配的索引器,并获取所需的数据类型。对于例如,如果您有大部分int和一些字符串:

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

    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库中,可以在IxxSignatures接口中为要使用的类型添加成员。

因此,我在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);
    }

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

或者。。。我们可以使用以下方法将该方法放入DLINQ库中:

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'")

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

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

您只需要在索引器前准备it,它在DynamicLinq语言中等效于C#中的this

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

但是,由于您的索引器返回object,所以会出现一些更复杂的情况。对于类类型(包括string),DLinq将根据需要提升所有内容。

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

相关文章: