C # GroupBy Trouble - PullRequest
       5

C # GroupBy Trouble

0 голосов
/ 04 июня 2019

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

У меня есть две таблицы базы данных, и одна таблица ассоциации.

Первая таблица

public partial class InitialTraining
{
    public InitialTraining()
    {
        InitialTrainingAssociations = new HashSet<InitialTrainingAssociation>();
    }
    public int Id { get; set; }

    [ForeignKey("MedicInfo")]
    public int TfoId { get; set; }

    [ForeignKey("InstructorInfo")]
    public int? InstructorId { get; set; }

    [ForeignKey("PilotInfo")]
    public int? PilotId { get; set; }

    public DateTime DateTakenInitial { get; set; }

    public decimal FlightTime { get; set; }

    public bool Active { get; set; }

    [StringLength(2000)]
    public string Narrative { get; set; }

    [Required]
    [StringLength(20)]
    public string TrainingType { get; set; }

    [ForeignKey("CodePhase")]
    public int PhaseId { get; set; }

    [ForeignKey("PhaseTrainingType")]
    public int PhaseTrainingTypeId { get; set; }
    public string EnteredBy { get; set; }
    public DateTime? EnteredDate { get; set; }

    public virtual MedicInfo MedicInfo { get; set; }
    public virtual MedicInfo InstructorInfo { get; set; }
    public virtual MedicInfo PilotInfo { get; set; }
    public virtual Code_Phase CodePhase { get; set; }
    public virtual Code_PhaseTrainingType PhaseTrainingType { get; set; }
    public virtual ICollection<InitialTrainingAssociation> InitialTrainingAssociations { get; set; }
}

Вторая таблица (таблица ассоциации)

public class InitialTrainingAssociation
{
    public int Id { get; set; }
    [ForeignKey("InitialTraining")]
    public int InitialTrainingId { get; set; }
    [ForeignKey("CodePerformanceAnchor")]
    public int? PerformanceAnchorId { get; set; }
    [ForeignKey("GradingSystem")]
    public int? GradingSystemId { get; set; }
    public virtual AviationMedicTraining.CodePerformanceAnchor CodePerformanceAnchor { get; set; }
    public virtual InitialTraining InitialTraining { get; set; }
    public virtual GradingSystem GradingSystem { get; set; }
}

Вот мой GroupByв C #.

// get list of initial training record ids for statistics
var lstInitialTrainings = db.InitialTrainings.Where(x => x.TfoId == medicId && x.Active).Select(x => x.Id).ToList();

// get list of initial training performance anchors associated with initial training records
var lstPerformanceAnchors = db.InitialTrainingAssociations
    .Where(x => lstInitialTrainings.Contains(x.InitialTrainingId)).GroupBy(t => t.PerformanceAnchorId)
    .Select(s => new MedicStatistic()
    {
        PerformanceAnchorName = db.CodePerformanceAnchor.FirstOrDefault(v => v.Id == s.Key).PerformanceAnchor,
        AnchorCount = s.Count()
    }).ToList();

Моя цель

Очевидно, из своего кода я хочу сгруппировать по якорю производительности в таблице ассоциации, но мне нужно большеинформацию из таблицы Initial Training для включения в мою ViewModel MedicStatistic, но у меня возникают проблемы с поиском наилучшего способа сделать это.

Моя общая цель - получить самые последние данныепривязка к производительности была завершена из таблицы Initial Training.

Visual

Таблица начальной подготовки (не все поля были захвачены во фрагменте, так как онине имеет значения для целей данного вопроса)

enter image description here

IniТаблица ассоциации тренировок

enter image description here


Что я ожидаю

Итак, отНа рисунках, представленных выше, как вы можете видеть, есть несколько 1 идентификаторов привязки производительности в таблице ассоциация , но каждый из них имеет различный InitialTrainingId.Таким образом, этот специфический якорь производительности был выполнен несколько раз, но мне нужно получить самую последнюю дату из таблицы Initial Training.Кроме того, мне нужно получить соответствующую оценку с привязкой из таблицы Grading System, основываясь на самой последней дате.

Итак, для привязки производительности, равной 1 .. Я хотел бы получить оценкуэто соответствует InitialTrainingId из 17, потому что эта запись была самой последней раз, когда была сделана привязка производительности 1.

Если у вас есть какие-либо вопросы, пожалуйста, дайте мне знать.

Ответы [ 3 ]

2 голосов
/ 06 июня 2019

Вы хотите, чтобы данные были сгруппированы по CodePerformanceAnchor, поэтому наиболее естественный способ запустить запрос - это DbSet, что немедленно устраняет необходимость группировки:

from pa in db.CodePerformanceAnchors
let mostRecentInitialTraining
    = pa.InitialTrainingAssociations
        .Select(ita => ita.InitialTraining)
        .OrderByDescending(tr => tr.DateTakenInitial)
        .FirstOrDefault()
