Используя приведенные ниже таблицы, я хотел бы суммировать дозы для каждого участка, который проходит один и тот же курс. Я хочу сначала сгруппировать по курсу, затем по участкам, а затем суммировать дозы.
Радиация
Радиационные сайты
Выход
Я попробовал что-то вроде следующего, но не могу уложиться в этом.
var totals =
dbContext
.Radiations
.Include(r => r.RadiationSites)
.ThenInclude(s => s.Site)
.Include(r => r.RadiationCourse)
.Where(r => r.Nhi == NHI)
.SelectMany(s => s.RadiationSites, (Radiation, RadiationSites) => new { Radiation, RadiationSites } )
.GroupBy(r => r.Radiation.RadiationCourseId, r => r.RadiationSites.SiteId);
Возможно ли это вообще? Пожалуйста помоги!
Было бы полезно иметь код классов в вопросе. Кроме того, задавая вопросы об EF, важно знать точную версию, поскольку версии различаются по поддерживаемым формам запросов.





Если у вас есть таблица Sites со столбцами SiteId и SiteName и таблица Courses со столбцами CourseId и CourseName, ваш запрос будет выглядеть примерно так:
var totals = from rad in dbContext.Radiations
join radSite in dbContext.RadiationSites on rad.RadiationId equals radSite.RadiationId
join site in dbContext.Sites on radSite.SiteId equals site.SiteId
join course in dbContext.Courses on rad.CourseId equals course.CourseId
group new { rad, site.SiteName, course.CourseName } by new { rad.CourseId, radSite.SiteId } into grouped
select new {
CourseId = grouped.Key.CourseId,
SiteId = grouped.Key.SiteId,
TotalDose = grouped.Sum(x => x.rad.Dose),
CourseName = grouped.Select(x => x.CourseName).FirstOrDefault(),
SiteName = grouped.Select(x => x.SiteName).FirstOrDefault()
};
var result = totals.ToList();
Здесь
Не используйте соединения явно, если есть свойства навигации. Например, join radSite... можно заменить на from radSite in rad.RadiationSites, а также на другие соединения.
Вот ваши данные как действительный код C#:
var sites = new[]
{
new { SiteId = 1, Site = "Spleen", },
new { SiteId = 2, Site = "Spine", },
new { SiteId = 3, Site = "Eye", },
};
var courses = new[]
{
new { CourseId = 1, Course = "Course 1", },
new { CourseId = 2, Course = "Course 2", },
};
var radiations = new[]
{
new { RadiationId = 1, CourseId = 1, Dose = 10, },
new { RadiationId = 2, CourseId = 1, Dose = 20, },
new { RadiationId = 3, CourseId = 2, Dose = 5, },
};
var radiationSites = new[]
{
new { RadiationId = 1, SiteId = 1, },
new { RadiationId = 1, SiteId = 2, },
new { RadiationId = 1, SiteId = 3, },
new { RadiationId = 2, SiteId = 3, },
new { RadiationId = 3, SiteId = 1, },
};
Теперь ваш запрос выглядит так:
var query =
from course in courses
join radiation in radiations on course.CourseId equals radiation.CourseId
join radiationSite in radiationSites on radiation.RadiationId equals radiationSite.RadiationId
join site in sites on radiationSite.SiteId equals site.SiteId
group radiation.Dose by new
{
course.CourseId,
site.SiteId,
course.Course,
site.Site,
} into gs
select new
{
gs.Key.CourseId,
gs.Key.SiteId,
TotalDose = gs.Sum(),
gs.Key.Course,
gs.Key.Site,
};
Это производит:
Что мне не нравится в других ответах, так это то, что:
В любом случае, здесь есть несколько важных обучающих моментов. Одним из них является то, что ваша задача может быть значительно проще в зависимости от того, с чего вы начинаете. Давайте сделаем это с помощью EF и посмотрим, как это будет выглядеть.
Я воспроизвел вашу БД настолько, насколько смог понять ее из вашего поста:
Не имеет значения, если это не совпадает точно, но при написании кода полезно иметь диаграмму, чтобы облегчить навигацию. Не беспокойтесь о незначительных различиях, например, у вас нет RadiationSiteId.
Главный момент: ваша жизнь станет значительно проще, если вы начнете с RadiationSites; это конец двух отношений, и у него есть только родители. Находиться «в» дочернем объекте и переходить к родительскому объекту обычно намного проще, чем находиться в родительском объекте и выбирать одного из его многочисленных дочерних объектов.
Вам не нужно включать; Включить — это не JOIN. Вы можете прочитать об этом более объемные тома, но Include предназначен для тех случаев, когда вы загружаете целые объекты и хотите сообщить EF, какие еще целые связанные объекты следует добавить. Группирующие запросы, вероятно, не приводят к получению выходного объекта, который полностью является объектом схемы, а вместо этого используют какую-то другую проекцию, например. анонимный тип, поэтому пропустите Include. Вам не требуется Include, чтобы EF мог «видеть» связанные данные, к которым вы обращаетесь — когда вы используете свойства навигации в разделах «Where», «GroupBy», «Select» и т. д., EF увидит, что вы перемещаетесь от одного объекта к другому, и сформирует необходимые соединения для ты на заднем плане
var totals = await db.RadiationSites
.Where(rs => rs.Radiation.Nhi == nhi)
.GroupBy(rs => new { rs.Radiation.CourseId, rs.SiteId, rs.Radiation.Course.CourseName, rs.Site.SiteName })
.Select(g => new { g.Key.CourseId, g.Key.SiteId, TotalDose = g.Sum(rs => rs.Radiation.Dose), g.Key.CourseName, g.Key.SiteName })
.ToArrayAsync();
Мы начинаем с RadiationSites и Where, переходя к Radiation, чтобы настаивать на том, что NHI представляет собой некоторую ценность. EF увидит это и присоединится, как бы ему хотелось это сделать.
Поскольку мы находимся внизу в иерархии отношений (представляя это как генеалогическое древо, где дети находятся ниже родителей), группировка столь же проста; мы поднимаемся по дереву ровно настолько, насколько нам нужно. В некоторых базах данных вам может сойти с рук просто группировка по первичному ключу, и БД знает, что неявно все остальные столбцы из этой таблицы должны быть уникальными, но SQLS предпочитает группировать все столбцы, даже если, например, для идентификатора курса, равного 1, имя курса никогда не будет иметь другого значения. Поскольку нам нужно название курса, мы извлекаем его в ключ группировки, потому что позже это упростит жизнь. Мы можем добраться до CourseId, только поднявшись до Radiation.CourseId, но нам придется идти дальше, чтобы узнать имя - Radiation.Course.CourseName
В конечном счете, чтобы не сломать себе голову, нужно посмотреть на желаемый результат; вам нужны CourseId, SiteId, CourseName и SiteName в качестве не сгруппированных (ключевых) столбцов, а сумма дозы — в виде агрегированного столбца. Поместите все эти ключевые столбцы в объект, который вы группируете: new { rs.Radiation.CourseId, rs.SiteId, rs.Radiation.Course.CourseName, rs.Site.SiteName }. Если вы хотите упростить некоторые вещи, вы всегда можете сгруппировать их только по идентификаторам, т. е. { rs.Radiation.CourseId, rs.SiteId }, и получить соответствующие данные позже, возможно, даже с помощью отдельного запроса.
Затем есть Select, где вы снова извлекаете значения ключей (повторяющиеся побитовые значения), но помогаете себе, называя входную переменную Select g или аналогичной, чтобы отразить тот факт, что это группа (набор коллекции RadiationSites) с .Key, указывающим, к чему были сгруппированы все сайты в нем, и сам является коллекцией RadationSites. Вот почему я использую g внутри Select, а rs внутри Sum, потому что сумма пропускает «группу» RadiationSites и суммирует Dose.
И вот оно:
Идеальный ответ и фантастические учения, сопровождающие его. Я подумаю об отправной точке в дальнейшем и дам отличный совет по включению. Большое спасибо!
Как вы могли понять из полученных ответов, выполнять соединения с использованием синтаксиса понимания запросов зачастую проще, чем использовать функциональный синтаксис. Каждый синтаксис имеет свои преимущества. Не волнуйтесь, что у вас есть преимущество во времени выполнения.