动态linq:有没有办法通过索引访问对象数据?
我需要使用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);
有没有办法做到这一点?
我有一些消息给你。
实际上可能……但是! (是的,还有)。
我假设您正在使用动态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.
-
另一个丑陋的解决方案,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);
并且,信不信由你,它工作:)
我没有测试过各种操作,因为我没有为其他类型和操作添加签名,但它应该是非常直接的!
UPDATE
更新了上面的一些小东西..
我正在尝试更多这方面,虽然它可能不是最漂亮的解决方案,你可以做这样的事情(DataObject只是一个带有索引器的DynamicObject):
[TestMethod] public void DynamicTest() { List dataObjects = new List (); 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'")
好吧,也许在那里拥有那些东西并不好,但是你明白了。 你可以有! 🙂
您可以使用DynamicLinq http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx或者构建表达式树木使用reflection。 很多人更喜欢Dynamic Linq库; 我自己从未使用过它,我采用了reflection方法。
您只需要在其中添加一个与DynamicLinq语言等效的C#中的索引器。
所以,你只需要it[1] == "wpf"
。
但是,由于您的索引器返回object
,因此会有一些更复杂的问题。 对于类类型(包括string
),你很好,DLinq将根据需要推广所有内容。
但是,对于像int
的值类型,您必须执行Int32(it[0]) > 10
。