2012-03-07 12 views
5

Actualmente estoy usando EF4.3 y el Código de Primera. La creación de mis objetos funciona (a través de mis vistas, simplemente usando el Crear generado automáticamente), pero cuando intento editar un objeto, no guarda ningún cambio que, por completo, se vincule a mis propiedades de navegación. He estado en reading on relationships, pero no entiendo cómo explicarle a mi contexto que la relación ha cambiado.no puede obtener la relación de actualizar las propiedades de navegación en el marco de la entidad

Aquí hay un código ejemplo de mi aplicación.

@* Snippet from my view where I link into my ViewModel. *@ 
<div class="row"> 
    <div class="editor-label"> 
     @Html.LabelFor(model => model.ManagerID) 
    </div> 
    <div class="editor-field"> 
     @Html.DropDownListFor(model => model.ManagerID, ViewBag.Manager as SelectList, String.Empty) 
     @Html.ValidationMessageFor(model => model.ManagerID) 
    </div> 
</div> 

Aquí es mi implementación del controlador (POST de mi Editar):

[HttpPost] 
    public ActionResult Edit(ProjectViewModel projectViewModel) 
    { 
     if (ModelState.IsValid) 
     { 
      Project project = new Project(); 
      project.ProjectID = projectViewModel.ProjectID; 
      project.Name = projectViewModel.Name; 
      project.ProjectManager = repository.GetUser(projectViewModel.ManagerID); 
      repository.InsertOrUpdateProject(project); 
      repository.Save(); 
      return RedirectToAction("Index"); 
     } 
     ViewBag.Manager = new SelectList(repository.GetUsers(), "UserID", "FullName", projectViewModel.ManagerID); 
     return View(projectViewModel); 
    } 

Dentro de mi objeto Project:

public class Project 
{ 
    public int ProjectID { get; set; } 
    [Required] 
    public string Name { get; set; } 

    // Navigation Properties 
    public virtual User Manager { get; set; } 
} 

Aquí es el método correspondiente desde el repositorio (donde mi contexto reside):

public void InsertOrUpdateProject(Project project) 
    { 
     if (program.ProjectID == default(int)) 
     { 
      context.Projects.Add(project); 
     } 
     else 
     { 
      context.Entry(project).State = EntityState.Modified; 
     } 
    } 

Para que quede claro, esto funciona para actualizar mis propiedades, pero no actualiza mis propiedades de navegación (en este caso, Administrador). Aprecia cualquier ayuda.

Respuesta

11

Ajuste del estado para Modified sólo marca propiedades escalares, no como propiedades de navegación modificados. Tiene varias opciones:

  • Un truco (que no gusta)

    //... 
    else 
    { 
        var manager = project.Manager; 
        project.Manager = null; 
        context.Entry(project).State = EntityState.Modified; 
        // the line before did attach the object to the context 
        // with project.Manager == null 
        project.Manager = manager; 
        // this "fakes" a change of the relationship, EF will detect this 
        // and update the relatonship 
    } 
    
  • cargar el proyecto de la base de datos que incluye (carga ansiosa) del gestor actual. Luego establece las propiedades. El seguimiento de cambios detectará un cambio del administrador nuevamente y escribirá una ACTUALIZACIÓN.

  • exponer una propiedad clave externa para la propiedad Manager la navegación en su modelo:

    public class Project 
    { 
        public int ProjectID { get; set; } 
        [Required] 
        public string Name { get; set; } 
    
        public int ManagerID { get; set; } 
        public virtual User Manager { get; set; } 
    } 
    

    Ahora ManagerID es una propiedad escalar y establecer el estado de Modified incluirá esta propiedad.Además no es necesario cargar el usuario del Administrador de la base de datos, sólo se puede asignar la identificación que recibe de su punto de vista:

    Project project = new Project(); 
    project.ProjectID = projectViewModel.ProjectID; 
    project.Name = projectViewModel.Name; 
    project.ManagerID = projectViewModel.ManagerID; 
    repository.InsertOrUpdateProject(project); 
    repository.Save(); 
    
+0

Excelente. Gracias. Originalmente había hecho la ruta de la clave externa, pero tuve algunos problemas con ella. Resulta que funciona, pero tuve que definir el atributo [ForeignKey ("Nombre")] con la propiedad de navegación para que todo se asignara correctamente. Aceptando tu respuesta. ¡Gracias! – glockman

