EF4: отфильтровывать ссылочные объекты, которые не существуют - PullRequest
2 голосов
/ 08 декабря 2011

У меня есть дизайн Entity Framework 4, который позволяет удалять ссылочные таблицы (без каскадного удаления) без изменения объектов, указывающих на них.Так, например, сущность A имеет ссылку внешнего ключа на сущность B в поле идентификатора.B можно удалить (и в базе данных нет ограничений FK, чтобы это остановить), поэтому, если я смотрю на ABID, это всегда допустимое поле (поскольку все, что он делает - возвращает поле идентификатора в A), даже если записи нетB с этим идентификатором из-за предыдущего удаления.Это сделано специально, я не хочу каскадного удаления, мне нужно, чтобы записи А оставались на некоторое время для целей аудита.

Проблема в том, что отфильтровать несуществующие удаленные записи не так простокак это звучит.Так, например, если я сделаю это:

from c in A
select A.B.somefield;

Это приведет к ВНЕШНЕМУ СОЕДИНЕНИЮ в сгенерированном SQL, поэтому он забирает все записи А, даже если они ссылаются на отсутствующие записи В.Итак, хак, который я использовал, чтобы решить эту проблему (поскольку я не могу найти лучший способ!), Это добавить условие where для проверки строкового поля в ссылочных B-записях.Если это поле в сущности B пустое, то я предполагаю, что B. не существует.

from c in A
where c.B.somestringfield != null
select A.B.somefield;

, кажется, работает, если B.somestringfield - строка.Если это целое число, это не сработает!

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

  1. Запросите все таблицы, которые ссылаются на B, когда B удален, и обнулите их внешние ключи.Это так уродливо, что я не хочу вспоминать об этом, если я добавлю другую сущность, которая ссылается на B в будущем.Не говоря уже об огромной задержке производительности, разрешающей все ссылки всякий раз, когда я что-то удаляю.
  2. Добавьте строковое поле в каждую таблицу, на которую я могу рассчитывать, чтобы быть там, чтобы проверить, существует ли сущность.Blech, я не хочу добавлять поле базы данных только для этого.
  3. Реализация мягкого удаления и сохранение всей ссылочной целостности без изменений - по сути, установка каскадного удаления, но это приведет к огромному раздутию базы данныхтак как я не могу очистить огромное количество записей из-за ссылок.Нет.

Я думал, что эту проблему вылизали с помощью трюка «проверить, является ли поле в ссылочной сущности нулевым», но он ломается в условиях, которые я до конца не понимаю (что, если янет строк в ссылочной таблице? Какие типы полей будут работать? Целые числа не будут.)

Например, если у меня есть целое поле "count" в сущности B, и я проверяю, чтобы увидетьесли оно равно нулю:

from c in A
where c.B.count != null
select c.B.count;

Я получаю кучу записей с нулем для подсчета, смешанным с результатами, и на самом деле запрос бомбардирует с "InvalidOperationException: приведение к значению типа Int32"не удалось из-за того, что материализованное значение является нулевым. Либо универсальный параметр результирующего типа, либо запрос должен использовать тип, допускающий значение NULL. "

Поэтому мне нужно сделать

from c in A
where c.B.count != null
select new { count = (int?)c.B.count };

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

Я только что обнаружил, что если я сделаю явное соединение, как это, SQL будет INNER JOIN, и все прекрасно работает:

from c in A
join j in B on A.B.ID equals j.ID
select c;

Но это отстой.Мне придется изменить тонну запросов, чтобы добавить явные предложения соединения вместо того, чтобы наслаждаться удобством полей отношений, которые я получаю с EF.Кинда побеждает цель и добавляет еще много кода для поддержки.

1 Ответ

1 голос
/ 08 декабря 2011

Когда вы говорите, что ваш первый фрагмент кода создает OUTER JOIN, это имеет место, потому что B является необязательным свойством навигации для объекта A. Для требуемого свойства навигации EF создаст ВНУТРЕННЕЕ СОЕДИНЕНИЕ (более подробно объяснено здесь: https://stackoverflow.com/a/7640489/270591).

Итак, единственная альтернатива, которую я вижу вашему последнему фрагменту кода (с использованием явного join в LINQ) - кроме использования прямого SQL - это сделать ваше свойство навигации обязательным.

На мой взгляд, это все еще очень уродливый хак, который может вести себя неожиданно в других ситуациях. Если свойство навигации является обязательным или необязательным, EF добавляет «семантическое значение» к этому отношению, а именно: если существует внешний ключ! = NULL, то должен быть связанным объектом, а EF ожидает, что вы этого не сделаете удалили принудительное применение ограничения FK в базе данных.

...