【问题标题】:IEnumerable<IDictionary<string, object>> to IQueryableIEnumerable<IDictionary<string, object>> 到 IQueryable
【发布时间】:2015-03-05 12:07:52
【问题描述】:

我有一个应用程序,它允许用户动态查询任何 OData 服务并将他们在网格内请求的特定列返回给他们。经过数周的研究,我最终使用Simple.OData.Client 来查询服务。为了取回数据,我有一个模型来定义需要做什么。以下是与我的问题相关的内容:

  • BaseUrl(服务地址)
  • ListName(要查询的表/列表)
  • 列(列表)
    • ODataColumnPath(我想要的数据的路径)
    • ColumnDataType(返回/要转换为的数据类型)
    • FriendlyName(易于使用的名称)
    • CalculationType(枚举None, Count, Sum, Min, Max

现在ODataColumnPath 可以像“ProductName”一样简单,也可以像“Category/Product/Orders/OrderID”一样复杂,以返回单个字段或返回多个字段。当它返回许多值时,我会用它进行某种计算。

目前,我通过所有IEnumerable&lt;IDictionary&lt;string, object&gt;&gt;s 递归循环(while 循环)来创建DataTable,直到我得到我正在寻找的值。然后,我使用 XML 数据创建 DataTable 列,然后从循环中填充行。这种方法效果很好,但我不得不认为有更好的方法来做到这一点。

现在,当我在 LinqPad 中运行直接连接到 a Northwind odata service 的查询时,我得到了一个 IQueryable&lt;Anonymous&gt; 对象。

LinqPad -> 北风

Products.Select (x => new { x.ProductID, x.ProductName, x.Category.CategoryName })

请求网址

http://demos.telerik.com/kendo-ui/service/Northwind.svc/Products()?$expand=Category&$select=ProductID,ProductName,Category/CategoryName

使用上面提到的 OData 库,我得到了与 IEnumerable&lt;IDictionary&lt;string, object&gt;&gt; 相同的数据

代码

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

现在,如果我指定一个强类型类,我会得到与 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();

我想要返回的是匿名列表对象或动态对象。如果需要,我可以从那里应用我的计算。有没有办法动态定义一个类并将其传递给For&lt;T&gt;(...)静态方法?

尽管我花了数周时间研究这个主题并最终使用 Simple.OData.Client,但我愿意使用其他一些方法来获取我的数据。

【问题讨论】:

    标签: c# linq datatable odata


    【解决方案1】:

    我最终找到了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);
    }
    

    如果你去掉所有未使用的方法、cmets 和变量,比如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,然后再返回到对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 2017-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多