Запрос динамических данных - PullRequest
0 голосов
/ 01 ноября 2018

Короче я знаю в чем проблема, вот в этом ...

Невозможно использовать лямбда-выражение в качестве аргумента для динамически отправляемой операции без предварительного приведения его к типу дерева делегата или выражения

... Я также знаю, что это невозможно по умолчанию.

Мой вопрос (см. Ниже) здесь, как я могу сделать это возможным (даже если мне нужно динамически вызывать компилятор Roslyn, например, для генерации целой новой конкретной сборки)?

Есть ли способ сделать следующий запрос возможным в C #?

Итак, давайте предположим, что у меня есть следующие примеры данных ...

var data = @"
{
    ""$type"":""System.Dynamic.ExpandoObject, System.Core"",
    ""Items"":[
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":1, ""BuyerRef"":1, ""SupplierRef"":2, ""FaceValue"":1.34 },
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":1, ""BuyerRef"":1, ""SupplierRef"":2, ""FaceValue"":2.12 },
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":2, ""BuyerRef"":1, ""SupplierRef"":3, ""FaceValue"":100.0 },
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":3, ""BuyerRef"":1, ""SupplierRef"":2, ""FaceValue"":1.0 },
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":4, ""BuyerRef"":3, ""SupplierRef"":2, ""FaceValue"":1.0 },
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":5, ""BuyerRef"":4, ""SupplierRef"":1, ""FaceValue"":1.0 },
        { ""$type"":""System.Dynamic.ExpandoObject, System.Core"", ""Ref"":5, ""BuyerRef"":4, ""SupplierRef"":1, ""FaceValue"":1.0 }
    ],
    ""Companies"":[
        { ""CompanyId"":1, ""CompanyName"":""Sample Company 1"" },
        { ""CompanyId"":2, ""CompanyName"":""Sample company 2"" },
        { ""CompanyId"":3, ""CompanyName"":""ACME"" },
        { ""CompanyId"":4, ""CompanyName"":""HSBC Bank UK"" },
        { ""CompanyId"":5, ""CompanyName"":""Basic Buyer UK Ltd"" },
        { ""CompanyId"":6, ""CompanyName"":""Test Global US-CA"" },
        { ""CompanyId"":7, ""CompanyName"":""Test Global US-TX"" },
        { ""CompanyId"":8, ""CompanyName"":""Test Global US-NH"" },
        { ""CompanyId"":9, ""CompanyName"":""Test Global UK"" },
        { ""CompanyId"":10, ""CompanyName"":""Test Global FR"" }
    ]
}
";

Затем я анализирую с помощью Newtonsofts JsonConvert ...

object source = JsonConvert.DeserializeObject<ExpandoObject>(data);

Теперь я хочу сделать что-то вроде ...

var result = source.Items.Select(item => new
{
    Ref = item.Ref,
    FaceValue = item.FaceValue,
    BuyerId = item.BuyerRef,
    Buyer = Companies.Where(company => item.BuyerRef == company.CompanyId).Select(company => company).FirstOrDefault(),
    SupplierId = item.SupplierRef,
    Supplier = Companies.Where(company => item.SupplierRef == company.Ref).Select(company => company).FirstOrDefault()

})
.ToArray()
.GroupBy(i => new { i.Ref, i.Buyer, i.Supplier })
.Select(group => new
{
    Ref = group.Key.Ref,
    BuyerId = group.Key.Buyer.Ref,
    Buyer = group.Key.Buyer,
    SupplierId = group.Key.Supplier.Ref,
    Supplier = group.Key.Supplier,
    Lines = group.Select(i => new { Ref = i.Ref, FaceValue = i.FaceValue }).ToArray(),
    FaceValue = group.Sum(i => i.FaceValue)
})
.ToArray();

... Конечно, проблема в том, что данные полностью динамические и LINQ не будет работать с динамическими объектами.

Допущения, которые можно сделать ...

  1. данные могут быть любой структурой данных. Приведенный выше пример является иллюстрацией моей проблемы.
  2. Запрос проверен и проверен и определенно будет работать с данными, указанными, когда я хочу выполнить эту операцию, поэтому я рад избежать проверок на уровне компилятора, если это возможно.
  3. Предполагается, что мой результат такой же динамичный, и потребляющий код должен делать с этим то, что ему нужно.

Возможно ли это вообще в C #?

1 Ответ

0 голосов
/ 02 ноября 2018

Хорошо, у меня все работает с вашими примерами данных, но мне пришлось изменить значительное количество кода:

Сначала необходимо указать компилятору обрабатывать source.Items и source.Companies как значения IEnumerable<dynamic>. Это достаточно просто, просто извлекая их в локальные переменные и используя неявное преобразование:

IEnumerable<dynamic> items = source.Items;
IEnumerable<dynamic> companies = source.Companies;

Далее, ваши запросы разбиты в разных местах, потому что вы предполагаете, что записи компании имеют свойство Ref - они не имеют, они имеют только CompanyId. Вот где у меня есть опасения по поводу вашего предположения о том, что «запрос проверен и проверен, и он обязательно будет работать с данными» - если это не так для предоставленного вами образца, как вы собираетесь убедиться, что это так? на самом деле?

Я также преобразовал ваши ...Where(...).Select(company => company).FirstOrDefault() части в .FirstOrDefault(...) для простоты. Кроме того, чтобы Sum работал, вам действительно нужно убедиться, что каждый элемент имеет одинаковый тип - приведение типов является самым простым.

Наконец, я удалил промежуточный вызов ToArray, который не делает ничего особенно полезного, и использовал инициализаторы проекций, где имена свойств в анонимных типах - это имена свойств, из которых вы извлекаете. (Вы уже делаете это в GroupBy - я просто делаю это больше.)

Это оставляет код как:

dynamic source = JsonConvert.DeserializeObject<ExpandoObject>(data);
IEnumerable<dynamic> items = source.Items;
IEnumerable<dynamic> companies = source.Companies;
var result = items
    .Select(item => new
    {
        item.Ref,
        item.FaceValue,
        BuyerId = item.BuyerRef,
        Buyer = companies.FirstOrDefault(company => item.BuyerRef == company.CompanyId),
        SupplierId = item.SupplierRef,
        Supplier = companies.FirstOrDefault(company => item.SupplierRef == company.CompanyId)
    })
    .GroupBy(i => new { i.Ref, i.Buyer, i.Supplier })
    .Select(group => new
    {
        group.Key.Ref,
        BuyerId = group.Key.Buyer.CompanyId,
        group.Key.Buyer,
        SupplierId = group.Key.Supplier.CompanyId,
        group.Key.Supplier,
        Lines = group.Select(i => new { i.Ref, i.FaceValue }).ToArray(),
        FaceValue = group.Sum(i => (decimal) i.FaceValue)
    })
    .ToArray();
foreach (var item in result)
{
    Console.WriteLine(item);
}

Это, по крайней мере, выводит значения - я не знаю, те, которые вам нужны, или нет, но, надеюсь, они ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...