#c# #sql #linq #.net-core #ef-core-2.1
#c# #sql #linq #.net-core #ef-core-2.1
Вопрос:
У меня есть таблица «Ссылка» и таблица «Статья», где статья ссылается на другие статьи.
У меня есть простые ссылки, такие как: 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».
Я предполагаю, что я должен включить / затем включить / присоединиться / выбрать / выделить много (?) коллекции перед разделом «новая ссылка», но я понятия не имею.
Есть ли какой-либо способ, которым я могу это заархивировать?
Комментарии:
1. Не могли бы вы, пожалуйста, добавить примеры данных в статьи и справочные таблицы?
Ответ №1:
Ну, вы можете сделать это точно так же, как в 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. Большое спасибо, это помогло мне. .SelectMany() является ли синтаксический эквивалент на основе метода «from» в синтаксисе на основе запроса, верно?
2. Действительно. В основном сглаживание на несколько вложенных
SelectMany
и проецирование пары (верхняя, нижняя).3. Спасибо даже за добавление синтаксиса на основе метода. Идеальный ответ!
Ответ №2:
Я смоделировал базу данных как классы, чтобы получить правильный синтаксис. Смотрите код ниже :
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; }
}
}