Únete / Dónde con LINQ y Lambda

458

Tengo problemas con una consulta escrita en LINQ y Lambda. Hasta ahora, recibo muchos errores aquí está mi código:

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

Soy nuevo en el uso de LINQ, por lo que no estoy seguro de si esta consulta es correcta.

David
fuente
11
¿Qué está tratando de lograr?
Germán Rodríguez
44
¿Qué quieres que haga la consulta en una oración?
cazador
66
Sus selectores de llave se forma demasiado complicado. Si desea seleccionar por id, solo x => x.ID está bien.
Eric Lippert
1
Quería obtener una publicación de la base de datos y los metadatos para esa publicación.
David

Respuestas:

1057

Creo que si está familiarizado con la sintaxis SQL, el uso de la sintaxis de consulta LINQ es mucho más claro, más natural y facilita la detección de errores:

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

Sin embargo, si realmente está atascado en el uso de lambdas, su sintaxis está bastante apagada. Aquí está la misma consulta, utilizando los métodos de extensión 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
Daniel Schaffer
fuente
10
@Emanuele Greco, con respecto a su edición, "La igualdad en los campos de ID está configurada en condición JOIN; ¡no necesita usar la cláusula WHERE!": La cláusula WHERE no está probando la igualdad entre los campos de ID, está probando la igualdad entre la ID de publicación columna y el parámetro id declarado fuera de la consulta.
Daniel Schaffer
99
Impresionante pieza de lambday es cita fácil de usar y entender
Piotr Kula
1
ejemplo impresionante
juguete
1
A veces las explicaciones de lambda se escriben en lambda. Explicado bien.
Pinch
80

Podrías ir de dos maneras con esto. Usando LINQPad (invaluable si eres nuevo en LINQ) y una base de datos ficticia, construí las siguientes consultas:

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 }

En este caso particular, creo que la sintaxis de LINQ es más limpia (cambio entre los dos dependiendo de cuál sea más fácil de leer).

Sin embargo, lo que me gustaría señalar es que si tiene claves foráneas apropiadas en su base de datos (entre post y post_meta), probablemente no necesite una unión explícita a menos que esté intentando cargar una gran cantidad de registros . Su ejemplo parece indicar que está intentando cargar una sola publicación y son metadatos. Suponiendo que hay muchos registros post_meta para cada publicación, puede hacer lo siguiente:

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

Si desea evitar el problema n + 1, puede decirle explícitamente a LINQ to SQL que cargue todos los elementos relacionados de una vez (aunque este puede ser un tema avanzado para cuando esté más familiarizado con L2S). El siguiente ejemplo dice "cuando cargue una publicación, también cargue todos sus registros asociados con ella a través de la clave externa representada por la propiedad '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

Es posible hacer muchas LoadWithllamadas en un solo conjunto DataLoadOptionspara el mismo tipo, o muchos tipos diferentes. Sin embargo, si hace esto mucho, es posible que desee considerar el almacenamiento en caché.

Damian Powell
fuente
1
LinqPad y CRM 2016 ?
Kiquenet
50

Daniel tiene una buena explicación de las relaciones de sintaxis, pero preparé este documento para mi equipo para que sea un poco más sencillo de entender. Espero que esto ayude a alguieningrese la descripción de la imagen aquí

Talspaugh27
fuente
Eso no funcionará cuando simplemente se trata de una lista de valores como estamos aquí. No hay propiedad de identificación en el objeto.
Talspaugh27
Encontré esto realmente útil, pero recibí un error que me obligó a agregar la columna de unión. También mirando la respuesta publicada por @Mark Byers, la columna de unión tiene el Post_IDcampo en el segundo alias meta => meta.Post_ID. En el ejemplo de esta ilustración, la g.idparte de la instrucción select original JOIN gStatus g on g.idno se replica en la expresión final de Lambda.
SausageFingers
3
No estaba tratando de publicar esto como una referencia al linq real requerido para responder publicado por el OP, era más una referencia de cómo mover SQL a un formato Linq, por lo que mis entradas eran un poco diferentes a la pregunta original. Si hubiera creado una clase para los valores de gStatus, habría puesto una propiedad de identificación y luego sí, se habría unido a g => g.id. Usé una lista de valores para tratar de mantener el código lo más simple posible.
Talspaugh27
@ Talspaugh27 Entonces, ¿por qué en la consulta SQL se une a gStatus en g.id? ¿Es eso un error o intencional?
Drammy
@Drammy en una tabla sql, cada columna debe tener un nombre, por lo que, dado que se trataba de una tabla de 1 columna estrictamente para contener estos identificadores, solo utilicé una columna llamada id, la Lista <int> no tiene ese problema. Si lo hubiera configurado como tal y public class IdHolder{ int id } luego hubiera usado ese objeto en el gStatus, List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); entonces habría cambiado el Linq para t =>t.value.TaskStatusId, g=>g.id que ese cambio tenga sentido.
Talspaugh27
37

Sus selectores clave son incorrectos. Deben tomar un objeto del tipo de tabla en cuestión y devolver la clave para usar en la unión. Creo que te refieres a esto:

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

Puede aplicar la cláusula where después, no como parte del selector de teclas.

Mark Byers
fuente
9

Publicando porque cuando comencé LINQ + EntityFramework, miré estos ejemplos por un día.

Si está utilizando EntityFramework, y tiene una propiedad de navegación nombrada Metaen su Postobjeto modelo configurado, esto es muy fácil. Si está utilizando una entidad y no tiene esa propiedad de navegación, ¿qué está esperando?

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

Si está haciendo código primero, configuraría la propiedad de esta manera:

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

He hecho algo como esto;

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);
Mahib
fuente
4

Podría ser algo como

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};
pepitomb
fuente
1

1 es igual a 1 dos tablas diferentes se unen

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 };
mtngunay
fuente
1

Esta consulta linq debería funcionar para usted. Obtendrá todas las publicaciones que tengan meta 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 });

Consulta SQL equivalente

Select * FROM Posts P
INNER JOIN Post_Metas pm ON pm.postId=p.postId
Ahamed Ishak
fuente
ha cerrado el paréntesis donde después de tercer parámetro ... "ninguna sobrecarga para Join tiene tres argumentos"
LastTribunal
3
Esto es idéntico a la respuesta aceptada y 7 años después -1
reggaeguitar