как получить подмножество подмножества в asp core entityframwork и перейти к viewmodel

#asp.net-mvc #linq #asp.net-core #.net-core #entity-framework-core

Вопрос:

У меня есть три таблицы, которые связаны друг с другом. Я искал методы groupBy в entityframwork, но не получил правильного ответа. это таблицы:

 public class DayProgramOffice
{
    public long Id { get; set; }
    public DateTime Day { get; set; }
    public virtual ICollection<ScheduleOffice> ScheduleOffices { get; set; }

}
public class ScheduleOffice
{
    public long Id { get; set; }
    public DateTime Time { get; set; }

    public virtual DayProgramOffice DayProgramOffice { get; set; }
    public long DayProgramOfficeId { get; set; }

    public virtual TypeConsultaion TypeConsultaion { get; set; }
    public long TypeConsultaionId { get; set; }

}

public class TypeConsultaion
{
    public byte Id { get; set; }
    public string TypeName { get; set; }
    public virtual ICollection<ScheduleOffice> ScheduleOffices { get; set; }
}
 

и у вас есть три модели просмотра, которые хотят отправлять в нее данные:

 public class Day
{
    public DateTime day { get; set; }
    public IList<TypeConViewModel> Types{ get; set; }
}

public class TypeConViewModel
{
    public string TypeName { get; set; }
    public IList<HoverViewModel> Hovers { get; set; }

}

public class HoverViewModel
{
    public DateTime Hover { get; set; }
}
 

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

 @foreach (var item1 in Model.Days)
{

    <p>@item1.Day</p>
    foreach (var item2 in item1.Types)
    {

        <p>@item2.TypeName</p>

        foreach (var item3 in item2.Hover)
        {
            <p>@item3.hover</p>
        }
    }

}
 

Я использую этот код, но не могу сгруппировать данные:

 _db.DayProgramOffice
    .Include(c=>c.ScheduleOffice)
    .ThenInclude(d=>d.TypeConsultaion)
    .ToList():
 

Комментарии:

1. можете ли вы опубликовать то, что вы пробовали для GroupBy?

2. нет, я удаляю коды. но я пишу много кода для этого

3. Между предложением 1 и предложением 2 должно быть объяснение того, чего вы пытаетесь достичь. Тем не менее, в ядре EF группировка объектов должна выполняться на стороне клиента (т. Е. после ToList или, лучше, AsEnumerable ).

4. Какое свойство вы хотите сгруппировать? Судя по вашим таблицам и моделям представления , это, по-видимому, не связано с группой, но сопоставляет данные таблицы с моделями представления. Пожалуйста, поделитесь с нами отношениями на карте между таблицами и моделями представлений.

Ответ №1:

группируйтесь таким образом, но вы можете повысить производительность запросов с помощью изменения структуры таблиц!

Контроллер или Действие :

     public IActionResult Index()
    { 
        var daysModel = _db.ScheduleOffices
                             .Include(a => a.DayProgramOffice)
                             .Include(b => b.TypeConsultaion)
                             .ToList()
                             .GroupBy(d => d.DayProgramOffice)
                             .Select(d => new Day
                             { 
                                day = d.Key.Day,
                                Types = d.GroupBy(x => x.TypeConsultaion).Select(f => new TypeConViewModel { 
                                    TypeName = f.Key.TypeName,
                                    Hovers = f.Select(g => new HoverViewModel { 
                                        Hover = g.Time
                                    }).ToList()
                                }).ToList()
                             })
                             .ToList();
        
        return View(daysModel);
    }
 

И в ваших представлениях у вас есть какая-то ошибка, измените ее на эту :

 @foreach (var item1 in Model){
   <p class="day">@item1.day</p>
   foreach (var item2 in item1.Types){
    <p class="type">@item2.TypeName</p>
    foreach (var item3 in item2.Hovers)
    {

        <p class="hover">@item3.Hover</p>
    }
}
 

}

конечный результат

Комментарии:

1. Ой, а модель-это @список моделей<День>

2. структуры таблиц! как улучшить таблицы??

3. например, вам не нужна эта таблица «DayProgramOffice». после удаления conver .GroupBy(d => d.DayProgramOffice) к . Группировка(d => d.Время.Дата). и вот так.

4. Дневной офис должен переместиться за другой стол//

5. ToList() все записано, а затем группировка не замедляет скорость загрузки?

Ответ №2:

Итак , у вас есть стол с DayProgramOffices , и стол с ScheduleOffices . Существует соотношение «один ко многим» между DayProgramOffices и ScheduleOffices: у каждого DayProgramOffice есть ноль или более scheduleoffice, каждый ScheduleOffice принадлежит ровно одному DayProgramOffice, а именно к DayProgramOffice, на который ссылается внешний ключ.

Аналогично, существует однозначная связь между TypeConsultations и ScheduleOffices : у каждой ТипоКонсультации есть ноль или более функций расписания, каждое расписание принадлежит ровно одному типу, а именно тому, на который ссылается внешний ключ.

Увы, вы дали нам какой-то код, который не делает то, что вы хотите, и вы забыли точно указать, чего вы хотите. Однако из вашего кода я думаю, что вам нужно следующее:

Требование: С каждого дня Programoffice предоставляет мне все свои ноль или более запланированных приложений вместе с настройками типа этого запланированного приложения.

Простой способ: используйте виртуальную коллекцию ICollection

