Присоединяйтесь/Где с помощью LINQ и Lambda

#c# #linq

Вопрос:

У меня возникли проблемы с запросом, написанным на 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, поэтому не уверен, что этот запрос правильный.

Комментарии:

1. чего вы пытаетесь достичь?

2. что вы хотите, чтобы запрос содержался в предложении?

3. Ваши ключевые селекторы слишком сложны. Если вы хотите выбрать по идентификатору, просто x=>x.ID все в порядке.

4. Я хотел получить сообщение из базы данных и метаданные для этого сообщения.

Ответ №1:

Я нахожу, что если вы знакомы с синтаксисом 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
 

Комментарии:

1. @Emanuele Greco, что касается вашего редактирования, «Равенство полей идентификаторов установлено в условии СОЕДИНЕНИЯ; вам не нужно использовать предложение WHERE!»: предложение WHERE не проверяет равенство полей идентификаторов, оно проверяет равенство между столбцом идентификатора записи и параметром идентификатора, объявленным вне запроса.

2. Потрясающая часть lambda и является цитатой, простой в использовании и понимании

3. Иногда объяснения лямбды написаны на лямбде. Хорошо объяснено.

4. я продолжаю пытаться использовать .включает, но это заводит меня только до сих пор. «из сообщения в базе данных. Сообщения» метод-это то, что я должен помнить. У меня также есть DapperDb, введенный в мои контроллеры для сложных запросов, но для простых соединений я использую запись из базы данных. Публикует пример. Это действительно удобно. Если у меня возникнут какие-либо проблемы с производительностью, я преобразую запрос в Dapper

Ответ №2:

С этим можно пойти двумя путями. Используя 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 и post_meta), то вам, вероятно, не нужно явное объединение, если вы не пытаетесь загрузить большое количество записей. Ваш пример, похоже, указывает на то, что вы пытаетесь загрузить одно сообщение и его метаданные. Предполагая, что для каждой записи существует много записей post_meta, вы можете сделать следующее:

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

Если вы хотите избежать проблемы n 1, вы можете явно указать LINQ to 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 ?

Ответ №3:

У Дэниела есть хорошее объяснение синтаксических взаимосвязей, но я собрал этот документ для своей команды, чтобы им было немного проще понять. Надеюсь, это кому-нибудь поможетвведите описание изображения здесь

Комментарии:

1. Это не сработает, когда вы просто имеете дело со списком ценностей, как мы здесь. У объекта нет свойства id.

2. Я нашел это действительно полезным, но я получил ошибку, из-за которой мне пришлось добавить столбец соединения. Также, глядя на ответ, опубликованный @Mark Byers, в столбце «Присоединение» есть Post_ID поле во втором псевдониме meta => meta.Post_ID . В примере на этой иллюстрации g.id часть исходного оператора select JOIN gStatus g on g.id не реплицируется в конечном лямбда-выражении.

3. Я не пытался опубликовать это как ссылку на фактический linq, необходимый для ответа, опубликованный OP, это была скорее ссылка на то, как перевести SQL в формат Linq, поэтому мои вводные данные немного отличались от исходного вопроса. Если бы я создал класс для значений gStatus, я бы поместил в него свойство id, а затем да, оно бы соединилось с g => g.id Я использовал список значений, чтобы попытаться сделать код как можно более простым.

4. @Talspaugh27 Так почему в SQL-запросе он присоединяется к gStatus на g.id? Это ошибка или преднамеренная?

5. @Drammy в таблице sql каждый столбец должен иметь имя, поэтому, поскольку это была таблица с 1 столбцом, в которой строго содержались эти идентификаторы, я просто использовал столбец с именем id, в списке<int> этой проблемы нет. Если бы я настроил его как таковой public class IdHolder{ int id } , а затем использовал этот объект в статусе, 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 , имеет ли смысл это изменение?

Ответ №4:

Ваши ключевые селекторы неверны. Они должны взять объект типа рассматриваемой таблицы и вернуть ключ для использования в соединении. Я думаю, ты имеешь в виду это:

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

Вы можете применить предложение where впоследствии, а не как часть селектора ключей.

Ответ №5:

Публикация, потому что, когда я начал LINQ EntityFramework, я целый день смотрел на эти примеры.

Если вы используете EntityFramework и у вас есть свойство навигации, указанное Meta в настройке Post объекта модели, это очень просто. Если вы используете entity и у вас нет этого свойства навигации, чего вы ждете?

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

Ответ №6:

Я делал что-то подобное;

 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" amp;amp;
        o.INDIVIDUALLICENSE.LICENSE_TYPE == "ABC")
    .Select(t => new
        {
            value = t.PSP_INDLICENSECLAsse.ID,
            name = t.PSP_INDIVIDUALLICENSE.LICENSE_CLASS,                
        })
    .OrderBy(x => x.name);
 

Ответ №7:

Это может быть что — то вроде

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

Ответ №8:

Этот запрос 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
 

Комментарии:

1. вы закрыли скобки, где после третьего параметра…»для объединения не требуется перегрузка с тремя аргументами».

2. Это совпадает с принятым ответом и 7 лет спустя -1

Ответ №9:

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

Ответ №10:

Синтаксис запроса для соединения LINQ

 var productOrderQuery = from product in Product.Setup()//outer sequence
                        join order in OrderDetails.Setup()//inner sequence
                        on product.Id equals order.ProductId //key selector
                        select new//result selector
                        {
                            OrderId = order.Id,
                            ProductId = product.Id,
                            PurchaseDate = order.PurchaseDate,
                            ProductName = product.Name,
                            ProductPrice = product.Price
                        };
 

Синтаксис метода для соединения LINQ

 var productOrderMethod = Product.Setup().//outer sequence
    Join(OrderDetails.Setup(), //inner sequence
    product => product.Id//key selector
    ,order=> order.ProductId //key selector
    ,(product,order)=> //projection result
        new
        {
            OrderId = order.Id,
            ProductId = product.Id,
            PurchaseDate = order.PurchaseDate,
            ProductName = product.Name,
            ProductPrice = product.Price
        }
    );
 

Product.cs для справки

 class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    public decimal Price { get; set; }
    public static IEnumerable<Product> Setup()
    {
        return new List<Product>()
        {
            new Product(){Id=1, Name="Bike", Price=30.33M },
            new Product(){Id=2, Name="Car", Price=50.33M },
            new Product(){Id=3, Name="Bus", Price=60.33M }
        };
    }
}
 

Класс OrderDetails.cs для справки

 class OrderDetails
{
    public int Id { get; set; }
    public virtual int ProductId { get; set; }

    public DateTime PurchaseDate { get; set; }
    public static IEnumerable<OrderDetails> Setup()
    {
        return new List<OrderDetails>()
        {
            new OrderDetails(){Id=1, ProductId=1, PurchaseDate= DateTime.Now },
            new OrderDetails(){Id=2, ProductId=1, PurchaseDate=DateTime.Now.AddDays(-1) },
            new OrderDetails(){Id=3, ProductId=2, PurchaseDate=DateTime.Now.AddDays(-2) }
        };
    }

}