Приєднуйтесь / де з LINQ та Lambda


457

У мене виникають проблеми із запитом, написаним LINQ та Lambda. Поки я отримую багато помилок, ось мій код:

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, тому не впевнений, чи правильний цей запит.


11
що ви намагаєтеся досягти?
Герман Родрігес

4
що ви хочете, щоб запит виконував у реченні?
мисливець

6
Ваші ключові селектори шлях дуже складний. Якщо ви хочете вибрати за id, просто x => x.ID - це добре.
Ерік Ліпперт

1
Я хотів отримати публікацію з бази даних та метаданих для цієї публікації.
Девід

Відповіді:


1055

Я вважаю, що якщо ви знайомі з синтаксисом SQL, використання синтаксису запитів LINQ набагато чіткіше, природніше і полегшує виявлення помилок:

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

Якщо ви дійсно зациклювалися на використанні лямбда, то ваш синтаксис зовсім небагато. Ось такий же запит, використовуючи методи розширення 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 щодо вашої редакції: "Рівність рівнів ідентифікаторів встановлена ​​в режимі JOIN; вам не потрібно використовувати пункт WHERE!": Пункт WHERE не перевіряє рівність між полями ідентифікатора, він перевіряє рівність між ідентифікатором повідомлення стовпця та параметр id, оголошений поза запитом.
Даніель Шаффер

9
Дивовижний твір lambdaі цитата проста у використанні та розумінні
Пьотр Кула,

1
приголомшливий приклад
іграшка

1
Іноді пояснення лямбда пишуться в лямбда. Пояснив добре.
Пінч

80

З цим можна піти двома шляхами. Використовуючи LINQPad (неоціненний, якщо ви новачок у LINQ) та манекену, я створив такі запити:

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

або

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

У цьому конкретному випадку я вважаю, що синтаксис LINQ є більш чистим (я змінююсь між двома залежно від того, яке простіше читати).

Хоча я хотів би зазначити, що якщо у вашій базі даних є відповідні зовнішні ключі (між поштою та post_meta), вам, мабуть, не потрібно явного приєднання, якщо ви не намагаєтеся завантажити велику кількість записів . Схоже, ваш приклад вказує на те, що ви намагаєтеся завантажити одну посаду і це метадані. Якщо припустити, що для кожної публікації існує багато записів post_meta, то можна зробити наступне:

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

Якщо ви хочете уникнути проблеми n + 1, тоді ви можете чітко сказати LINQ SQL, щоб завантажувати всі пов'язані елементи за один раз (хоча це може бути розширеною темою, коли ви більше знайомі з L2S). У наведеному нижче прикладі написано: "Коли ви завантажуєте повідомлення, також завантажуйте всі її записи, пов'язані з нею, за допомогою зовнішнього ключа, представленого властивістю" 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

Можна здійснити багато LoadWithдзвінків на одному наборі одного DataLoadOptionsі того ж типу або на багатьох різних типах. Якщо ви робите це безліч, ви можете просто розглянути можливість кешування.


1
LinqPad та CRM 2016 ?
Кікенет

49

Даніель добре пояснює синтаксичні стосунки, але я склав цей документ для своєї команди, щоб зробити їх трохи простішим для розуміння. Сподіваюся, що це комусь допоможевведіть тут опис зображення


Це не спрацює, коли ви просто маєте справу зі списком цінностей, таких як ми тут. На об’єкті немає властивості id.
Talspaugh27

Я вважав це дійсно корисним, але я отримав помилку, яка вимагала від мене додати стовпчик приєднання. Також дивлячись на відповідь, опубліковану @Mark Byers, стовпець приєднання має Post_IDполе у ​​другому псевдонімі meta => meta.Post_ID. У прикладі на цій ілюстрації g.idчастина оригінального оператора select JOIN gStatus g on g.idне повторюється в остаточному виразі лямбда.
SausageFingers

3
Я не намагався розмістити це як посилання на фактичний linkq, необхідний для відповіді, опублікований ОП, це було більше посиланням на те, як перемістити SQL у формат Linq, щоб мої входи трохи відрізнялися від початкового питання. Якби я створив клас для значень gStatus, я би поставив на нього властивість id і тоді так, він би приєднався до g => g.id Я використав список значень, щоб спробувати зберегти код максимально простим.
Talspaugh27

@ Talspaugh27 Так чому в SQL запиті він приєднується до gStatus на g.id? Це помилка чи навмисне?
Драммі

@Drammy в таблиці sql кожен стовпець повинен мати ім’я, тому оскільки це була таблиця з 1 стовпцем строго для зберігання цих ідентифікаторів, я просто використовував стовпець з ідентифікатором id, у списку <int> цього питання немає. Якби я встановив його як такий, public class IdHolder{ int id } то використовував би цей об'єкт у gStatus, List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); тоді він змінив би Linq, чи є t =>t.value.TaskStatusId, g=>g.id ця зміна сенсом?
Talspaugh27

37

Вибір ключів невірний. Вони повинні взяти об’єкт типу відповідної таблиці та повернути ключ для використання в з'єднанні. Я думаю, ти маєш на увазі це:

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

Ви можете застосувати після цього пункт де, а не як частина вибору ключа.


9

Публікація, тому що коли я запускав LINQ + EntityFramework, я цілий день дивився на ці приклади.

Якщо ви використовуєте EntityFramework і у вас встановлено властивість навігації, вказану Metaна Postоб'єкті моделі, це легко забруднити . Якщо ви використовуєте сутність і не маєте цього властивості навігації, що ви чекаєте?

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

Якщо ви робите код спочатку, ви встановите властивість таким чином:

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

5

Я зробив щось подібне;

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

Це могло бути щось на кшталт

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 дорівнює 1 приєднанню двох різних таблиць

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

Цей запит linq повинен працювати для вас. Він отримає всі повідомлення, які містять мета-повідомлення.

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

Еквівалентний запит SQL

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

ви закрили дужки, де після третьої парами ... "жодне перевантаження для Join бере три аргументи"
LastTribunal

3
Це ідентично прийнятої відповіді і через 7 років -1
reggaeguitar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.