Упростите многие соединения LINQ

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

Вы можете преобразовать его в синтаксис метода, используя вышеупомянутые правила, но для меня это не стоит усилий.