繁体   English   中英

IEnumerable的 <IDictionary<string, object> &gt;到IQueryable

[英]IEnumerable<IDictionary<string, object>> to IQueryable

我有一个应用程序,允许用户动态查询任何OData服务,并向他们返回他们在网格内请求的特定列。 经过数周的研究,我最终使用Simple.OData.Client来查询服务。 为了取回数据,我有定义需要完成的工作的模型。 这与我的问题有关:

  • BaseUrl(服务地址)
  • ListName(要查询的表/列表)
  • 列(列表)
    • ODataColumnPath(指向所需数据的路径)
    • ColumnDataType(要返回/要强制转换为的数据类型)
    • FriendlyName(使用友好的东西)
    • CalculationType(枚举None, Count, Sum, Min, Max

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

当前,我通过递归遍历所有IEnumerable<IDictionary<string, object>>创建DataTable ,直到获得所需的值。 然后,我使用XML数据创建DataTable列,然后从循环中填充行。 这种方法效果很好,但我必须认为有一种更好的方法。

现在,当我运行查询时,我正在直接连接到Northwind odata服务的 LinqPad中寻找一个IQueryable<Anonymous>对象。

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<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

现在,如果我指定一个强类型的类,我将得到与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<T>(...)静态方法?

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

我最终找到了LatticeUtils AnonymousTypeUtils.cs ,它从Dictionary创建了一个匿名对象来创建一个匿名对象。 我最终使用以下命令修改了第一个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,然后再序列化回对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM