Associar-se / Onde com LINQ e Lambda

458

Estou tendo problemas com uma consulta escrita em LINQ e Lambda. Até agora, estou recebendo muitos erros, eis o meu 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 });

Sou iniciante no uso do LINQ, portanto, não tenho certeza se esta consulta está correta.

David
fonte
11
O que você está tentando realizar?
Germán Rodríguez
4
o que você deseja que a consulta faça em uma frase?
hunter
6
Suas seletores chave são maneira muito complicado. Se você deseja selecionar por ID, apenas x => x.ID está correto.
Eric Lippert
1
Eu queria obter uma postagem do banco de dados e os metadados dessa publicação.
David

Respostas:

1057

Acho que se você estiver familiarizado com a sintaxe SQL, o uso da sintaxe de consulta LINQ é muito mais clara, mais natural e facilita a identificação de erros:

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

No entanto, se você realmente está preso ao uso de lambdas, sua sintaxe está um pouco diferente. Aqui está a mesma consulta, usando os métodos de extensão 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
fonte
10
@Emanuele Greco, em relação à sua edição, "A igualdade nos campos de ID está definida na condição JOIN; você não precisa usar a cláusula WHERE!": A cláusula WHERE não está testando a igualdade entre os campos de ID, está testando a igualdade entre o ID da postagem coluna e o parâmetro id declarado fora da consulta.
Daniel Schaffer
9
Parte impressionante de lambdae é citar fácil de usar e entender
Piotr Kula
1
exemplo impressionante
brinquedo
1
Às vezes, explicações sobre lambda são escritas em lambda. Explicou bem.
Aperte
80

Você poderia seguir dois caminhos com isso. Usando o LINQPad (inestimável se você é novo no LINQ) e um banco de dados fictício, criei as seguintes consultas:

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

ou

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

Nesse caso em particular, acho que a sintaxe do LINQ é mais limpa (alterno entre os dois, dependendo do que é mais fácil de ler).

O que eu gostaria de salientar é que, se você possui chaves estrangeiras apropriadas em seu banco de dados (entre post e post_meta), provavelmente não precisará de uma associação explícita, a menos que esteja tentando carregar um grande número de registros . Seu exemplo parece indicar que você está tentando carregar uma única postagem e seus metadados. Supondo que haja muitos registros post_meta para cada postagem, você pode fazer o seguinte:

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

Se você deseja evitar o problema n + 1, pode dizer explicitamente ao LINQ to SQL para carregar todos os itens relacionados de uma só vez (embora esse possa ser um tópico avançado para quando você estiver mais familiarizado com o L2S). O exemplo abaixo diz "quando você carrega uma postagem, também carrega todos os seus registros associados a ela por meio da chave estrangeira representada pela propriedade '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

É possível fazer várias LoadWithchamadas em um único conjunto DataLoadOptionspara o mesmo tipo ou vários tipos diferentes. Se você fizer isso muito, porém, considere apenas o cache.

Damian Powell
fonte
1
LinqPad e CRM 2016 ?
Kiquenet
49

Daniel tem uma boa explicação dos relacionamentos de sintaxe, mas eu juntei este documento para minha equipe, a fim de torná-lo um pouco mais simples para eles entenderem. Espero que isso ajude alguéminsira a descrição da imagem aqui

Talspaugh27
fonte
Isso não funcionará quando você estiver simplesmente lidando com uma lista de valores como estamos aqui. Não há propriedade id no objeto.
Talspaugh27
Achei isso realmente útil, mas recebi um erro que exigia a adição da coluna de junção. Observando também a resposta postada por @Mark Byers, a coluna de junção tem o Post_IDcampo no segundo alias meta => meta.Post_ID. No exemplo desta ilustração, a g.idparte da instrução select original JOIN gStatus g on g.idnão é replicada na expressão final do Lambda.
SausageFingers 13/03
3
Eu não estava tentando postar isso como uma referência ao linq real necessário para responder postado pelo OP, era mais uma referência de como mover o SQL para um formato Linq, para que minhas entradas fossem um pouco diferentes da pergunta original. Se eu tivesse criado uma classe para os valores de gStatus, eu teria colocado uma propriedade id e, sim, teria entrado em contato com g => g.id. Usei uma lista de valores para tentar manter o código o mais simples possível.
Talspaugh27
@ Talspaugh27 Então, por que na consulta SQL ele se junta ao gStatus no g.id? Isso é um erro ou intencional?
Drammy
@ Drammy em uma tabela sql, cada coluna precisa ter um nome; portanto, como essa era uma tabela de 1 coluna estritamente para armazenar esses IDs, usei apenas uma coluna chamada id, a lista <int> não tem esse problema. Se eu o tivesse configurado como tal public class IdHolder{ int id } , usaria esse objeto no gStatus List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); , ele teria mudado o Linq para t =>t.value.TaskStatusId, g=>g.id que essa mudança faça sentido?
Talspaugh27
37

Seus seletores de teclas estão incorretos. Eles devem pegar um objeto do tipo da tabela em questão e retornar a chave a ser usada na associação. Eu acho que você quer dizer isso:

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

Você pode aplicar a cláusula where posteriormente, não como parte do seletor de teclas.

Mark Byers
fonte
9

Postagem porque quando iniciei o LINQ + EntityFramework, observei esses exemplos por um dia.

Se você estiver usando o EntityFramework e tiver uma propriedade de navegação nomeada Metana Postconfiguração do objeto de modelo, isso é muito fácil. Se você estiver usando entidade e não tiver essa propriedade de navegação, o que está esperando?

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

Se você estiver criando o código primeiro, configure a propriedade da seguinte maneira:

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

Eu fiz algo assim;

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
fonte
4

Pode 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
fonte
1

1 é igual a 1 duas junções de tabela diferentes

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
fonte
1

Esta consulta linq deve funcionar para você. Ele receberá todas as postagens que possuem 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
fonte
você fechou os parênteses onde após terceira param ... "sem sobrecarga para Junte três argumentos"
LastTribunal
3
Isso é idêntico à resposta aceita e, 7 anos depois -1
reggaeguitar 26/03/19