В моей контекстной модели есть пара связанных таблиц:
CREATE TABLE "CarSystem"."Reads" (
"ReadId" UUID PRIMARY KEY,
. . .
);
CREATE TABLE "CarSystem"."Alarms" (
"AlarmId" UUID PRIMARY KEY DEFAULT UUID_GENERATE_V4(),
"ReadId" UUID NOT NULL REFERENCES "CarSystem"."Reads" ( "ReadId" ),
. . .
);
Есть и другие столбцы, но они не важны. Между таблицей Reads и таблицей Alarms существует связь от нуля до многих. В таблице Alarms может быть любое количество строк для каждой строки в таблице Reads, от нуля до.
У меня есть модель Entity Framework для этой структуры базы данных. У меня также есть объект ViewModel для строк в таблице Reads. Я хочу, чтобы объект ViewModel имел логическое свойство с именем HasAlarms. Это свойство должно иметь значение true, если в таблице Alarms есть хотя бы одна строка для строки в таблице Reads.
У меня есть функция на уровне доступа к данным, которая должна возвращать массив объектов Read ViewModel, которые соответствуют набору критериев. Я не уверен, как построить запрос, чтобы установить свойство HasAlarms. Я хочу зайти в базу данных только один раз, и мне нужна одна запись в массиве для каждой строки в таблице Reads.
Сейчас я делаю два запроса к базе данных, один для получения всех Чтений, а другой для получения всех Тревог:
IQueryable<Read> query = from read in context.Reads
where SomeCondition
select read;
Alarm[] alarms = ( from read in query
join alarm in context.Alarms on read.Readid equals alarm.ReadId
select alarm ).ToArray();
ReadViewModel[] result = ( from read in query
select new ReadViewModel {
ReadId = read.ReadId,
. . .
HasAlarms = alarms.Where( a => a.ReadId == read.ReadId ).Any(),
. . .
} ).ToArray();
Это работает, но неэффективно, потому что я дважды нажимаю на базу данных, один раз, чтобы получить строки в таблице Reads, и один раз, чтобы получить Alarms.
Есть ли способ создать этот запрос, чтобы он попадал в базу данных только один раз?
Tony
@ Крейг Штунц:
Я пытаюсь ускорить загрузку данных в мое приложение в ответ на запрос отчета. Это приложение WPF, и мне нужно, чтобы данные загружались как можно быстрее, чтобы улучшить взаимодействие с пользователем. Важно совершить как можно меньше поездок в базу данных, иначе приложение будет сканироваться.
Объект ReadViewModel состоит из свойств, которые сопоставляются со столбцами в таблице, а также с парой вложенных объектов, которые либо хранятся в виде столбцов в одной и той же таблице, либо в виде строк в нескольких связанных таблицах. Это сложная структура, и Entity Framework жалуется, если я пытаюсь создать объекты View Model с одним IQueryable. То есть что-то вроде:
ReadViewModel[] reads = ( from read in context.Reads
join alarm in context.Alarms on read.ReadId equals alarm.ReadId
where SomeCondition
select new ReadViewModel { ... } ).ToArray();
выдает исключение, в котором говорится, что Entity Framework не может создать константу одного из вложенных типов.
Если я извлекаю данные в массивы объектов Entity, а затем создаю объекты View Model с помощью Linq, все работает. Но затем мне нужно совершить более одной поездки в базу данных, чтобы получить все. Однако если я получу все данные с помощью этих запросов, это будет намного быстрее, чем позволить Entity Framework вернуться и запросить дополнительные данные для каждой строки, что и происходит, когда я делаю это так, как вы рекомендуете. Существует также сложность необходимости сделать левое внешнее соединение с таблицей Alarms, чтобы все испортить.
Я обнаружил, что если я сначала получу массивы объектов сущности, вместо того, чтобы совершать одну поездку для каждой строки в дополнение к первоначальной поездке, я могу сделать в общей сложности 2 или 3 поездки. Это гораздо быстрее, чем совершать n + 1 или n + 2 поездки. Я просто пытался понять, есть ли способ заставить Entity Framework сделать все это за один вызов базы данных, что я мог бы легко сделать, если бы сам писал SQL. Мне просто нужно добавить столбец, который возвращает 0 или 1 для HasAlarms, если в таблице Alarms есть хотя бы одна строка для чтения.