Допускаются ли в Entity Framework 4 внешние ключи, допускающие значение NULL?

У меня проблема с обновлением внешнего ключа в сущности Entity Framework. Я использую объекты с самопроверкой и имею объект с некоторыми отношениями, в которых внешний ключ также присутствует как свойство (одна из новых функций EF4). Ключ (целое число) помечен как Nullable и фиксированный режим параллелизма.

В частности, у меня есть объект Alarm с отношением многие к 0..1 к подтверждающему пользователю. (пользователь может подтвердить несколько тревог, но тревогу может подтвердить только ноль или один пользователь).

Определения сущностей (упрощенные):

Alarm properties
Id      Int32   non-nullable  identity entity key
UserId  Int32   nullable concurrency mode fixed
Alarm navigation properties
User    0..1 multiplicity

User properties
Id      Int32   non-nullable  identity entity key
Name    String  non-nullable

В моей сущности с самотслеживанием подтверждающий идентификатор пользователя автоматически генерируется как Nullable, как и ожидалось, однако, если я назначаю пользователю уже постоянный сигнал тревоги и запускаю ApplyChanges, расширение контекста самотслеживания пытается установить исходное значение (null) в контекст EF (в SetValue в расширениях контекста), но молча пропускает это, потому что ClrEquivalentType из EdmType не допускает значения NULL Int32.

Автоматически сгенерированный код расширения:

    private static void SetValue(this OriginalValueRecord record, EdmProperty edmProperty, object value)
    {
        if (value == null)
        {
            Type entityClrType = ((PrimitiveType)edmProperty.TypeUsage.EdmType).ClrEquivalentType;
            if (entityClrType.IsValueType &&
                !(entityClrType.IsGenericType && typeof(Nullable<>) == entityClrType.GetGenericTypeDefinition()))
            {
                // Skip setting null original values on non-nullable CLR types because the ObjectStateEntry won't allow this
                return;
            }
        }

        int ordinal = record.GetOrdinal(edmProperty.Name);
        record.SetValue(ordinal, value);
    }

Когда позже EF пытается обновить мой аварийный сигнал, я получаю исключение OptimisticConcurrencyException, потому что он создает предложение WHERE в операторе UPDATE, где он использует 0 (ноль) в качестве исходного значения внешнего ключа пользователя вместо правильного «равно нулю». (Предложение WHERE является частью механизма оптимистичного параллелизма EF, в котором исходные значения свойств, отмеченных «фиксированным» режимом параллелизма, проверяются по свойствам в базе данных).

Внешние ключи / примитивные типы, допускающие значение NULL, не полностью поддерживаются в объектах самопроверки для EF? Если нет, должен ли я использовать фиктивные сущности вместо null или есть другие обходные пути?

Обновление. Я попытался воспроизвести проблему без STE, но простой EF, похоже, хорошо справляется с оптимистичным параллелизмом для внешних ключей, допускающих значение NULL, так что это проблема STE, а не EF. Существует множество проблем с объектами самопроверки, поэтому неудивительно, что здесь есть сбой. Если я найду обходной путь, который можно реализовать в сценарии STE T4, я опубликую его здесь.


person Holstebroe    schedule 30.08.2010    source источник


Ответы (2)


Билл Хут опубликовал рабочий патч по адресу MSDN.

person Holstebroe    schedule 02.11.2010

Да, внешние ключи, допускающие значение NULL, безусловно, разрешены. Мы используем их повсюду. Вы не показываете свою базу данных или модель, поэтому трудно понять, в чем может быть проблема, но похоже, что Entity Framework не может определить первичный ключ одной из задействованных таблиц. Возможно, у вас его нет, может быть, потому что один из них - вид? Я предполагаю здесь, потому что вы не даете много информации о том, что делаете.

