Произошла ошибка при сохранении сущностей, которые не раскрывают свойства внешнего ключа для своих отношений.

Сначала у меня есть простой код в Entity Framework 4.1 коде:

PasmISOContext db = new PasmISOContext();
var user = new User();
user.CreationDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.Users.Add(user);

db.SaveChanges();
user.Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") };
db.SaveChanges();


db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();

Проблема в том, что я получаю сообщение об ошибке

Произошла ошибка при сохранении сущностей, которые не раскрывают свойства внешнего ключа для своих отношений. Свойство EntityEntries вернет значение null, поскольку отдельная сущность не может быть идентифицирована как источник исключения. Обработку исключений при сохранении можно упростить, если раскрыть свойства внешнего ключа в типах сущностей. Подробнее см. InnerException.

at

db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();

Не понимаю, почему работает подобная операция. Что-то не так с моей моделью или с ef-code-first?

public class Avatar
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string LinkInString { get; set; }

    [NotMapped]
    public Uri Link
    {
        get { return new Uri(LinkInString); }
        set { LinkInString = value.AbsoluteUri; }
    }
}

public class User
{
    [Key]
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public Avatar Avatar { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
    public virtual ICollection<Achievement> Achievements { get; set; }

    public DateTime CreationDate { get; set; }
    public DateTime LastLoginDate { get; set; }
    public DateTime LastActivityDate { get; set; }
}

person user278618    schedule 29.10.2011    source источник
comment
Я не могу воспроизвести ошибку. Можете ли вы проверить первый фрагмент кода? Это именно то, что вы делаете? (По крайней мере, один db.Users.Add(user) кажется отсутствует, иначе 2 SaveChanges не имеют смысла.) Есть ли у вас какое-либо дополнительное сопоставление Fluent API?   -  person Slauma    schedule 29.10.2011
comment
Спасибо, Слаума. Я отредактировал свой код. У меня нет Fluent API. Первое и второе SaveChanges добавляют строку в таблицы Avatars и Users. Не знаю, важно ли это, но в таблице у меня есть столбец Avatar_Id.   -  person user278618    schedule 29.10.2011
comment
Avatar_Id в User таблице - это столбец внешнего ключа для свойства навигации Avatar, это нормально. Вы создавали таблицы базы данных вручную или позволили EF создать базу данных? На данный момент я не понимаю, почему вы получаете эту ошибку. Вы можете попробовать следовать совету, данному в исключении, и добавить свойство внешнего ключа в свой User класс: public int? AvatarId { get; set; } и посмотреть, что произойдет. Возможно, по крайней мере, исключение раскроет более подробную информацию, в чем именно заключается проблема.   -  person Slauma    schedule 29.10.2011
comment
Я позволил EF создать базу данных. Как вы сказали, я добавил это свойство, и теперь оно работает нормально - теперь у меня AvatarId вместо Avatar_Id, и он сохраняет правильный аватарид. Это странно, но спасибо, что помогли мне.   -  person user278618    schedule 30.10.2011
comment
Вы можете опубликовать это как ответ, что свойство FK устранило проблему, а затем принять свой ответ, просто чтобы закончить этот вопрос. Я не понимаю, почему это решило вашу проблему, для меня это также работало без свойства FK.   -  person Slauma    schedule 30.10.2011


Ответы (15)


Для тех из вас, у кого все еще будет эта ошибка при правильном определении всех ключей, взгляните на свои объекты и убедитесь, что вы не оставляете поле datetime с нулевым значением.

person Baral    schedule 19.06.2012
comment
Если вы посмотрите на внутренние исключения, то увидите исключение типа System.Data.SqlClient.SqlException с сообщением: Преобразование типа данных datetime2 в тип данных datetime привело к значению вне допустимого диапазона. Это происходит из-за того, что в столбец вставляется нулевое значение DateTime с недопустимыми значениями NULL. См. stackoverflow.com/questions/1331779/. - person Ryan Kyle; 04.01.2015
comment
Мое внутреннее исключение не имело ничего, связанного с преобразованием datetime2, но я устанавливал для datetime значение null. Столбец DB допускает нули, поэтому я предположил, что проблема не в этом, но оказалось, что это так. Попробуйте, даже если вы не думаете, что это ваша проблема! - person vaindil; 01.03.2016
comment
привет @SarangK, можешь сказать, как решить эту проблему? - person Med Elgarnaoui; 18.09.2016
comment
Спасибо, это помогло и сэкономило мне много времени! - person hotfusion; 07.03.2017
comment
@MohamedElgarnaoui Я проверил, что все поля DateTime должны быть инициализированы перед 'context.SaveChanges ();' - person SarangK; 09.06.2017
comment
Это действительно для любого типа данных. У меня есть проект, который передает данные через несколько методов, и строковое поле не было установлено в одном из DTO. C # это не волновало, но поле базы данных не допускает значения NULL, как и SQL Server. - person Wildcat Matt; 31.07.2019

Это сообщение об ошибке может быть выдано по любой причине. Свойство InnerException (или его InnerException, или его InnerException и т. Д.) Содержит фактическую основную причину проблемы.

Конечно, было бы полезно узнать что-нибудь о том, где возникла проблема - какой объект (ы) в единице работы вызывает проблему? Сообщение об исключении обычно сообщает вам в свойстве EntityEntries, но в этом случае по какой-то причине это невозможно. Эта диагностическая сложность - пустое свойство EntityEntries - по-видимому, вызвана тем, что некоторые сущности «не раскрывают свойства внешнего ключа для своих отношений».

Даже если OP получает ошибку из-за неспособности инициализировать DateTimes для второго экземпляра User, они получают диагностическое осложнение - EntityEntries пусто и сбивает с толку сообщение верхнего уровня ... потому что одна из их Entity не 'выставить свойства внешнего ключа'. Чтобы исправить это, Avatar должно иметь свойство public virtual ICollection<User> Users { get; set; }.

person David Bullock    schedule 14.03.2014

Проблема была решена добавлением свойства FK.

person user278618    schedule 30.10.2011
comment
Я наткнулся на этот ответ. Я получал эту ошибку во время инициализации базы данных (dropcreatedb). У меня уже был внешний ключ, но создание его нулевого значения решило мою проблему. - person Ben Felda; 01.03.2013

В моем случае следующая ситуация дала мне такое же исключение:

Представьте себе модель EF с первым кодом, в которой у вас есть Garage сущность, которая имеет набор Car сущностей. Мне нужно было вывести машину из гаража, поэтому я получил код, который выглядел так:

garageEntity.Cars.Remove(carEntity);

Вместо этого это должно было выглядеть так:

context.Cars.Remove(carEntity);
person Memet Olsen    schedule 03.03.2014
comment
Если предположить, что у автомобиля ДОЛЖЕН быть гараж, да. Потому что иначе вы бы попытались передать значение GarageId в свой объект Car. - person Flater; 26.03.2014

Просто для тех, у кого могут быть похожие проблемы. У меня была такая же ошибка, но по другой причине. В одном из дочерних объектов я определил [Key] как значение, одинаковое для разных сохранений. Глупая ошибка с моей стороны, но сообщение об ошибке не сразу приводит вас к проблеме.

person Simon The Cat    schedule 24.04.2012

В моем случае исключение было создано из-за того, что EF неправильно создал миграцию. Он пропустил установку identity: true во второй таблице. Итак, войдите в миграции, которые создали соответствующие таблицы, и проверьте, не пропущено ли оно, чтобы добавить личность.

CreateTable(
    "dbo.LogEmailAddressStats",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            EmailAddress = c.String(),
        })
    .PrimaryKey(t => t.Id);

