LINQ-to-entity generic == обходной путь

У меня есть следующий запрос LINQ-to-entities

IQueryable> GetFirstOperationsForEveryId
    (IQueryable> ItemHistory)
{
    var q = (from h in ItemHistory
             where h.OperationId ==
                (from h1 in ItemHistory
                 where h1.GenericId == h.GenericId
                 select h1.OperationId).Min()
             select h);
    return q;
}

ItemHistory is a generic query. It can be obtained in the following way

var history1 = MyEntitiySet1.Select(obj =>
    new History{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
    new History{ obj.OperationId, GenericId = obj.StringId });

In the end of all I want a generic query being able to work with any entity collection convertible to History.

Проблема заключается в том, что код не компилируется из-за сравнения GenericId во внутреннем запросе (Operator '==' не может применяться к операндам типов T и T.

Если я изменяю == на h1.GenericId.Equals (h.GenericId) , я получаю следующий NotSupportedException :

Невозможно ввести тип «System.Int64» для ввода «System.Object». LINQ to Entities поддерживает только листинг примитивных типов данных Entity Data Model.

Я попытался сделать группировку вместо подзапроса и присоединиться к результатам.

IQueryable> GetFirstOperationsForEveryId
    (IQueryable> ItemHistory)
{
    var grouped = (from h1 in ItemHistory
                   group h1 by h1.GenericId into tt
                   select new
                   {
                        GenericId = tt.Key,
                        OperationId = tt.Min(ttt => ttt.OperationId)
                   });

    var q = (from h in ItemHistory
             join g in grouped
                on new { h.OperationId, h.GenericId }
                equals new { g.OperationId, g.GenericId }
             select h);
    return q;
}

Он компилируется, потому что GenericId сравнивается с ключевым словом equals , и он работает, но запрос с реальными данными слишком медленный (он работает в течение 11 часов на выделенном сервере postgresql).

Существует возможность построить целое выражение для выражения external where. Но код будет слишком длинным и неясным.

Существуют ли какие-либо простые обходные пути для сравнения равенства с дженериками в LINQ-to-entity?

2

4 ответы

Попробуйте это, я думаю, что он должен выполнить то, что вы хотите, без дополнительного запроса/присоединения

IQueryable> GetFirstOperationsForEveryId
    (IQueryable> ItemHistory)
{
  var q = from h in ItemHistory
          group h by h.GenericId into tt
          let first = (from t in tt
                        orderby t.GenericId
                        select t).FirstOrDefault()
          select first;

  return q;
}
1
добавлено
Довольно элегантное решение! Но, к сожалению, пока не реализовано в Npgsql (начиная с версии 2.0.11.92). System.Data.EntityCommandCompilationException: Произошла ошибка при подготовке определения команды. ---> System.NotImplementedException: метод или операция не реализованы. Моя фактическая проблема заключалась в том, что я забыл создавать индексы. Вот почему запрос group + join работал 11 часов. С индексами он работает достаточно быстро.
добавлено автор Mike, источник

Вы также можете установить общее ограничение на T для IItemHistory inteface, которое реализует свойство GenericId и OperationId.

0
добавлено

Мой вопрос уже содержит решение. Второй метод с group + join работает хорошо, если таблица правильно проиндексирована . Для извлечения 370 тыс. Строк из таблицы базы данных требуется 3,28 секунды. На самом деле в не-generic варианте первый запрос медленнее на postgresql, чем второй. 26,68 секунды против 4,75.

0
добавлено
IQueryable> GetFirstOperationsForEveryId
(IQueryable> ItemHistory)
{
var grouped = (from h1 in ItemHistory
               group t by h1.GenericId into tt
               select new
               {
                    GenericId = tt.Key,
                    OperationId = tt.Min(ttt => ttt.OperationId)
               });

var q = (from h in ItemHistory
         join g in grouped
            on new { h.OperationId, h.GenericId }
            equals new { g.OperationId, g.GenericId }
         select h);
return q;
}
0
добавлено
В чем отличие от того, что я написал?
добавлено автор Mike, источник
Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot