‘Contiene()’ soluzione con Linq to Entities?

Sto cercando di creare una query che utilizza un elenco di id nella clausola where, utilizzando Silverlight ADO.Net client di Servizi Dati api (e, quindi, Linq To Entities). Qualcuno conosce una soluzione per non essere supportato?

Voglio fare qualcosa di simile a questo:

List<long?> txnIds = new List<long?>();
//Fill list 

var q = from t in svc.OpenTransaction
        where txnIds.Contains(t.OpenTransactionId)
        select t;

Provato questo:

var q = from t in svc.OpenTransaction
where txnIds.Any<long>(tt => tt == t.OpenTransactionId)
select t;

Ma ha Il “metodo” Qualsiasi “non è supportato”.

  • Nota: Entity Framework 4 (in .Rete 4) ha un “Contiene” il metodo, nel caso in cui qualcuno capita di essere la lettura di questo che non sa su di esso. So che l’OP è stato utilizzando EF1 (.NET 3.5).
  • Ho sprecato mezz’ora perché ho saltato il tuo commento. Vorrei poter rendere il tuo commento lampeggiare ed il perimetro attraverso lo schermo.

 

10 Replies
  1. 96

    Aggiornamento: EF ≥ 4 supporta “>Contiene direttamente (Checkout “>Qualsiasi), in modo da non avete bisogno di alcuna soluzione alternativa.

    public static IQueryable<TEntity> WhereIn<TEntity, TValue>
      (
        this ObjectQuery<TEntity> query,
        Expression<Func<TEntity, TValue>> selector,
        IEnumerable<TValue> collection
      )
    {
      if (selector == null) throw new ArgumentNullException("selector");
      if (collection == null) throw new ArgumentNullException("collection");
      if (!collection.Any()) 
        return query.Where(t => false);
    
      ParameterExpression p = selector.Parameters.Single();
    
      IEnumerable<Expression> equals = collection.Select(value =>
         (Expression)Expression.Equal(selector.Body,
              Expression.Constant(value, typeof(TValue))));
    
      Expression body = equals.Aggregate((accumulate, equal) =>
          Expression.Or(accumulate, equal));
    
      return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p));
    }
    
    //Optional - to allow static collection:
    public static IQueryable<TEntity> WhereIn<TEntity, TValue>
      (
        this ObjectQuery<TEntity> query,
        Expression<Func<TEntity, TValue>> selector,
        params TValue[] collection
      )
    {
      return WhereIn(query, selector, (IEnumerable<TValue>)collection);
    }

    USO:

    public static void Main()
    {
      using (MyObjectContext context = new MyObjectContext())
      {
        //Using method 1 - collection provided as collection
        var contacts1 =
          context.Contacts.WhereIn(c => c.Name, GetContactNames());
    
        //Using method 2 - collection provided statically
        var contacts2 = context.Contacts.WhereIn(c => c.Name,
          "Contact1",
          "Contact2",
          "Contact3",
          "Contact4"
          );
      }
    }
    • (“domanda relativa a” commento rimosso, come è stato eliminato dall’autore)
    • contrassegnati per l’ulteriore lettura +1… molto creativo : D
    • Veramente bello, elegante… Funziona bene.
    • Un’altra buona idea sarebbe avere la stessa funzione di dichiarare la raccolta di parametri come paramarray TValue[]. questo darebbe un numero illimitato di controllo è possibile specificare manualmente gli elementi come la raccolta, dirò di più, se necessario.
    • Attenzione; quando arg è grande collezione (la mia era 8500 elemento int list), di overflow dello stack. Si può pensare che un pazzo a passare come un elenco, ma credo che questo espone un difetto in questo approccio, tuttavia.
    • Mi corregga se ho sbagliato. ma questo significa che quando il passato di raccolta (filtro) è un insieme vuoto sarà sostanzialmente tutti i dati causa è appena tornato query param. Mi aspettavo per filtrare tutto il suo valore, c’è un modo per fare questo?
    • Se vuoi dire che quando il controllo insieme è vuoto deve restituire alcun risultato, in questo frammento di codice sostituire il if (!collection.Any()) //action; – azione di sostituire con la semplice restituzione di una query vuota del tipo richiesto per ottenere le migliori prestazioni, o semplicemente rimuovere questa riga.
    • di ritorno in Cui(query di selezione, raccolta); dovrebbe essere sostituito da restituire in Cui(query di selezione, (IEnumerable<T> e)raccolta); al fine di evitare indesiderati ricorsione.
    • Puoi fornire un link per la documentazione comprovante che Contiene è supportato in EF4? Sto avendo problemi di inseguimento verso il basso. Grazie!
    • L’ho testato e funziona, qual è il problema che stai avendo.
    • Sto solo cercando di vedere se è stato documentato in ogni dove. Io non sto avendo problemi.
    • Una volta ho visto e documentato in una connessione, ed è stato segnato “fissa”, ma non riusciva a farlo.
    • Credo che ci sia un bug nel codice. Se la dotazione elenco di valori è vuoto, il comportamento corretto dovrebbe essere quello di restituire risultati ie/ no oggetti della query nella collezione. Tuttavia, il codice non fa l’esatto opposto, tutti i valori vengono restituiti, non è nessuno di loro. Credo che si vogliono “if (!collezione.Qualsiasi()) return query.Dove(e => falso)”
    • ha un senso. risposta aggiornati.
    • Come faccio a fare qualcosa di simile a questo dove devo specificare esplicitamente i tipi. predicate = predicate.And(x=>Extensions.WhereIn<>(x.id,ids));. x è un Person Entity, e id e ids sono entrambi strings. Io non sono sicuro di cosa mettere tra le parentesi di WhereIn.
    • Le modifiche necessarie per creare una “WhereNotIn()” metodo?
    • hai trovato qualche soluzione a questo problema ?
    • Guardando indietro nel codice, sembra che ho finito per usare una stored procedure in Sql Server. Ho passato la lista di int in stored procedure come valori separati da virgola string (varchar max) che ho trasformato in un ritorno in un set all’interno della stored procedure. Hack per certo.

  2. 18

    Si può ripiegare su mano di codifica alcune e-sql (nota la parola chiave “it”):

    return CurrentDataSource.Product.Where("it.ID IN {4,5,6}"); 

    Qui è il codice che ho usato per generare un po ‘ di e-sql da collezione, YMMV:

    string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray();
    return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}");
    • Hai qualche info in più su “si”? Il “si” del prefisso si presenta in MSDN campioni, ma un posto dove posso trovare una spiegazione su quando e perché “essa” è necessario.
    • Utilizzato in Entity Framework query dinamica, dare un’occhiata a geekswithblogs.net/thanigai/archive/2009/04/29/…, Thanigainathan Siranjeevi spiega, non c’è.
  3. 13

    Da MSDN:

    static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
        Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
    {
        if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
        if (null == values) { throw new ArgumentNullException("values"); }
        ParameterExpression p = valueSelector.Parameters.Single();
    
        //p => valueSelector(p) == values[0] || valueSelector(p) == ...
        if (!values.Any())
        {
            return e => false;
        }
    
        var equals = values.Select(
                 value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
    
        var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
    
        return Expression.Lambda<Func<TElement, bool>>(body, p);
    } 

    e la query diventa:

    var query2 = context.Entities.Where(BuildContainsExpression<Entity, int>(e => e.ID, ids));
    • Se vuoi fare un ‘Non contiene’, basta apportare le seguenti modifiche nel BuildContainsExpression metodo: – Espressione.Pari diventa Espressione.NotEqual – Espressione.O diventa Espressione.E
  4. 2

    Io non sono sicuro di Silverligth, ma in linq to objects io uso sempre qualsiasi() per queste query.

    var q = from t in svc.OpenTranaction
            where txnIds.Any(t.OpenTransactionId)
            select t;
    • Qualsiasi non prendere un oggetto del tipo di sequenza – si o non ha parametri (in questo caso è solo “è vuota o non”) o ci vuole un predicato.
    • Sono terribilmente contento di aver trovato questa risposta : ) +1 Grazie AndreasN
  5. 1

    Per completare il record, ecco il codice che ho finalmente usato (controllo degli errori sono stati omessi per chiarezza)…

    //How the function is called
    var q = (from t in svc.OpenTransaction.Expand("Currency,LineItem")
             select t)
             .Where(BuildContainsExpression<OpenTransaction, long>(tt => tt.OpenTransactionId, txnIds));
    
    
    
     //The function to build the contains expression
       static System.Linq.Expressions.Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
                    System.Linq.Expressions.Expression<Func<TElement, TValue>> valueSelector, 
                    IEnumerable<TValue> values)
            {
                if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
                if (null == values) { throw new ArgumentNullException("values"); }
                System.Linq.Expressions.ParameterExpression p = valueSelector.Parameters.Single();
    
                //p => valueSelector(p) == values[0] || valueSelector(p) == ...
                if (!values.Any())
                {
                    return e => false;
                }
    
                var equals = values.Select(value => (System.Linq.Expressions.Expression)System.Linq.Expressions.Expression.Equal(valueSelector.Body, System.Linq.Expressions.Expression.Constant(value, typeof(TValue))));
                var body = equals.Aggregate<System.Linq.Expressions.Expression>((accumulate, equal) => System.Linq.Expressions.Expression.Or(accumulate, equal));
                return System.Linq.Expressions.Expression.Lambda<Func<TElement, bool>>(body, p);
            }
  6. 0

    Grazie molto. Quale metodo di estensione era abbastanza per me. Ho profilato e generato lo stesso comando SQL per il DataBase e sql.

    public Estado[] GetSomeOtherMore(int[] values)
    {
        var result = _context.Estados.WhereIn(args => args.Id, values) ;
        return result.ToArray();
    }

    Generato questo:

    SELECT 
    [Extent1].[intIdFRLEstado] AS [intIdFRLEstado], 
    [Extent1].[varDescripcion] AS [varDescripcion]
    FROM [dbo].[PVN_FRLEstados] AS [Extent1]
    WHERE (2 = [Extent1].[intIdFRLEstado]) OR (4 = [Extent1].[intIdFRLEstado]) OR (8 = [Extent1].[intIdFRLEstado])
  7. 0

    Mi dispiace nuovo utente, avrei commentato la risposta vera, ma sembra che non posso fare ancora?

    Comunque, per quanto riguarda la risposta con un codice di esempio per BuildContainsExpression(), essere consapevoli del fatto che se si utilizza questo metodo sull’Entità del database (cioè non-oggetti di memoria) e si utilizza IQueryable, che in realtà è quello di andare fuori al database in quanto, fondamentalmente, fa un sacco di SQL “o” condizioni di controllare il “dove” clausola (eseguito con SQL Profiler per vedere).

    Questo può significare, se si sono perfezionamento di un IQueryable con più BuildContainsExpression(), non si trasformano in una istruzione SQL che viene eseguito alla fine, come ci si aspetta.

    La soluzione per noi era quella di usare più LINQ unisce tenere per una chiamata SQL.

  8. 0

    In aggiunta alla risposta selezionata.

    Sostituire Expression.Or con Expression.OrElse per l’uso con Nhibernate e fix Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type
    'NHibernate.Hql.Ast.HqlBooleanExpression'
    eccezione.

Lascia un commento