2011-10-25 23 views
14

Estoy trabajando en una aplicación WPF utilizando Entity Framework 4.0. Cuando traté de guardar el objeto, recibí una excepción de clave principal, pero la clave principal es un campo AutoIncrementado y no puedo entender el motivo de la excepción.Evitar que Entity Framework inserte valores para las propiedades de navegación

Así que después de probar esto y eso, y un poco de depuración y uso del generador de perfiles SQL, descubrí que antes de insertar mi objeto, un registro debe insertarse en la tabla padre, como establecí la propiedad de navegación de ese objeto .

Por lo tanto, la crux es si un intento de insertar el objeto Empleado y establecer su departamento como Employee.Department = deptObject, entonces un nuevo registro se establece para ser insertado en el objeto del departamento.

Sírvanse sugerirme de qué objetos de propiedad de navegación no se insertarán en la base de datos, cualquier propiedad o método, Cualquier cosa.

Gracias

+0

Cuál es el ¿excepción de clave principal? Es posible que no tenga la propiedad de la clave principal del modelo 'StoreGeneratedPattern' establecida en' Identity'. Si este no es el caso, ¿podría proporcionar algunas muestras de código para ver? – philt5252

Respuesta

40

Esta es la forma en que funciona EF si usa incorrectamente entidades separadas. Supongo que está utilizando algo como esto:

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

... 

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

Este código preparado entidad empleado, agregó referencia al departamento existente y salvó nuevo empleado a la base de datos. ¿Dónde está el problema? El problema es que AddObject no agrega solo un empleado sino un gráfico de objetos completos. Así es como funciona EF: no puede tener un gráfico de objetos donde parte de los objetos están conectados al contexto y parte de no. AddObject agrega cada objeto en el gráfico como uno nuevo (nuevo = insertar en la base de datos). Por lo tanto, debe cambiar la secuencia de sus operaciones o corregir el estado de las entidades manualmente para que su contexto sepa que ese departamento ya existe.

La primera solución - utilizar el mismo contexto para el departamento de la carga y de los empleados de verano:

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

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

segunda solución - conectar entidades al contexto por separado y después de eso hacer referencia entre entidades:

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

Tercera solución: corrige el estado del departamento manualmente para que el contexto no lo vuelva a insertar:

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(); 
} 
+1

Muchas gracias señor. Solo tengo una palabra para ti, DESTACADA. –

+0

Uso la clase derivada de 'DbContext' y el siguiente tiene el mismo efecto que Third Solution para' DbContext' 'context.Entry (employee.Department) .State = EntityState.Unchanged;' – twnaing

+3

En la tercera solución, ¿qué ocurre si el objeto del Departamento tiene otra relación? ¿AddObject marca todo sobre eso como agregado también? ¿Cómo puedes volver a marcar las cosas de manera recursiva sin cambios? – Bobson

0

Cuando se establece el departamento de empleado - pienso que usted debe verificar el departamento fue recuperado de la base de datos y que atribuye entidad.
Además, puede poner el id de deprtment (la propiedad de clave foránea) en lugar de establecer la propiedad de navegación del departamento.

1

Me gustaría agregar una cuarta solución además de las 3 soluciones ya incluidas en la gran respuesta de Ladislavs. En hechos, es una versión detallada de la respuesta corta de Naor. Estoy trabajando con la versión de Entity Framework 6.


asignar la identificación deparment de que el empleado en lugar de objeto departamento de

que tienden a tener una propiedad "valor de clave externa", además de la propiedad de navegación en mis clases modelo.

Así que en la clase Employee Tengo un Department propiedad y también un DepartmentId de tipo int (Hacer la int anulable si es posible que un Employee no tiene 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 
} 

¿Le podría hacer ahora es sólo la creación de la DepartmentId: Así que en lugar de:

employee.Department = departmentObject; 

acaba de establecer:

employee.DepartmentId = departmentObject.Id; 

o

employee.DepartmentId = departmentid 

Ahora al llamar SaveChanges en el empleado añadido, sólo el empleado se guarda y se crea ningún nuevo departamento. Pero la referencia de Employee a Department se establece correctamente debido a la identificación del departamento asignada.


Más información

por lo general iba a tener acceso a la Department objeto de la clase Employee sólo cuando leo/empleados de procesamiento. Al crear o actualizar empleados, utilizaría la propiedad DepartmentId de la clase Employee para asignar.

no asignar a la propiedad Department del Employee tiene un inconveniente: Se podría hacer una depuración más difícil, porque antes de llamar SaveChanges y volver a leer los empleados que no sería posible ver o utilizar el Department objeto de la Employee. Datos de estado de entidad


fijación en EF6

Esto se refiere al número Ladislavs solución 3.

Con EF6 se hace de esa manera:

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