select new 
{
    pa.PerformanceAnchor,
    mostRecentInitialTraining.DateTakenInitial,
    mostRecentInitialTraining. ...
    ...
    AnchorCount = pa.InitialTrainingAssociations.Count()
}

Как видите, используются только свойства навигации, и запрос в целом довольно прост. Я предполагаю, что класс PerformanceAchor также имеет коллекцию InitialTrainingAssociations.

Я не могу гарантировать, что EF сможет выполнить его полностью на стороне сервера, хотя это всегда сложно с более сложными запросами LINQ.

1 голос
/ 06 июня 2019

Я собираюсь игнорировать свойства virtual в вашем классе InitialTrainingAssociation, так как вы ничего о них не упомянули, и мне не сразу понятно, содержат ли они данные или почему они virtual.

Кажется, IQueryable.Join - это самый простой способ объединить нужные данные.

В следующем примере мы начнем с записей из таблицы InitialTrainings.Затем мы будем Join с таблицей InitialTrainingAssociations, что приведет к коллекции парных объектов InitialTraining и InitialTrainingAssociation.

var initialTrainingResults =
    // Start with the InitialTrainings data.
    db.InitialTrainings

    // Add association information.
    .Join(
        // The table we want to join with
        db.InitialTrainingAssociations,

        // Key selector for the outer type (the type of the collection
        // initiating the join, in this case InitialTraining)
        it => it.Id,

        // Key selector for the inner type (the type of the collection
        // being joined with, in this case InitialTrainingAssociation)
        ita => ita.InitialTrainingId,

        // Result selector. This defines how we store the joined data.
        // We store the results in an anonymous type, so that we can
        // use the intermediate data without having to declare a new class.
        (InitialTraining, InitialTrainingAssociation) =>
            new { InitialTraining, InitialTrainingAssociation }
    )

Отсюда мы можем добавлять данные из таблиц PerformanceAnchors и GradingSystems, выполняя большеJoin s.Каждый раз, когда мы выполняем Join, мы добавляем новую сущность к нашему анонимному типу.Результатом будет коллекция анонимных типов, представляющих данные, которые мы извлекли из базы данных.

    // Add performance anchor information.
    .Join(
        db.PerformanceAnchors,
        x => x.InitialTrainingAssociation.PerformanceAnchorId,
        pa => pa.Id,
        (x, PerformanceAnchor) =>
            new { x.InitialTrainingAssociation, x.InitialTraining, PerformanceAnchor }
    )

    // Add grading system information.
    .Join(
        db.GradingSystems,
        x => x.InitialTrainingAssociation.GradingSystemId,
        gs => gs.Id,
        // No need for InitialTrainingAssociation anymore, so we don't
        // include it in this final selector.
        (x, GradingSystem) =>
            new { x.InitialTraining, x.PerformanceAnchor, GradingSystem }
    );

(Это был подробный пример, показывающий, как вы можете объединить все таблицы вместе. Вы можете использовать меньшеПрисоединяется, если вам не нужен доступ ко всем данным одновременно, и вы можете отфильтровать коллекцию InitialTrainings, с которой мы начинаем, если вы знаете, что вам нужен только доступ к определенным частям данных.)

На этом этапе initialTrainingResults - это IEnumerable, содержащий одну запись для каждой ассоциации между таблицами InitialTrainings, PerformanceAnchors и GradingSystems.По сути, все, что мы сделали, взяли все InitialTrainingAssociations и расширили их идентификаторы до реальных объектов.

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

var performanceAnchors = initialTrainingResults
    // Group by each unique Performance Anchor. Remember, the IEnumerable
    // we are operating on contains our anonymous type of combined Training,
    // Performance Anchor and Grading data.
    .GroupBy(x => x.PerformanceAnchor.Id)

    // Order each Performance Anchor group by the dates of its training,
    // and take the first one from each group
    .Select(g => g.OrderByDescending(x => x.InitialTraining.DateTakenInitial).First());
0 голосов
/ 06 июня 2019

В Select вы можете упорядочить групповой результат, чтобы получить самую последнюю связанную InitialTraining на DateTakenInitial, и оттуда получить нужные данные

//...omitted for brevity
.GroupBy(t => t.PerformanceAnchorId)
.Select(g => {

    var mostRecent = g.OrderByDescending(_ => _.InitialTraining.DateTakenInitial).First();
    // get the corresponding grade with the anchor from the Grading System table
    var gradeid = mostRecent.GradingSystemId;
    var gradingSystem = mostRecent.GradingSystem;

    //get the most recent date from the Initial Training
    var mostRecentDate = mostRecent.InitialTraining.DateTakenInitial

    //..get the desired values and assign to view model

    var model = new MedicStatistic {
        //Already have access to CodePerformanceAnchor
        PerformanceAnchorName = mostRecent.CodePerformanceAnchor.PerformanceAnchor
        AnchorCount = g.Count(),
        MostRecentlyCompleted = mostRecentDate,
    };

    return model;
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...