Два разных объекта с одним и тем же ключом для структуры сущности не работают

Я пытаюсь вставить ссылку на объект в свой основной объект, но EntityFramework будет жаловаться, если я не буду использовать свой ранее управляемый объект. Я просто хочу избежать зависимости от dbContext при создании моего объекта.

Упрощенный пример:

class Movie {
    public ApplicationUser Owner { get;set; }
}

var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.Owner = new ApplicationUser { Id = 2 };

// I have to attach or change its state, otherwise, EF will complain the object is not complete
db.Entry(myMovie.Owner).State = EntityState.Unchanged;

Как бы то ни было, если тот же ApplicationUser был ранее загружен контекстом, я получаю эту ошибку:

Сохранение или принятие изменений не удалось, поскольку более одного объекта типа "ApplicationUser" имеют одинаковое значение первичного ключа. Убедитесь, что явно заданные значения первичного ключа уникальны. Убедитесь, что первичные ключи базы данных настроены правильно в базе данных и в модели Entity Framework. Используйте конструктор Entity Designer для конфигурации First First/Model First. Используйте свободно распространяемый API 'HasDatabaseGeneratedOption' или 'DatabaseGeneratedAttribute' для конфигурации Code First.

Как я могу избежать этой проблемы? Оптимально, я бы не хотел говорить о состоянии этого нового объекта.

Ответ 1

Если у вас есть экземпляр, в котором вы только читаете данные, а не изменяете его, вы можете использовать AsNoTrack(), это предотвратит наличие прикрепленного экземпляра модели, о котором знает ваш контекст (это, по существу, только чтение).

Следующий код должен работать, даже если он дважды удалил один и тот же объект.

var myMovieReadOnly = db.Movies.AsNoTracking().FirstOrDefault(m, m => m.Id = 1);

var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.Owner = new ApplicationUser { Id = 2 };

db.SaveChanges();

Еще одно замечание: AsNoTraking() также может экономить на производительности в сценариях, где вы только читаете данные.


Изменить: просто перечитайте и поняли, что это модель ApplicationUser, а не фильм, но та же концепция должна применяться с повторением первого экземпляра.


Edit2:

Из наших комментариев вы также можете сделать следующее: не нужно вообще выполнять поиск, если вы уже знаете ID:

class Movie {
    public int OwnerId { get;set; }
    public ApplicationUser Owner { get;set; }
}

var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.OwnerId = 2;
db.SaveChanges();

Ответ 2

если тот же ApplicationUser был ранее загружен контекстом, я получаю эту ошибку

Итак, сначала проверьте, загружен ли ApplicationUser.

var newId = 2;
var newApplicationUser = db.ApplicationUsers.Local.FirstOrdefault(u => u.Id == newId)
                             ?? new ApplicationUser { Id = newId };
myMovie.Owner = newApplicationUser;