Произошла ошибка при сохранении сущностей, которые не предоставляют свойства внешнего ключа для своих отношений - PullRequest
47 голосов
/ 29 октября 2011

У меня есть простой код в 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.

на

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; }
}

Ответы [ 14 ]

159 голосов
/ 19 июня 2012

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

12 голосов
/ 14 марта 2014

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

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

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

6 голосов
/ 30 октября 2011

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

3 голосов
/ 03 марта 2014

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

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

garageEntity.Cars.Remove(carEntity);

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

context.Cars.Remove(carEntity);
2 голосов
/ 08 февраля 2014

В моем случае было сгенерировано исключение, потому что EF неправильно создал миграцию. Он пропустил установку идентификатора : 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}

Затем вручную добавьте идентификатор : 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));
        }
    }
2 голосов
/ 24 апреля 2012

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

1 голос
/ 04 января 2013

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

0 голосов
/ 15 мая 2018

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

В моем случае система комбинировала две модели и запрашивала DB

Неверное имя объекта 'dbo.RoleModelUserModel'.

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

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

0 голосов
/ 21 августа 2016

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

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

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

0 голосов
/ 09 сентября 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, чтобы не было никаких созданных объектов.

...