IEnumerable<IDictionary<string, object>> 到 IQueryable
IEnumerable<IDictionary<string, object>> to IQueryable
我有一个应用程序可以让用户动态查询任何 OData 服务,并 return 向他们查询他们在网格内请求的特定列。经过数周的研究,我最终使用 Simple.OData.Client 来查询服务。为了取回数据,我有一个模型来定义需要做什么。以下是与我的问题相关的内容:
- BaseUrl(服务地址)
- ListName(table/list 查询)
- 列(列表)
- ODataColumnPath(我想要的数据的路径)
- ColumnDataType(被return编辑/转换为的数据类型)
- FriendlyName(使用方便)
- 计算类型(枚举
None, Count, Sum, Min, Max
)
现在 ODataColumnPath
可以像 "ProductName" 一样简单,也可以像 "Category/Product/Orders/OrderID" 一样复杂到 return 单个字段或 return 多个字段。当它 return 有很多值时,我用它做一些计算。
目前我通过递归循环(while 循环)遍历所有 IEnumerable<IDictionary<string, object>>
来创建一个 DataTable
,直到我得到我正在寻找的值。然后,我使用 XML 数据创建 DataTable 列,然后填充循环中的行。这种方法效果很好,但我不得不认为有更好的方法。
现在,当我 运行 我在 LinqPad 中寻找的查询直接连接到 a Northwind odata service 时,我得到了一个 IQueryable<Anonymous>
对象。
LinqPad -> 罗斯文
Products.Select (x => new { x.ProductID, x.ProductName, x.Category.CategoryName })
请求URL
http://demos.telerik.com/kendo-ui/service/Northwind.svc/Products()?$expand=Category&$select=ProductID,ProductName,Category/CategoryName
使用上面提到的 OData 库,我得到了相同的数据,但是 IEnumerable<IDictionary<string, object>>
代码
ODataClient client = new ODataClient(new ODataClientSettings { UrlBase = "http://demos.telerik.com/kendo-ui/service/Northwind.svc/", OnTrace = (a, b) => { string.Format(a, b).Dump("Trace Event"); } });
var data = await client.For("Products").Expand("Category").Select("ProductID,ProductName,Category/CategoryName").FindEntriesAsync().Dump();
请求URL(来自跟踪事件)
http://demos.telerik.com/kendo-ui/service/Northwind.svc/Products?$expand=Category&$select=ProductID,ProductName,Category/CategoryName
现在,如果我指定一个强类型 class,我会得到与 IQueryable
相同的结果(需要一些额外的工作):
var strongly = (await client
.For<Product>()
.Expand(x => x.Category)
.Select(x => new { x.ProductID, x.ProductName, x.Category.CategoryName })
.FindEntriesAsync())
.Select(x => new { x.ProductID, x.ProductName, x.Category.CategoryName })
.Dump();
我想取回的是匿名列表对象或动态对象。如果需要,我可以从那里应用我的计算。有没有办法动态定义一个 class 并将其传递给 For<T>(...)
静态方法?
尽管我花了数周时间研究这个主题以最终使用 Simple.OData.Client,但我愿意使用其他方法来获取我的数据。
我最终找到了 LatticeUtils AnonymousTypeUtils.cs,它从字典中创建了一个匿名对象来创建一个匿名对象。
我最终用以下
修改了第一个 CreateObject
方法
public static object CreateObject(IDictionary<string, object> valueDictionary)
{
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> pair in valueDictionary)
{
if (pair.Value != null && pair.Value.GetType() == typeof(Dictionary<string, object>))
{
// Create object and add
object o = CreateObject(pair.Value as IDictionary<string, object>);
values.Add(pair.Key, o);
}
else if (pair.Value != null && pair.Value.GetType() == typeof(List<IDictionary<string, object>>))
{
// Get first dictionary entry
IDictionary<string, object> firstDictionary = ((IEnumerable<IDictionary<string, object>>)pair.Value).First();
// Get the base object
object baseObject = CreateObject(firstDictionary);
// Create a new array based off of the base object
Array anonArray = Array.CreateInstance(baseObject.GetType(), 1);
// Return like the others
values.Add(pair.Key, anonArray);
}
else
{
values.Add(pair.Key, pair.Value);
}
}
Dictionary<string, Type> typeDictionary = values.ToDictionary(kv => kv.Key, kv => kv.Value != null ? kv.Value.GetType() : typeof(object));
Type anonymousType = CreateType(typeDictionary);
return CreateObject(values, anonymousType);
}
如果你去掉所有未使用的方法、注释和变量,比如 mutable
,我最终得到 160 多行可读代码。
为了读取数据,我仍在使用 Simple.OData.Client 来获取数据,但我将对象序列化为 JSON,创建匿名对象,然后将所有内容反序列化为 IEnumerable
。有了这个,我能够在大约 0.35 秒左右的时间内处理来自我的 OData 服务的 1000 条记录。
// Get Data
ODataClient client = new ODataClient(new ODataClientSettings { UrlBase = "http://demos.telerik.com/kendo-ui/service/Northwind.svc/", OnTrace = (a, b) => { string.Format(a, b).Dump("Trace Event"); } });
IEnumerable<IDictionary<string, object>> data = await client
.For("Products")
.Expand("Category,Order_Details")
.Select("ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued,Category/CategoryName,Order_Details")
.FindEntriesAsync();
// Convert to JSON
string json = JsonConvert.SerializeObject(data);
// Create anonymous type/object
object anonymousObject = AnonymousClassBuilder.CreateObject(data.First());
// Deserialize into type
IEnumerable enumerable = (IEnumerable)JsonConvert.DeserializeObject(json, anonymousObject.GetType().MakeArrayType());
我最终可能会创建一个 Simple.OData.Client 的 Fork 并将其添加到其中,这样我就不必将对象序列化回 JSON 然后再返回到一个对象。
我有一个应用程序可以让用户动态查询任何 OData 服务,并 return 向他们查询他们在网格内请求的特定列。经过数周的研究,我最终使用 Simple.OData.Client 来查询服务。为了取回数据,我有一个模型来定义需要做什么。以下是与我的问题相关的内容:
- BaseUrl(服务地址)
- ListName(table/list 查询)
- 列(列表)
- ODataColumnPath(我想要的数据的路径)
- ColumnDataType(被return编辑/转换为的数据类型)
- FriendlyName(使用方便)
- 计算类型(枚举
None, Count, Sum, Min, Max
)
现在 ODataColumnPath
可以像 "ProductName" 一样简单,也可以像 "Category/Product/Orders/OrderID" 一样复杂到 return 单个字段或 return 多个字段。当它 return 有很多值时,我用它做一些计算。
目前我通过递归循环(while 循环)遍历所有 IEnumerable<IDictionary<string, object>>
来创建一个 DataTable
,直到我得到我正在寻找的值。然后,我使用 XML 数据创建 DataTable 列,然后填充循环中的行。这种方法效果很好,但我不得不认为有更好的方法。
现在,当我 运行 我在 LinqPad 中寻找的查询直接连接到 a Northwind odata service 时,我得到了一个 IQueryable<Anonymous>
对象。
LinqPad -> 罗斯文
Products.Select (x => new { x.ProductID, x.ProductName, x.Category.CategoryName })
请求URL
http://demos.telerik.com/kendo-ui/service/Northwind.svc/Products()?$expand=Category&$select=ProductID,ProductName,Category/CategoryName
使用上面提到的 OData 库,我得到了相同的数据,但是 IEnumerable<IDictionary<string, object>>
代码
ODataClient client = new ODataClient(new ODataClientSettings { UrlBase = "http://demos.telerik.com/kendo-ui/service/Northwind.svc/", OnTrace = (a, b) => { string.Format(a, b).Dump("Trace Event"); } });
var data = await client.For("Products").Expand("Category").Select("ProductID,ProductName,Category/CategoryName").FindEntriesAsync().Dump();
请求URL(来自跟踪事件)
http://demos.telerik.com/kendo-ui/service/Northwind.svc/Products?$expand=Category&$select=ProductID,ProductName,Category/CategoryName
现在,如果我指定一个强类型 class,我会得到与 IQueryable
相同的结果(需要一些额外的工作):
var strongly = (await client
.For<Product>()
.Expand(x => x.Category)
.Select(x => new { x.ProductID, x.ProductName, x.Category.CategoryName })
.FindEntriesAsync())
.Select(x => new { x.ProductID, x.ProductName, x.Category.CategoryName })
.Dump();
我想取回的是匿名列表对象或动态对象。如果需要,我可以从那里应用我的计算。有没有办法动态定义一个 class 并将其传递给 For<T>(...)
静态方法?
尽管我花了数周时间研究这个主题以最终使用 Simple.OData.Client,但我愿意使用其他方法来获取我的数据。
我最终找到了 LatticeUtils AnonymousTypeUtils.cs,它从字典中创建了一个匿名对象来创建一个匿名对象。 我最终用以下
修改了第一个CreateObject
方法
public static object CreateObject(IDictionary<string, object> valueDictionary)
{
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> pair in valueDictionary)
{
if (pair.Value != null && pair.Value.GetType() == typeof(Dictionary<string, object>))
{
// Create object and add
object o = CreateObject(pair.Value as IDictionary<string, object>);
values.Add(pair.Key, o);
}
else if (pair.Value != null && pair.Value.GetType() == typeof(List<IDictionary<string, object>>))
{
// Get first dictionary entry
IDictionary<string, object> firstDictionary = ((IEnumerable<IDictionary<string, object>>)pair.Value).First();
// Get the base object
object baseObject = CreateObject(firstDictionary);
// Create a new array based off of the base object
Array anonArray = Array.CreateInstance(baseObject.GetType(), 1);
// Return like the others
values.Add(pair.Key, anonArray);
}
else
{
values.Add(pair.Key, pair.Value);
}
}
Dictionary<string, Type> typeDictionary = values.ToDictionary(kv => kv.Key, kv => kv.Value != null ? kv.Value.GetType() : typeof(object));
Type anonymousType = CreateType(typeDictionary);
return CreateObject(values, anonymousType);
}
如果你去掉所有未使用的方法、注释和变量,比如 mutable
,我最终得到 160 多行可读代码。
为了读取数据,我仍在使用 Simple.OData.Client 来获取数据,但我将对象序列化为 JSON,创建匿名对象,然后将所有内容反序列化为 IEnumerable
。有了这个,我能够在大约 0.35 秒左右的时间内处理来自我的 OData 服务的 1000 条记录。
// Get Data
ODataClient client = new ODataClient(new ODataClientSettings { UrlBase = "http://demos.telerik.com/kendo-ui/service/Northwind.svc/", OnTrace = (a, b) => { string.Format(a, b).Dump("Trace Event"); } });
IEnumerable<IDictionary<string, object>> data = await client
.For("Products")
.Expand("Category,Order_Details")
.Select("ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued,Category/CategoryName,Order_Details")
.FindEntriesAsync();
// Convert to JSON
string json = JsonConvert.SerializeObject(data);
// Create anonymous type/object
object anonymousObject = AnonymousClassBuilder.CreateObject(data.First());
// Deserialize into type
IEnumerable enumerable = (IEnumerable)JsonConvert.DeserializeObject(json, anonymousObject.GetType().MakeArrayType());
我最终可能会创建一个 Simple.OData.Client 的 Fork 并将其添加到其中,这样我就不必将对象序列化回 JSON 然后再返回到一个对象。