person Craig Stuntz    schedule 30.08.2010
comment
Я почти уверен, что мои первичные ключи в порядке. Проблема возникает, когда я меняю указанную сущность, допускающую значение NULL, на новую сущность. Это приводит к тому, что исходная ссылка на сущность (null) и ключ (null) сохраняются в исходной коллекции значений. Когда вызывается ApplyChanges, выполняется попытка перемещения исходных значений из трекера изменений сущностей в контекст EF, но поскольку контекст EF определяет тип ключа как Int32, а не Nullable ‹Int32› как сущность самотслеживания, он не может быть назначен нулевой. - person Holstebroe; 31.08.2010
comment
Обнуляемые столбцы int должны быть в вашем CSDL как int?, а не int. Попробуйте это с новой (ранее не отображенной) таблицей; вот увидишь. Изменили ли вы возможность нулевого значения с момента создания модели? - person Craig Stuntz; 31.08.2010
comment
Я попытался создать новую сущность FooEntity без первичного ключа и обнуляемого Int32 под названием Foo. Это то, что создается в CSDL: ‹EntityType Name = FooEntity› ‹Тип свойства = Int32 Name = Foo Nullable = true /› ‹/EntityType› Нет Int32? здесь. Изменение Int32 на Int32? вызывает ошибку Значение Int32? недействителен в соответствии с его типом данных 'schemas.microsoft.com/ado/2008/09 / edm: TPropertyType ' - person Holstebroe; 01.09.2010
comment
Кстати, вы используете объекты с самопроверкой с проверкой параллелизма? Проблема возникает, когда расширение контекста объектов самопроверки заполняет контекст целым числом, допускающим значение NULL, с фиксированным режимом параллелизма. Если вы не используете модель параллелизма или объекты с самопроверкой, вы, вероятно, не столкнетесь с этой проблемой. - person Holstebroe; 01.09.2010
comment
Type="Int32" Nullable="true" is what I meant whent I wrote "int?". This shouls be codegened as int? . Fixed concurrency is probably not right for a random FK; we use a TIMESTAMP` для этого. - person Craig Stuntz; 01.09.2010
comment
Думаю, я мог бы использовать стандартный оптимистичный шаблон параллелизма с полями TIMESTAMP, но я не вижу логической причины, по которой EF не должен поддерживать модель параллелизма для внешних ключей. EF может нормально обрабатывать внешние ключи, допускающие значение NULL. То, что он не может обработать, - это сценарий, в котором STE хочет сохранить обновленный нулевой ключ в качестве исходного значения в контексте EF. Нет никакого смысла в том, что EF не поддерживает это. Для меня это в лучшем случае похоже на ошибку или недоработку в дизайне, но я надеялся, что мне удастся ее обойти. Другие люди на форумах MSDN сообщают о той же проблеме, но от MS нет ответов. - person Holstebroe; 02.09.2010
comment
Возможно, это ошибка. Но если столбец, допускающий значение NULL, отображается как int, а не int? (в вашей кодовой сущности), тогда ошибка находится в вашем CSDL или кодогенераторе, а не в EF. Если вы видите такое же поведение с int?, это будет похоже на ошибку EF. - person Craig Stuntz; 02.09.2010
comment
Обнуляемый Int32 отображается в STE как Nullable ‹Int32›, как и ожидалось. STE обрабатывает значения, допускающие значение NULL, как и ожидалось, но при присвоении значения NULL с ненулевым значением значение NULL сохраняется в исходной коллекции значений. Однако STE не может ввести нулевое значение в качестве исходного значения в контекст EF, что заставляет EF использовать ноль, а не ноль при проверке оптимистичного параллелизма. - person Holstebroe; 03.09.2010
comment
Вы смотрели код, сгенерированный шаблоном STE? Если бы ошибка была там (я не проверял; я не использую STE), вы могли бы исправить это самостоятельно, изменив T4. - person Craig Stuntz; 03.09.2010
comment
Да, приведенный выше метод SetValue взят из шаблона STE. Я не знаю, можно ли устранить проблему в коде STE, поскольку я не знаю, как сообщить контексту EF, что мой измененный внешний ключ был исходным нулевым значением, и он должен использовать IS NULL not = 0 в оптимистическом параллелизме, где оговорка при обновлении. Мне кажется, что у EF есть проблема с оптимистичным параллелизмом при использовании внешних ключей, допускающих значение NULL. Заголовок моего вопроса, вероятно, должен был заключаться в следующем: внешние ключи, допускающие значение NULL, с оптимистическим параллелизмом в фиксированном режиме, разрешенным в EF ?. - person Holstebroe; 07.09.2010
comment
Мне кажется, проблема в этом коде: typeof(Nullable<>) == entityClrType.GetGenericTypeDefinition(). Если ваша сущность имеет int?, этот код должен разрешить SetValue функционировать, потому что он должен определить, что тип допускает значение NULL, и разрешить это. Если бы я был на вашем месте, я бы отладил это и посмотрел, где он терпит неудачу. - person Craig Stuntz; 07.09.2010
comment
Да, проблема в том, что entityClrType.GetGenericTypeDefinition () возвращает тип Int32, не допускающий значения NULL. Это противоречит обертке STE, которая правильно определяет внешний ключ как Nullable ‹Int32›. - person Holstebroe; 13.09.2010
comment
Как я уже писал в своей последней редакции, простой EF обрабатывает оптимистичный параллелизм, как и ожидалось. Это проблема STE, которая, надеюсь, может быть решена путем исправления кода T4. - person Holstebroe; 13.09.2010
comment
Да, я согласен; похоже, что ошибка в шаблоне. - person Craig Stuntz; 13.09.2010