Partecipa / Where con LINQ e Lambda


458

Sto riscontrando problemi con una query scritta in LINQ e Lambda. Finora, sto ricevendo molti errori ecco il mio codice:

int id = 1;
var query = database.Posts.Join(database.Post_Metas,
                                post => database.Posts.Where(x => x.ID == id),
                                meta => database.Post_Metas.Where(x => x.Post_ID == id),
                                (post, meta) => new { Post = post, Meta = meta });

Sono nuovo di usare LINQ, quindi non sono sicuro che questa query sia corretta.


11
cosa stai cercando di realizzare?
Germán Rodríguez,

4
cosa vuoi fare la query in una frase?
cacciatore

6
I suoi selettori a chiave sono modo troppo complicato. Se si desidera selezionare per ID, solo x => x.ID va bene.
Eric Lippert,

1
Volevo ottenere un post dal database e i metadati per quel post.
David,

Risposte:


1057

Trovo che se hai familiarità con la sintassi SQL, l'uso della sintassi della query LINQ è molto più chiaro, più naturale e semplifica l'individuazione degli errori:

var id = 1;
var query =
   from post in database.Posts
   join meta in database.Post_Metas on post.ID equals meta.Post_ID
   where post.ID == id
   select new { Post = post, Meta = meta };

Se sei davvero bloccato sull'uso di lambda, la tua sintassi è un po 'fuori. Ecco la stessa query, utilizzando i metodi di estensione LINQ:

var id = 1;
var query = database.Posts    // your starting point - table in the "from" statement
   .Join(database.Post_Metas, // the source table of the inner join
      post => post.ID,        // Select the primary key (the first part of the "on" clause in an sql "join" statement)
      meta => meta.Post_ID,   // Select the foreign key (the second part of the "on" clause)
      (post, meta) => new { Post = post, Meta = meta }) // selection
   .Where(postAndMeta => postAndMeta.Post.ID == id);    // where statement

10
@Emanuele Greco, per quanto riguarda la tua modifica, "L'uguaglianza sui campi ID è impostata nella condizione JOIN; non è necessario utilizzare la clausola WHERE!": La clausola WHERE non sta verificando l'uguaglianza tra i campi ID, sta verificando l'uguaglianza tra l'ID post colonna e il parametro id dichiarato all'esterno della query.
Daniel Schaffer,

9
Fantastico pezzo di lambdaed è citazione facile da usare e da capire
Piotr Kula

1
fantastico esempio
giocattolo

1
A volte le spiegazioni di lambda sono scritte in lambda. Spiegato bene.
Pizzica il

80

Potresti andare in due modi con questo. Utilizzando LINQPad (prezioso per chi non conosce LINQ) e un database fittizio, ho creato le seguenti query:

Posts.Join(
    Post_metas,
    post => post.Post_id,
    meta => meta.Post_id,
    (post, meta) => new { Post = post, Meta = meta }
)

o

from p in Posts
join pm in Post_metas on p.Post_id equals pm.Post_id
select new { Post = p, Meta = pm }

In questo caso particolare, penso che la sintassi LINQ sia più pulita (cambio tra i due a seconda di quale è più facile da leggere).

La cosa che vorrei sottolineare è che se nel tuo database sono presenti chiavi esterne appropriate (tra post e post_meta), probabilmente non avrai bisogno di un join esplicito a meno che tu non stia tentando di caricare un numero elevato di record . Il tuo esempio sembra indicare che stai cercando di caricare un singolo post e i suoi metadati. Supponendo che ci siano molti record post_meta per ogni post, è possibile effettuare le seguenti operazioni:

var post = Posts.Single(p => p.ID == 1);
var metas = post.Post_metas.ToList();

Se vuoi evitare il problema n + 1, puoi esplicitamente dire a LINQ to SQL di caricare tutti gli elementi correlati in una volta (anche se questo potrebbe essere un argomento avanzato per quando hai più familiarità con L2S). L'esempio seguente dice "quando carichi un Post, carica anche tutti i suoi record ad esso associati tramite la chiave esterna rappresentata dalla proprietà 'Post_metas'":

var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(p => p.Post_metas);

var dataContext = new MyDataContext();
dataContext.LoadOptions = dataLoadOptions;

var post = Posts.Single(p => p.ID == 1); // Post_metas loaded automagically

È possibile effettuare più LoadWithchiamate su un singolo set dello DataLoadOptionsstesso tipo o su molti tipi diversi. Se lo fai spesso, potresti prendere in considerazione la memorizzazione nella cache.


1
LinqPad e CRM 2016 ?
Kiquenet,

50

Daniel ha una buona spiegazione delle relazioni di sintassi, ma ho messo insieme questo documento per il mio team per renderlo un po 'più semplice da capire. Spero che questo aiuti qualcunoinserisci qui la descrizione dell'immagine


