LINQ ve Lambda ile Katılın / Nerede


458

LINQ ve Lambda yazılmış bir sorgu ile sorun yaşıyorum. Şimdiye kadar, benim hata burada bir sürü hata alıyorum:

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

LINQ kullanmak için yeniyim, bu yüzden bu sorgunun doğru olup olmadığından emin değilim.


11
ne başarmaya çalışıyorsun?
Germán Rodríguez

4
sorgunun bir cümlede ne yapmasını istiyorsunuz?
avcı

6
Anahtar seçiciler olan yolu çok karmaşık. Kimlikle seçmek istiyorsanız, sadece x => x.ID iyidir.
Eric Lippert

1
Veritabanından bir gönderi ve bu gönderinin meta verilerini almak istedim.
David

Yanıtlar:


1057

SQL sözdizimine aşina iseniz, LINQ sorgu sözdizimini kullanmanın çok daha net, daha doğal olduğunu ve hataları tespit etmeyi kolaylaştırdığını görüyorum:

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

Eğer lambdas kullanmakta gerçekten sıkışıp kalırsanız, sözdiziminiz biraz kapalıdır. Aynı sorgu, LINQ uzantısı yöntemlerini kullanarak:

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, düzenlemenizle ilgili olarak, "Kimlik alanlarında eşitlik JOIN koşulunda ayarlandı; WHERE deyimini kullanmanıza gerek yok!": WHERE deyimi kimlik alanları arasındaki eşitliği test etmiyor, posta kimliği arasındaki eşitliği test ediyor sütunu ve id parametresi sorgunun dışında bildirildi.
Daniel Schaffer

9
Müthiş bir parçası lambdave teklif kullanımı ve anlaşılması kolay
Piotr Kula

1
harika örnek
oyuncak

1
Bazen lambda açıklamaları lambda'da yazılır. İyi açıkladı.
Çimdikle

80

Bununla iki yoldan gidebilirsin. Kullanılması LINQPad ve bir kukla veritabanı, aşağıdaki sorguları inşa (Sen LINQ yeni eğer çok değerli):

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

veya

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

Bu özel durumda, ben LINQ sözdizimi daha temiz olduğunu düşünüyorum (Ben okumak için en kolay bağlı olarak ikisi arasında geçiş).

Ama işaret etmek istediğim, eğer veritabanınızda uygun yabancı anahtarlar varsa, (post ve post_meta arasında), çok sayıda kayıt yüklemeye çalışmadığınız sürece muhtemelen açık bir birleştirmeye ihtiyacınız yoktur. . Örneğin, tek bir gönderi ve meta verileri yüklemeye çalıştığınızı gösteriyor gibi görünüyor. Her bir gönderi için birçok post_meta kaydı olduğunu varsayarsak, aşağıdakileri yapabilirsiniz:

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

N + 1 probleminden kaçınmak istiyorsanız, açıkça LINQ'dan SQL'e ilgili öğelerin tümünü bir seferde yüklemesini söyleyebilirsiniz (bu, L2S'ye daha aşina olduğunuzda gelişmiş bir konu olabilir). Aşağıdaki örnekte "bir Yazı yüklediğinizde, onunla ilişkili tüm kayıtları 'Post_metas' özelliği tarafından temsil edilen yabancı anahtar aracılığıyla da yüklenir" yazıyor:

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

Aynı türden veya birçok farklı türden LoadWitholuşan tek bir kümede birçok çağrı yapmak mümkündür DataLoadOptions. Bu lotları yine de yaparsanız, sadece önbelleğe almayı düşünebilirsiniz.


1
LinqPad ve CRM 2016 ?
Kiquenet

50

Daniel'in sözdizimi ilişkilerine ilişkin iyi bir açıklaması var, ancak anlamalarını biraz daha kolay hale getirmek için bu belgeyi ekibim için bir araya getirdim. Umarım bu birine yardımcı olurresim açıklamasını buraya girin


Burada olduğumuz gibi bir değer listesi ile uğraşırken bu işe yaramaz. Nesnede id özelliği yok.
Talspaugh27

Bunu gerçekten yararlı buldum, ancak birleştirme sütununu eklememi gerektiren bir hata aldım. Ayrıca @Mark Byers tarafından gönderilen yanıta bakıldığında, birleştirme sütununun Post_IDikinci takma addaki alanı vardır meta => meta.Post_ID. Bu çizimdeki örnekte g.id, orijinal select deyiminin bir JOIN gStatus g on g.idkısmı nihai Lambda ifadesinde çoğaltılmaz.
SausageFingers

3
Bu OP tarafından gönderilen cevap için gerekli gerçek linq bir referans olarak göndermeye çalışmıyordu, benim girişleri orijinal sorudan biraz farklı böylece bir Linq biçimine SQL taşımak için bir referans daha fazla oldu. Eğer gStatus değerleri için bir sınıf oluşturmuş olsaydım üzerine bir id özelliği koyardım ve sonra evet g => g.id ile katılırdım.
Talspaugh27

@ Talspaugh27 Öyleyse neden SQL sorgusunda g.id üzerinde gStatus'a katılıyor? Bu bir hata mı yoksa kasıtlı mı?
Drammy

@ Sql tablosunda her sütun bir isim olması gerekir, bu yüzden bu kesinlikle bu kimlikleri tutmak için 1 sütun tablo olduğundan, sadece id adlı bir sütun kullandım, List <int> bu sorunu yok. Eğer bu şekilde kurmuş public class IdHolder{ int id } olsaydım List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); o nesneyi gStatus'ta kullansaydım Linq'i değiştirirdi t =>t.value.TaskStatusId, g=>g.id ki bu değişiklik mantıklı mı?
19:19

37

Anahtar seçicileriniz yanlış. Söz konusu tablonun türünde bir nesne almalı ve birleşimde kullanmak için anahtarı döndürmelidirler. Bunu demek istediğini düşünüyorum:

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

Daha sonra where yan tümcesini, anahtar seçicinin parçası olarak uygulayamazsınız.


9

LINQ + EntityFramework'a başladığımda bir günlüğüne bu örneklere baktım.

Eğer EntityFramework kullanıyorsanız ve adlandırılmış bir navigasyon özelliği varsa Metasenin üzerinde Postmodeli nesnesi kurmak, bu kir kolaydır. Varlığı kullanıyorsanız ve bu navigasyon özelliğine sahip değilseniz, ne bekliyorsunuz?

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

Önce kod yapıyorsanız, mülkü bu şekilde kuracaksınız:

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

5

Ben böyle bir şey yaptım;

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

Gibi bir şey olabilir

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 eşittir 1 iki farklı masa birleşimi

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

Bu linq sorgusu sizin için çalışmalıdır. Post meta içeren tüm gönderileri alacaktır.

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

Eşdeğer SQL Sorgusu

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

üçüncü
paragraftan

3
Bu, kabul edilen cevapla aynıdır ve 7 yıl sonra -1
reggaeguitar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.