Подтвердить что ты не робот

Запретить платформу Entity Framework вставлять значения для навигационных свойств

Я работаю над WPF-приложением, использующим Entity Framework 4.0. Когда я попытался сохранить объект, у меня возникло первичное ключевое исключение, но первичный ключ - это поле AutoIncremented, и я не могу понять причину исключения.

Итак, после того, как вы это проделали, и немного отлаживая и используя профилировщик SQL, я обнаружил, что перед вставкой моего объекта запись должна быть вставлена ​​в родительскую таблицу, поскольку я устанавливаю свойство навигации этого объекта.

Таким образом, проблема заключается в попытке вставить объект Employee и установить его отдел как Employee.Department = deptObject, после чего новая запись устанавливается в объект отдела.

Пожалуйста, предложите мне, каким образом объекты навигационной собственности не будут вставлены в базу данных, какое-либо свойство или какой-либо метод Anything.

Спасибо

4b9b3361

Ответ 1

Так работает EF, если вы неправильно используете отдельные объекты. Я полагаю, вы используете что-то вроде этого:

var employee = new Employee();
employee.Department = GetDepartmentFromSomewhere(departmentId);

...

using (var context = new YourContext())
{
    context.Employees.AddObject(employee);
    context.SaveChanges();
}

Этот код подготовил сущность сотрудника, добавил ссылку на существующий отдел и сохранил нового сотрудника в базе данных. В чем проблема? Проблема в том, что AddObject не добавляет только графа работника, а всего объекта. Так работает EF - вы не можете иметь граф объектов, где часть объектов связана с контекстом и частью нет. AddObject добавляет каждый объект в график как новый (новый = вставить в базу данных). Таким образом, вы должны либо изменить последовательность своих операций, либо зафиксировать состояние объектов вручную, чтобы ваш контекст знал, что этот отдел уже существует.

Первое решение - использовать тот же контекст для загрузки отдела и сохранения сотрудника:

using (var context = new YourContext())
{
    var employee = new Employee();
    ...
    context.Employees.AddObject(employee);

    employee.Department = context.Departments.Single(d => d.Id == departmentId);
    context.SaveChanges();
}

Второе решение - подключить объекты к контексту отдельно и после этого сделать ссылку между объектами:

var employee = new Employee();
...

var department = GetDepartmentFromSomewhere(departmentId);

using (var context = new YourContext())
{
    context.Employees.AddObject(employee);
    context.Departments.Attach(department);
    employee.Department = department;

    context.SaveChanges();
}

Третье решение - правильное состояние отдела вручную, чтобы контекст не вставлял его снова:

var employee = new Employee();
employee.Department = GetDepartmentFromSomewhere(departmentId);

...

using (var context = new YourContext())
{
    context.Employees.AddObject(employee);
    context.ObjectStateManager.ChangeObjectState(employee.Department, 
                                                 EntityState.Unchanged);
    context.SaveChanges();
}

Ответ 2

Я хотел бы добавить четвертое решение в дополнение к трем решениям, уже представленным в Ladislavs. В фактах это подробная версия короткого ответа от Наора. Я работаю с картой сущностей 6.


Назначить id депараметра сотруднику вместо объекта отдела

Я склонен иметь свойство "внешнего ключа" в дополнение к свойству навигации в моих классах моделей.

Итак, в классе Employee у меня есть свойство Department, а также DepartmentId типа int (сделать int nullable, если возможно, что Employee не имеет Department):

public class Employee
{
    public int Id { get; set; }

    public String EmployeeName { get; set; }


    #region FK properties

    public Department Department { get; set; }

    public int? DepartmentId { get; set; }

    #endregion
}

Не могли бы вы сейчас просто установить DepartmentId: Поэтому вместо:

employee.Department = departmentObject;

просто установите:

employee.DepartmentId = departmentObject.Id;

или

employee.DepartmentId = departmentid

Теперь при вызове SaveChanges для добавленного сотрудника сохраняется только сотрудник, и новый отдел не создается. Но ссылка от Employee до Department устанавливается правильно из-за назначенного идентификатора отдела.


Дополнительная информация

Я обычно обращался к объекту Department класса Employee только при чтении/обработке сотрудников. При создании или обновлении сотрудников я бы использовал свойство DepartmentId класса Employee для назначения.

Не назначать свойство Department для Employee имеет один недостаток: он может затруднить отладку, поскольку перед вызовом SaveChanges и повторным чтением сотрудников невозможно было бы увидеть или использовать Department объекта Employee.


Фиксирование информации о состоянии объекта в EF6

Это относится к решению Ladislavs № 3.

С EF6 это делается следующим образом:

_context.Entry(employee.Department).State = EntityState.Unchanged;

Ответ 3

Когда вы устанавливаете отдел для сотрудника - я думаю, что вы должны проверить, что отдел был извлечен из db и прикрепленного объекта.
Кроме того, вы можете поместить идентификатор депрессии (свойство внешнего ключа) вместо того, чтобы устанавливать свойство навигации отдела.