LINQ-to-entities generieke == workaround

Ik heb een volgende LINQ-naar-entiteitsquery

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.

Het probleem is dat de code niet compileert vanwege GenericId-vergelijking in de interne query (Operator '==' kan niet worden toegepast op operanden van het type 'T' en 'T').

Als ik == in h1.GenericId.Equals (h.GenericId) verander, krijg ik de volgende NotSupportedException :

Kon het type 'System.Int64' niet casten om 'System.Object' in te typen. LINQ to Entities ondersteunt alleen primitieve typen voor entiteitsgegevensmodellen.

Ik heb geprobeerd om te groeperen in plaats van subquery en deel te nemen aan de resultaten.

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

Het compileert omdat GenericId's worden vergeleken met equals zoekwoord en het werkt, maar de query met echte gegevens is te traag (deze loopt al 11 uur op een speciale postgresql-server).

Er is een optie om een ​​hele expressie te bouwen voor de instructie outer waar. Maar de code zou te lang en onduidelijk zijn.

Zijn er eenvoudige oplossingen voor gelijkheidsvergelijking met generieke geneesmiddelen in LINQ-entiteiten?

2

4 antwoord

Probeer dit, ik denk dat het moet volbrengen wat je wilt zonder de extra vraag/join

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
toegevoegd
Heel elegante oplossing! Maar helaas nog niet geïmplementeerd in Npgsql (vanaf 2.0.11.92). System.Data.EntityCommandCompilationException: er is een fout opgetreden bij het voorbereiden van de opdrachtdefinitie. ---> System.NotImplementedException: de methode of bewerking is niet geïmplementeerd. Mijn werkelijke probleem was dat ik ben vergeten om indexen te maken. Dat is de reden waarom groep + join-zoekopdracht 11 uur werkte. Met indexen werkt het redelijk snel.
toegevoegd de auteur Mike, de bron

U kunt ook een generieke beperking op T instellen voor een inteface van de IItemHistory waarmee de eigenschap GenericId en OperationId wordt geïmplementeerd.

0
toegevoegd

Mijn vraag bevat al een oplossing. De tweede methode met groep + join werkt goed als de tabel correct is geïndexeerd . Het kost 3,28 seconden om 370k rijen uit de databasetabel op te halen. In feite is de eerste query in niet-generieke variant langzamer op postgresql dan de tweede. 26,68 seconden versus 4,75.

0
toegevoegd
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
toegevoegd
Wat is het verschil met wat ik heb gepost?
toegevoegd de auteur Mike, de bron