2012-05-16 15 views
6

Ho la seguente query in LINQ to Entities:LINQ to Entities non supporta Invoke

var query = from p in db.Products 
      where p.Price > 10M 
      select p; 

A questo punto la query non ha eseguito, e voglio scrivere una query che restituirà vero/falso Based in alcune condizioni:

return query.Any(p => p.IsInStock && 
       (p.Category == "Beverage" || 
       p.Category == "Other")); 

Questo funziona correttamente; tuttavia, vorrei ottenere un po 'di riutilizzo dal mio codice. Ho molti metodi che devono filtrare in base a se la categoria è una bevanda o di un altro, così ho cercato di creare un delegato:

Func<Product, bool> eligibleForDiscount = (product) => product.Category == "Beverage" || product.Category == "Other"; 

ho voluto sostituire il controllo in linea con il delegato:

return query.Any(p => p.IsInStock && eligibleForDiscount(p)); 

Questo mi dà un errore dicendo che LINQ alle entità non supporta Invoke. Perché non posso sostituire il codice inline per un delegato come questo, e c'è un modo per completare il mio riutilizzo in un altro modo?

+1

possibile duplicato del [Il LINQ tipo di nodo espressione 'invocare' non è supportato in LINQ to Entities in Entity Framework] (http: // StackOverflow .it/questions/8741667/the-linq-expression-node-type-invoke-is-not-support-in-linq-to-entities-in-e) –

+2

Considerando che la domanda non ha una risposta accettata e non ho idea di cosa sia questo PredicateBuilder, non considero questa domanda un duplicato. – Dismissile

risposta

6

Ricordare che sotto il cofano Linq-to-{DATABASE} sta semplicemente trasformando il IQueryable prodotto in Sql.

È possibile codice non in linea così perché un Invoke (il metodo che si sta effettivamente chiamando quando si chiama un Func o Action) non ha modo coerente per trasformarla in un'istruzione SQL (si potrebbe fare qualcosa di lì dentro).

Detto questo è possibile riutilizzare parti dividendo in su:

var query = from p in db.Products 
      where p.Price > 10M 
      select p; 

query = query.Where(p => p.IsInStock); 
query = query.Where(p => p.Category == "Beverage" || p.Category == "Other"); 
return query.Any(); 

Entrambi questi possono essere messi in metodi che accettano un IQueryable<Product> e restituire lo stesso (ma filtrata). Quindi puoi riutilizzare il contenuto del tuo cuore!

+0

Quindi il tuo suggerimento sarebbe quello di avere un metodo che richiede IQueryable e restituire IQueryable invece di un delegato. Immagino che entrambi facciano la stessa cosa :) – Dismissile

+0

Fanno esattamente la stessa cosa, ed è molto più riutilizzabile ... I metodi e le funzioni che rendono IQueryable e restituiscono IQueryable sono davvero potenti in questo modo. – Crisfole

+1

Come ulteriore vantaggio, questo può anche essere fatto in un metodo di estensione che fornisce anche una query dall'aspetto slick.WhereIsInAnyCategory ("Beverage", "Other") (o query.WhereBeverageOrOther()) digita la sintassi su un'interfaccia IQueryable . – MerickOWA

2

Il problema è che IQueryable deve generare un'espressione SQL da passare a RDBMS e non può farlo quando tutto ciò che ha è un predicato opaco.

Il modo più ovvio, ma inefficiente è di riscrivere la query come segue:

return query.Where(p => p.IsInStock).AsEnumerable().Any(eligibleForDiscount); 

Un modo meno banale sarebbe come segue:

bool GotEligible(Expression<Func<Product,bool>> pred) { 
    return query.Where(p => p.IsInStock).Any(pred); 
} 

Si noti come, invece di un predicato questo metodo richiede un espressione predicato. Ora è trasparente per EF e può essere convertito in una query SQL senza problemi.

+0

btw, vuoi sbarazzarti di '(p)' nella clausola 'Any' – Crisfole

+0

@ChristopherPfohl Sì, hai ragione! Grazie! – dasblinkenlight

+0

E * probabilmente * intendevi "GotEligible';;) – Crisfole

0

Fintanto che si rimane con IQueryable, è possibile mantenere le funzioni riutilizzabili.

public IQueryable<Product> EligibleForDiscount(IQueryable<Product> products) 
    { 
     return products.Where(p => product.Category == "Beverage" || 
            product.Category == "Other"); 
    } 

Ora lo chiamano come qualsiasi altra funzione:

IQueryable<Product> query = (from p in db.Products 
           where p.Price > 10M 
           select p); 

    query = EligibleForDiscount(query); 
Problemi correlati