Non funzionerà quando hai semplicemente a che fare con un elenco di valori come se fossimo qui. Non esiste alcuna proprietà id sull'oggetto.
Talspaugh27,

L'ho trovato davvero utile, ma ho riscontrato un errore che mi ha richiesto di aggiungere la colonna di join. Anche guardando la risposta inviata da @Mark Byers, la colonna di join ha il Post_IDcampo nel secondo alias meta => meta.Post_ID. Nell'esempio in questa illustrazione, la g.idparte dell'istruzione select originale JOIN gStatus g on g.idnon viene replicata nell'espressione Lambda finale.
SausageFingers

3
Non stavo cercando di pubblicare questo come riferimento all'effettivo linq richiesto per rispondere pubblicato dall'OP, era più un riferimento per come spostare SQL in un formato Linq, quindi i miei input erano un po 'diversi dalla domanda originale. Se avessi creato una classe per i valori di gStatus avrei inserito una proprietà id su di essa e quindi sì, si sarebbe unita a g => g.id Ho usato un elenco di valori per cercare di mantenere il codice il più semplice possibile.
Talspaugh27,

@ Talspaugh27 Quindi perché nella query SQL si unisce a gStatus su g.id? È un errore o intenzionale?
Drammy,

@Drammy in una tabella sql ogni colonna deve avere un nome, quindi dato che questa era una tabella a 1 colonna strettamente per contenere questi ID, ho appena usato una colonna chiamata id, la Lista <int> non ha questo problema. Se lo avessi impostato come tale public class IdHolder{ int id } e avessi usato quell'oggetto in gStatus List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); , avrebbe cambiato Linq in modo t =>t.value.TaskStatusId, g=>g.id che il cambiamento abbia senso?
Talspaugh27,

37

I selettori chiave non sono corretti. Dovrebbero prendere un oggetto del tipo di tabella in questione e restituire la chiave da utilizzare nel join. Penso che intendi questo:

var query = database.Posts.Join(database.Post_Metas,
                                post => post.ID,
                                meta => meta.Post_ID,
                                (post, meta) => new { Post = post, Meta = meta });

In seguito è possibile applicare la clausola where, non come parte del selettore chiave.


9

Pubblicando perché quando ho iniziato LINQ + EntityFramework, ho fissato questi esempi per un giorno.

Se si utilizza EntityFramework, e si dispone di una proprietà di navigazione di nome Metasul vostro Postoggetto del modello impostato, questo è sporco facile. Se stai usando un'entità e non hai quella proprietà di navigazione, cosa stai aspettando?

database
  .Posts
  .Where(post => post.ID == id)
  .Select(post => new { post, post.Meta });

Se stai eseguendo prima il codice, dovresti impostare la proprietà in questo modo:

class Post {
  [Key]
  public int ID {get; set}
  public int MetaID { get; set; }
  public virtual Meta Meta {get; set;}
}

5

Ho fatto qualcosa del genere;

var certificationClass = _db.INDIVIDUALLICENSEs
    .Join(_db.INDLICENSECLAsses,
        IL => IL.LICENSE_CLASS,
        ILC => ILC.NAME,
        (IL, ILC) => new { INDIVIDUALLICENSE = IL, INDLICENSECLAsse = ILC })
    .Where(o => 
        o.INDIVIDUALLICENSE.GLOBALENTITYID == "ABC" &&
        o.INDIVIDUALLICENSE.LICENSE_TYPE == "ABC")
    .Select(t => new
        {
            value = t.PSP_INDLICENSECLAsse.ID,
            name = t.PSP_INDIVIDUALLICENSE.LICENSE_CLASS,                
        })
    .OrderBy(x => x.name);

4

Potrebbe essere qualcosa del genere

var myvar = from a in context.MyEntity
            join b in context.MyEntity2 on a.key equals b.key
            select new { prop1 = a.prop1, prop2= b.prop1};

1

1 è uguale a 1 due diversi join di tabella

var query = from post in database.Posts
            join meta in database.Post_Metas on 1 equals 1
            where post.ID == id
            select new { Post = post, Meta = meta };

1

Questa query linq dovrebbe funzionare per te. Riceverà tutti i post che hanno post meta.

var query = database.Posts.Join(database.Post_Metas,
                                post => post.postId, // Primary Key
                                meta => meat.postId, // Foreign Key
                                (post, meta) => new { Post = post, Meta = meta });

Query SQL equivalente

Select * FROM Posts P
INNER JOIN Post_Metas pm ON pm.postId=p.postId

hai chiuso le parentesi dove dopo il terzo parametro ... "nessun sovraccarico per Join prende tre argomenti"
LastTribunal

3
Ciò è identico alla risposta accettata e 7 anni dopo -1
reggaeguitar il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.