#c# #linq #entity-framework-core
#c# #linq #сущность-фреймворк-ядро
Вопрос:
У меня довольно сложный запрос LINQ, но в этом суть вопроса
var result = await _context.TblBreakpoints
.GroupBy(b => new { b.BpgroupId, b.ResultType, b.DrugId, b.Susceptible, b.LowIntermediate, b.Intermediate, b.Resistant})
.Join(_context.TblBreakpointgroups,
bg => bg.Key.BpgroupId,
g => g.BpgroupId,
(bg, g) => new
{
GroupId = bg.Key.BpgroupId,
DrugId = bg.Key.DrugId,
Susceptible = bg.Key.Susceptible,
LowIntermediate = bg.Key.LowIntermediate,
Intermediate = bg.Key.Intermediate,
Method = bg.Key.ResultType,
Resistant = bg.Key.Resistant,
StandardId = g.BpstandardId,
GroupName = g.BpgroupName,
Count = bg.Count(),
})
.Join(_context.TblBreakpointStandards,
i => i.StandardId,
j => j.BpstandardId,
(i, j) => new
{
Standard = j.Bpstandard,
GroupId = i.GroupId,
GroupName = i.GroupName,
Count = i.Count,
Method = i.Method,
DrugId = i.DrugId,
Susceptible = i.Susceptible,
LowIntermediate = i.LowIntermediate,
Intermediate = i.Intermediate,
Resistant = i.Resistant
})
.Join(_context.TblDrugs,
i => i.DrugId,
j => j.DrugId,
(i, j) => new
{
DrugName = j.DrugName,
Standard = i.Standard,
GroupId = i.GroupId,
GroupName = i.GroupName,
Count = i.Count,
Method = i.Method,
Susceptible = i.Susceptible,
LowIntermediate = i.LowIntermediate,
Intermediate = i.Intermediate,
Resistant = i.Resistant
})
.Join(_context.TblBreakpointgroupmembers,
i => i.GroupId,
j => j.BpgroupId,
(i, j) => new
{
OrganismId = j.OrganismId,
Standard = i.Standard,
GroupId = i.GroupId,
GroupName = i.GroupName,
Count = i.Count,
Method = i.Method,
DrugName = i.DrugName,
Susceptible = i.Susceptible,
LowIntermediate = i.LowIntermediate,
Intermediate = i.Intermediate,
Resistant = i.Resistant
})
.Join(_context.TblOrganismNames,
i => i.OrganismId,
j => j.OrganismId,
(i, j) => new BreakpointSummary
{
OrganismName = j.OrganismName,
Standard = i.Standard,
GroupName = i.GroupName,
Count = i.Count,
Method = i.Method,
DrugName = i.DrugName,
Susceptible = i.Susceptible,
LowIntermediate = i.LowIntermediate,
Intermediate = i.Intermediate,
Resistant = i.Resistant
})
.ToListAsync().ConfigureAwait(false);
Из запроса с каждым соединением я продолжаю передавать предыдущие значения и добавлять значения, полученные из соединения. Это уже утомительно всего с 5 объединениями, это будет еще больше с большим количеством объединений. Есть ли лучший способ, которого мне не хватает?
Я думаю, что эквивалентный SQL
WITH bpg (BPGroupId, ResultType, DrugId, Susceptible, LowIntermediate, Intermediate, Resistant, Count)
AS (
SELECT BPGroupId, ResultType, DrugId, Susceptible, LowIntermediate, Intermediate, Resistant, COUNT(BPGroupId)
FROM dbo.tbl_Breakpoint a
GROUP BY BPGroupId,
ResultType,
DrugId,
Susceptible,
LowIntermediate,
Intermediate,
Resistant
)
SELECT a.BpgroupName, b.BPStandard, c.DrugName, e.OrganismName, CTE.ResultType, CTE.Susceptible, CTE.LowIntermediate, CTE.Intermediate, CTE.Resistant, CTE.Count
FROM dbo.tbl_breakpointgroup a
INNER JOIN bpg CTE ON a.BPGroupId = CTE.BPGroupId
INNER JOIN tbl_BreakpointStandard b ON b.BPStandardId = a.BPStandardId
INNER JOIN tbl_Drug c ON c.DrugID = CTE.DrugId
INNER JOIN tbl_breakpointgroupmember d ON d.BPGroupId = CTE.BPGroupId
INNER JOIN tbl_OrganismName e ON e.OrganismId = d.OrganismId
WHERE a.BPGroupId = CTE.BPGroupId
Комментарии:
1. Предпочтительный способ выполнения соединений в EF (Core) — не кодировать их вручную, а определять свойства навигации , а затем выполнять только конечную проекцию (
Select
) — необходимые соединения будут выполнены автоматически для вас.2. Проблема в том, что как только вы «группируетесь по», у вас больше нет сущности EF?
3. Что вы пытаетесь сделать? Все эти соединения создают довольно сложную настройку, которую, вероятно, следует упростить с точки зрения производительности. Вместо этого вы могли бы использовать EF
Include()
, но иногда лучше получать связанные данные отдельно. И почему вы не группируете в конце?
Ответ №1:
В общем, при использовании соединений вручную в LINQ лучше использовать синтаксис запроса, поскольку он обеспечивает прозрачность переменных диапазона (которые соответствуют псевдонимам таблиц / запросов в SQL). например (в псевдокоде)
from a in queryA
join b in queryB on ... // can use any field from a
join c in queryC on ... // can use any field from a and b
join d in queryD on ... // can use any field from a, b and c
...
select new
{
// can use any field for a, b, c, d etc.
}
То же самое с синтаксисом метода немного сложнее, но принцип заключается в том, чтобы обернуть предыдущие «переменные» в простой кортеж, подобный анонимным типам, пока вы не дойдете до окончательной проекции, например (в псевдокоде)
queryA
.Join(queryB, a => {a fields}, b => {b fields), (a, b) => new { a, b }) // first result
.Join(queryC, r => {r.a, r.b fields), c => { c fields }, (r, c) => new { r.a, r.b, c } // second result
.Join(queryD, r => {r.a, r.b, r.c fields), d => { d fields }, (r, d) => new { r.a, r.b, r.c, d } // third result
...
.Select(r => new { r.a, r.b, r.c, r.d... fields });
Применяя его к вашему примеру, соответствующий синтаксис запроса может быть таким (обратите внимание, что вложенные запросы внутри могут использовать любой подходящий синтаксис):
var query =
from cte in _context.TblBreakpoints
.GroupBy(b => new { b.BpgroupId, b.ResultType, b.DrugId, b.Susceptible, b.LowIntermediate, b.Intermediate, b.Resistant})
.Select(g => new
{
g.Key.BpgroupId, g.Key.ResultType, g.Key.DrugId, g.Key.Susceptible, h.Key.LowIntermediate, g.Key.Intermediate, g.Key.Resistant,
Count = g.Count(),
})
join a in _context.TblBreakpointgroups on cte.BpgroupId equals a.BpgroupId
join b in _context.TblBreakpointStandards on a.BpstandardId equals b.BpstandardId
join c in _context.TblDrugs on cte.DrugId equals c.DrugId
join d in _context.TblBreakpointgroupmembers on cte.BpgroupId equals d.BpgroupId
join e in _context.TblOrganismNames on d.OrganismId equals e.OrganismId
select new BreakpointSummary
{
OrganismName = e.OrganismName,
Standard = b.Bpstandard,
GroupName = a.BpgroupName,
Count = cte.Count,
Method = cte.ResultType,
DrugName = d.DrugName,
Susceptible = cte.Susceptible,
LowIntermediate = cte.LowIntermediate,
Intermediate = cte.Intermediate,
Resistant = cte.Resistant,
};
Вы можете преобразовать его в синтаксис метода, используя вышеупомянутые правила, но для меня это не стоит усилий.