EF Core: LINQ Выберите «многие ко многим ко многим» к «многим ко многим» - PullRequest
4 голосов
/ 01 апреля 2019

У меня есть таблица «Ссылка» и таблица «Статья», где статья ссылается на другие статьи.

У меня есть простые ссылки, такие как: A -> B

SQL:

select ab.*
from Article a
inner join Reference ab on ab.ArticleFromId = a.Id
inner join Article b on b.Id = ab.ArticleToId
where a.ArticleNo = "1234"

C # LINQ:

_context.Reference
   .Where(r => r.ArticleFromNavigation.ArticleNo.Equals("1234"));

У меня также есть цепочки ссылок, такие как: A -> B -> C (допустим, что в цепочке не более 3 статей)

SQL:

select ab.ArticleFromId, bc.ArticleToId
from Article a
inner join Reference ab on ab.ArticleFromId = a.Id
inner join Article b on b.Id = ab.ArticleToId
inner join Reference bc on bc.ArticleFromId = b.Id
inner join Article c on c.Id = bc.ArticleToId
where a.ArticleNo = "1234"

Это легко сделать в SQL, поскольку результат просто умножается на дополнительные объединения, но я не знаю, как записать это в LINQ.

Я хочу, чтобы это было что-то вроде этого (что не будет работать):

_context.Reference
   .Where(r => r.ArticleFromNavigation.ArticleNo.Equals("1234"))
   .Select(r => new Reference
   {
       ArticleFromNavigation = r.ArticleFromNavigation, //this is article "A"
       ArticleToNavigation = r.ArticleToNavigation.ReferenceArticleToNavigations //this wont work as it's a iCollection
   }).AsNoTrackable();

Здесь я хочу новые результаты типа "Ссылка" для "A -> C".Я предполагаю, что мне нужно включить / затем включить / присоединиться / выбрать / выбрать / выбрать коллекцию перед разделом "Новая ссылка", но я понятия не имею.

Есть ли способ архивировать это?

Ответы [ 2 ]

3 голосов
/ 01 апреля 2019

Ну, вы можете сделать это точно так же, как в SQL, но использовать свойства навигации вместо соединений.

Я буду использовать синтаксис запросов LINQ, потому что он лучше показывает сходство, а также синтаксис метода довольно сложен и труден для чтения для запросов такого типа:

from a in _context.Article
from ab in a.ReferenceArticleFromNavigations
let b = ab.ArticleToNavigation
from bc in b.ReferenceArticleFromNavigations
let c = bc.ArticleToNavigation
where a.ArticleNo = "1234"
select new Reference
{
    ArticleFromNavigation = a,
    ArticleToNavigation = c,
}

Операторы let не являются строго необходимыми (вы можете использовать свойство ссылочной навигации напрямую), я включил их просто для того, чтобы сделать запрос LINQ ближе к запросу SQL.

На самом деле эквивалент метода не так уж и плох в этом случае - выровнять несколько уровней с помощью вложенного SelectMany и спроецировать (верхнюю, нижнюю) пару, используя перегрузку SelectMany, позволяющую:

_context.Article
    .Where(a => a.ArticleNo = "1234")
    .SelectMany(a => a.ReferenceArticleFromNavigations
        .SelectMany(ab => ab.ArticleToNavigation.ReferenceArticleFromNavigations)
        // include as many `SelectMany` like the above as you wish until you hit the desired level of nesting
        .Select(bc => bc.ArticleToNavigation),
    (a, c) => new Reference
    {
        ArticleFromNavigation = a,
        ArticleToNavigation = c,
    });
1 голос
/ 01 апреля 2019

Я смоделировал базу данных как классы, чтобы получить правильный синтаксис. Смотрите код ниже:

using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication107
{
    class Program
    {
        static void Main(string[] args)
        {
            Context _context = new Context();
            string ArticleNo = "1234";

            var results = (from a in _context.article.Where(x => x.Id == ArticleNo)
                           join ab in _context.reference
                              .Where(x => (x.ArticleFromId == x.ArticleToId))
                              on a.Id equals ab.ArticleFromId
                           select new { a = a, ab = ab }
                          ).Select(r => new Reference()
                          {
                              ArticleFromNavigation = r.a,
                              ArticleToNavigation = r.a.ReferenceArticleToNavigations.ToList() 
                          }).ToList();
        }
    }

    public class Context
    {
        public List<Reference> reference { get; set; }
        public List<Article> article { get; set; }
    }
    public class Reference
    {
        public string ArticleFromId { get; set; }
        public string ArticleToId { get; set; }
        public Article ArticleFromNavigation { get; set; }
        public List<string> ArticleToNavigation { get; set; }
    }
    public class Article
    {
        public string Id { get; set; }
        public List<string> ReferenceArticleToNavigations { get; set; }
    }

}
...