CreateTable(
    "dbo.LogEmailAddressStatsFails",
    c => new
        {
            Id = c.Int(nullable: false), // EF missed to set identity: true!!
            Timestamp = c.DateTime(nullable: false),
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.LogEmailAddressStats", t => t.Id)
    .Index(t => t.Id);

Столбец Id должен иметь идентификатор (т.е. автоматически увеличиваться!), Поэтому это должна быть ошибка EF.

Вы можете вручную добавить идентификацию с помощью SQL прямо в базу данных, но я предпочитаю использовать Entity Framework.

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

Альтернативный вариант 1

отменить неправильно созданную миграцию с помощью

update-database -target:{insert the name of the previous migration}

Затем вручную добавьте identity: true в код миграции, а затем снова update-database.

Альтернативный вариант 2

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

add-migration identity_fix

это создаст пустую миграцию. Тогда просто добавьте это

    public partial class identity_fix : DbMigration
    {
        public override void Up()
        {
            AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false, identity: true));
        }

        public override void Down()
        {
            AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false));
        }
    }
person fredrik.hjarner    schedule 07.02.2014
comment
Этот меня поймал переименованием таблицы. Спасибо. - person Tristan Warner-Smith; 12.01.2015

Эта проблема также может возникнуть из-за перевернутого объявления ключей. Если вы используете fluent для настройки отношения, убедитесь, что левая и правая клавиши сопоставлены с правильным объектом.

