2015-03-04 2 views
1

У меня есть приложение, которое позволяет пользователю динамически запрашивать любую службу OData и возвращать им конкретные столбцы, которые они запрашивали внутри сетки. После нескольких недель исследований я закончил использование Simple.OData.Client, чтобы запросить услугу. Чтобы вернуть данные, у меня есть модель, которая определяет, что нужно сделать. Вот то, что имеет отношение к моему вопросу:IEnumerable <IDictionary <string, object >> to IQueryable

  • BaseUrl (Service адрес)
  • ИМЯ_СПИСКА (таблица/список для запроса против)
  • Столбцы (Список)
    • ODataColumnPath (Путь к I данных хотите)
    • ColumnDataType (тип данных, возвращаемых/должны быть поданы в)
    • FRIENDLYNAME (что-то дружественное использовать)
    • C alculationType (Enum None, Count, Sum, Min, Max)

Теперь ODataColumnPath может быть столь же просто, как «ProductName» или быть столь же сложным, как «Категория/Product/Заказы/OrderID» возвращать одно поле или вернуть много. Когда он возвращает много значений, я делаю с ним какие-то вычисления.

В настоящее время я создаю DataTable путем рекурсивного цикла (while loop) через все IEnumerable<IDictionary<string, object>> с, пока не получу значения, которые я ищу. Затем я использую данные 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() $ расширение = Категория & $ выберите = ProductID, ProductName, категория/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?$ Расширение = Категория & $ выберите = ProductID, ProductName, категория/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, я открыт для использования другого метода получения моих данных.

ответ

0

В итоге я нашел 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 я закончил с 160ish строк читаемый код.

Для чтения данных я все еще использую Simple.OData.Client для получения моих данных, но я сериализую объект в JSON, создавая анонимный объект, а затем десериализую все обратно в IEnumerable. С этим я могу обрабатывать 1000 записей из моей службы OData примерно за 0,35 секунды или около того.

// 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 и добавив в него, так что я не должен сериализовать объект обратно в JSON, а затем обратно к объекту.

Смежные вопросы