При объединении таблиц с помощью entity framework наиболее простым решением обычно является использование виртуальных коллекций. Entity Framework знает ваши отношения и может перевести это в соответствующие (групповые)объединения.

 using (var dbContext = new OfficeDbContext(...))
{
    var dayProgramOffices = dbContext.DayProgramOffices.Select(dayProgramOffice => new
    {
        // Select the DayProgramOffice properties that you plan to use:
        Id = dayProgramOffice.Id,
        Day = dayProgramOffice.Day,
        ...

        // Get all ScheduledOffices of this dayProgramOffice
        ScheduledOffices = dayProgramOffice.ScheduledOffices
            .Select(scheduleOffice => new
            {
                // Select only the properties that you plan to use:
                Id = scheduledOffice.Id,
                ...

                // Don't select the foreign key, you already know the value
                // DayProgramOfficeId = scheduledOffice.DayProgramOfficeId,

                // This ScheduledOffice has exactly one TypeConsultation:
                TypeConsultation = new
                {
                    Id = scheduledOffice.TypeConsultation.Id,
                    TypeName = scheduledOffice.TypeConsultation.TypeName,
                    ...
                }
             })
             .ToList(),
        });
}
 

Я решил создать анонимные типы ( new {...} ), чтобы предотвратить передачу значений, которые не используются системой управления базами данных, в ваш локальный процесс. Это также даст вам возможность добавлять свойства, которых нет в таблицах, например, в самое раннее время, когда был запланирован офис:

 DayProgramOffices.Select(dayProgramOffice => new
{
    ...
    NewestScheduledOfficeTime = dayProgramOffice.ScheduledOffices
        .Select(scheduledOffice => scheduledOffice.Time)
        .Min(),
 

Конечно, если вы хотите, вы можете указать типы, в которые вы хотите поместить полученные данные.

Альтернатива: используйте внешние ключи

 var dayProgramOffices = dbContext.DayProgramOffices.Select(dayProgramOffice => new
{
    Id = dayProgramOffice.Id,
    Day = dayProgramOffice.Day,
    ...

    // Get all ScheduledOffices of this dayProgramOffice using the foreign key
    ScheduledOffices = dbContext.ScheduledOffices
        .Where(scheduledOffice.DayProgramOfficeId == dayProgramOffice.Id)
        .Select(scheduleOffice => new
        {
            Id = scheduledOffice.Id,
            ...

            // fetch the TypeConsultation of this ScheduledOffice using the foreign key
            TypeConsultation = dbContext.TypeConsultations
                .Where(typeConsultation => typeConsulation.Id == scheduledOffice.TypeConfulstionId)
                .Select(typeConsulation => new
                {
                    Id = typeConsultation.Id,
                    TypeName = typeConsultation.TypeName,
                    ...
                })
                // expect exactly one suche TypeConsultation:
                .SingleOrDefault(),
         })
         .ToList(),
    });
 

Возможно, ваша версия entity framework этого не позволяет SingleOrDefault . Конечно, вы тоже можете использовать FirstOrDefault . Это, вероятно, даже более эффективно, потому что нет проверки уникальности.

Трудный путь: присоединяйтесь (к группе)сами

Entity framework преобразует выражения в предыдущем методе в (Групповые)объединения. Конечно, вы можете сделать эти групповые соединения самостоятельно:

 var dayProgramOffices = dbContext.DayProgramOffices.GroupJoin(
    dbContext.ScheduledOffices,

    dayProgramOffice => dayProgramOffice.Id,               // take the primary key
    scheduledOffice => scheduledOffice.DayProgramOfficeId, // take the foreign key

    // parameter resultSelector: for every dayProgramOffice and all zero or more
    // scheduledOffices that have a foreign key that refers to this dayProgramOffice
    // make one new:
    (dayProgramOffice, scheduledOffices) => new
    {
        Id = dayProgramOffice.Id,
        Day = dayProgramOffice.Day,

        // To get the one and only TypeConsultaition of each ScheduledOffice do a Join
        ScheduledOffices = scheduledOffices.Join(
            dbContext.TypeConsultations,

            scheduledOffice => scheduledOffice.TypeConsultationId, // foreign key
            typeConsultation => typeConsultation.Id,               // primary key

            // parameter resultSelector: for every scheduledOffice, with its one
            // and only typeConsultation, make one new:
            (scheduledOffice, typeConsultation) => new
            {
                Id = scheduledOffice.Id,
                ...

                TypeConsultation = new
                {
                    Id = typeConsultation.Id,
                    TypeName = typeConsultation.TypeName,
                    ...
                }),
        })
        .ToList(),
   });
 

Если у вас есть отношение «один ко многим», и вам нужны «элементы с их подпунктами», начните с «одной» стороны и используйте a GroupJoin . Если вам нужен подпункт с его единственным родительским элементом, на который ссылается внешний ключ, используйте a Join .

Почему вы не должны использовать Include

В вашем Dbcontext есть ChangeTracker . Если вы запросите объект без использования Select , то будет передана полная строка таблицы.

Кроме того, это может привести к передаче свойств, которые вы не планировали использовать, объект также помещается в средство отслеживания изменений вместе с копией объекта. Вы получаете ссылку на копию (или, может быть, оригинал, не имеет значения, теория одна и та же). Всякий раз, когда вы вносите изменения в выбранный объект, на самом деле вы меняете копию в трекере изменений.

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

Если вы не планируете изменять выбранный объект, это будет пустой тратой вычислительной мощности.

При извлечении данных из базы данных с помощью entity framework всегда используйте Select и выбирайте только те свойства, которые вы действительно планируете использовать. Извлекайте только полный объект и используйте Include / только ThenInclude в том случае, если вы планируете изменить извлеченные данные.

Комментарии:

1. спасибо, но я хочу показать данные в таком виде: Дата, время /////////////////////// пример: 2019/5/12 тип1 15:00 17:00 тип2 8:00 2019/6/18…..