person Edyn    schedule 03.01.2013

У меня была такая же проблема. в моем случае это произошло из-за поля datetime с нулевым значением. Мне пришлось перенести значение на datetime, и все прошло нормально

person onlyme    schedule 11.03.2015

Другой ответ:

Я использовал это:

public List<EdiSegment> EdiSegments { get; set; }

вместо этого:

public virtual ICollection<EdiSegment> EdiSegments { get; set; }

и получил сообщение об ошибке, указанное выше.

person Greg Gum    schedule 05.11.2014

У меня была такая же ошибка, и в моем случае проблема заключалась в том, что я добавил уже загруженный объект отношения «AsNoTracking». Мне пришлось перезагрузить свойство отношения.

Кстати, некоторые предлагают использовать «Attach» для отношений, которые уже существуют в db, хотя я не пробовал этот вариант.

person Hamed    schedule 31.01.2015

В моем случае проблема заключалась в том, что я неправильно переименовал столбец, поэтому миграция сделала два столбца: один с именем «TeamId» и один с именем «TeamID». C # заботится, SQL - нет.

person Duston    schedule 26.05.2015

Еще один другой случай. Запрос был приведен к списку, и при этом он создал сущности с помощью их конструктора для сравнения в выражении linq сразу после ToList (). Это создало сущности, которые перешли в удаленное состояние после завершения выражения linq.
Однако! Произошла небольшая корректировка, которая создала еще одну сущность в конструкторе, так что эта новая сущность была связана с сущностью, которая была помечена как удаленная.

Код для иллюстрации:

query.Except(_context.MyEntitySetSet()
                .Include(b => b.SomeEntity)
                .Where(p => Condition)
                .ToList() // This right here calls the constructor for the remaining entities after the where
                .Where(p => p.Collection.First(b => Condition).Value == 0)
                .ToList();

Конструктор MyEntity:

public partial class MyEntity
{
    protected MyEntity()
    {
        // This makes the entities connected though, this instance of MyEntity will be deleted afterwards, the instance of MyEntityResult will not.
        MyEntityResult = new MyEntityResult(this);
    }
}

Мое решение заключалось в том, чтобы убедиться, что все выражение было выполнено внутри IQueryable, чтобы не было никаких созданных объектов.

person Mixxiphoid    schedule 09.09.2015

Я не совсем уверен, что это поможет в вашем случае, потому что я настраиваю свои таблицы с помощью Fluent API, однако, насколько я могу судить, проблема возникает независимо от того, настроена ли схема с использованием аннотаций данных (атрибутов) или Fluent API (конфигурация).

Кажется, есть ошибка в EF (v. 6.1.3), поскольку он пропускает определенные изменения схемы при обновлении БД до следующей миграции. Самый быстрый способ обойти это (на этапе разработки) - удалить все таблицы из БД и снова выполнить миграцию runt со стадии инициализации.

Если вы уже в производственной среде, самым быстрым решением, которое я нашел, было вручную изменить схему в БД или, если вы хотите иметь контроль версий изменений, вручную манипулировать методами Up () и Вниз () в процессе миграции.

person Konrad Viltersten    schedule 21.08.2016

Сегодня я столкнулся с этой проблемой и попробовал возможные решения, опубликованные выше, но ни одно из них мне не помогло. У меня был реализован шаблон UnitOfWork, и система фиксировала данные в последнюю очередь после добавления всех записей.

В моем случае система объединяла две модели и запрашивала БД.

Недопустимое имя объекта "dbo.RoleModelUserModel".

где на самом деле это были две разные модели.

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

person Nawaz Khan    schedule 15.05.2018

После небольшого исследования я обнаружил, что, хотя .Net поддерживает минимальную дату (DateTime.MinValue) 01/01/0001 00:00:00 и максимальную (DateTime.MaxValue) 31/12/9999 23:59:59 в SQL Server Compact Edition, минимальная дата составляет 01/01/1753 00:00:00. Когда я ввел дату больше 01/01/1753 00:00:00, эта ошибка исчезла.

person Andrei Krasutski    schedule 15.10.2020