+0

@ user1240560: Con los nombres en mi respuesta debería funcionar sin el atributo ForeignKey (detección de FK por convención de nombre). Pero si sus nombres son levemente diferentes y no siguen las reglas de la convención, entonces sí, necesita la anotación. – Slauma

+0

@Slauma Te amo, los otros enfoques deberían funcionar con Code First Model, pero con DataBase First, el único que funcionó para mí fue tu hack, lo estuve jugando por días, y ahora tengo que decir que me encanta tanto –

-2

No estoy seguro exactamente lo que quiere decir con propiedades de navegación? ¿Te refieres a una relación de clave extranjera? Si es así, intente con la siguiente anotación de datos:

public class Project 
{ 
    public int ProjectID { get; set; } 

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

    [ForeignKey("YourNavigationProperty")] 
    public virtual UserManager { get; set; } 
} 

Actualice su contexto EF y vea qué ocurre?

ACTUALIZACIÓN

public class Project 
{ 
    public int ProjectID { get; set; } 

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

    [ForeignKey("ManagerId")] 
    public ManagerModel UserManager { get; set; } 
} 

public class ManagerModel 
{ 
    [Key] 
    public int ManagerId { get; set; } 

    public String ManagerName { get; set; } 
} 

ver si funciona?

+0

Así que no defino explícitamente mis nombres de FK. Quiero poder configurar el valor de mi Administrador en un nuevo Administrador (que parece funcionar) pero los cambios no persisten realmente en la base de datos cuando llamo a SaveChanges(). No estoy seguro de cómo hacer que eso suceda. – glockman

+0

Revisa la actualización a mi respuesta – jacqijvv

2

Hay varias opciones aquí, voy a enumerar 3 de ellos:

Opción 1: Uso GraphDiff

* Esto necesita la Configuration.AutoDetectChangesEnabled de su contexto se define como true.

sólo tiene que instalar GraphDiff con NuGet

Install-Package RefactorThis.GraphDiff 

Entonces

using (var context = new Context()) 
{ 
    var customer = new Customer() 
    { 
     Id = 12503, 
     Name = "Jhon Doe", 
     City = new City() { Id = 8, Name = "abc" } 
    }; 

    context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City)); 
    context.Configuration.AutoDetectChangesEnabled = true; 

    context.SaveChanges(); 
} 

Para más detalles sobre GraphDiff ven here.

Opción 2: Buscar y editar

Buscando su entidad con EF para realizar un seguimiento al contexto. Luego edita las propiedades.

* Esto necesita el Configuration.AutoDetectChangesEnabled de su contexto establecido en verdadero.

var customer = new Customer() 
{ 
    Id = 12503, 
    Name = "Jhon Doe", 
    City = new City() { Id = 8, Name = "abc" } 
}; 

using (var context = new Contexto()) 
{ 
    var customerFromDatabase = context.Customers 
             .Include(x => x.City) 
             .FirstOrDefault(x => x.Id == customer.Id); 

    var cityFromDataBase = context.Cities.FirstOrDefault(x => x.Id == customer.City.Id); 

    customerFromDatabase.Name = customer.Name; 
    customerFromDatabase.City = cityFromDataBase;     

    context.Configuration.AutoDetectChangesEnabled = true; 
    context.SaveChanges(); 
} 

Opción 3: Uso de una propiedad escalar

En cuestión de rendimiento esta es la mejor manera, pero su desorden clase con las preocupaciones de base de datos. Debido a que deberá crear una propiedad escalar (primitiva) para asignar el Id.

* De esta manera no hay necesidad de configurar el Configuration.AutoDetectChangesEnabled en verdadero. Y tampoco necesitará realizar una consulta a la base de datos para recuperar las entidades (ya que las dos primeras opciones sí lo harían, sí GraphDiff lo hace detrás de las escenas).

var customer = new Customer() 
{ 
    Id = 12503, 
    Name = "Jhon Doe", 
    City_Id = 8, 
    City = null 
}; 

using (var contexto = new Contexto()) 
{ 
    contexto.Entry(customer).State = EntityState.Modified; 
    contexto.SaveChanges(); 
} 
Cuestiones relacionadas