#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
. Если вам нужен подпункт с его единственным родительским элементом, на который ссылается внешний ключ, используйте aJoin
.
Почему вы не должны использовать 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…..