2012-09-16 38 views
5

estoy usando Código EF Primero con la siguiente configuraciónmuchos a muchos colección con código de EF primera

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    base.OnModelCreating(modelBuilder); 
    modelBuilder.Entity<UserProfile>() 
     .HasMany(x => x.TeamLeaders) 
     .WithMany() 
     .Map(m => m.MapLeftKey("UserId") 
     .MapRightKey("TeamLeaderId") 
     .ToTable("UserTeamLeaders")); 
} 

jefes de equipo es un ICollection de Usuarios es decir, se trata de una auto-referencia a muchos a muchos relación.

En inglés simple, un usuario puede tener más de un líder de equipo. Esta configuración parece ser correcta, ya que crea la tabla de enlace FK/PK en duelo como era de esperar.

Tengo una aplicación MVC4 que permite editar, agregar y eliminar a los líderes de equipo de la colección.

En mi controlador, que originalmente tenía la siguiente:

var original = context.UserProfiles 
     .Include("TeamLeaders") 
     .Single(x => x.UserId == model.UserId); 

    context.Entry(original).CurrentValues.SetValues(model); 

Sin embargo, esa última línea estaba fallando para marcar la colección jefes de equipo en su versión actualizada, y cuando llamé SaveChanges(), no se registraron cambios.

En lugar de eso escribió un simple método CopyProperties en mi clase de usuario, que utiliza la reflexión para copiar manualmente las propiedades de ancho, por lo que en mi controlador ahora tengo:

var original = context.UserProfiles 
     .Include("TeamLeaders") 
     .Single(x => x.UserId == model.UserId); 

    //context.Entry(original).CurrentValues.SetValues(model); 

    original.CopyProperties(model); 

Sin embargo, esto va demasiado lejos, y SaveChanges intenta agregar un nuevo usuario al sistema que coincida con el perfil del líder del equipo seleccionado.

¿Alguien puede aconsejarme sobre qué parte de esto estoy haciendo mal? No estoy seguro de si necesito actualizar las asignaciones, o cambiar la forma en que copio las propiedades del modelo de vista al modelo

Respuesta

3

Debe modificar la colección TeamLeaders cargada en el usuario original de acuerdo con sus cambios para que la detección de cambios pueda reconocer qué líderes se han eliminado y cuáles se han agregado. Cuando llame al SaveChanges, EF escribirá las instrucciones DELETE e INSERT apropiadas para la tabla de unión en función de los cambios detectados. La manera más simple sería el siguiente:

var original = context.UserProfiles 
    .Include("TeamLeaders") 
    .Single(x => x.UserId == model.UserId); 

original.TeamLeaders.Clear(); 
foreach (var teamLeader in model.TeamLeaders) 
{ 
    var user = context.UserProfiles.Find(teamLeader.UserId); 
    if (user != null) 
     original.TeamLeaders.Add(user) 
} 
context.SaveChanges(); 

Find obtendrá los jefes de equipo del contexto si ya están cargados. Si no están cargados, consultarán la base de datos.

Si se quiere evitar consultas adicionales puede adjuntar los jefes de equipo de forma manual con el contexto:

var original = context.UserProfiles 
    .Include("TeamLeaders") 
    .Single(x => x.UserId == model.UserId); 

original.TeamLeaders.Clear(); 
foreach (var teamLeader in model.TeamLeaders) 
{ 
    var user = context.UserProfiles.Local 
     .SingleOrDefault(o => o.UserId == teamLeader.UserId); 
    if (user == null) 
    { 
     user = new User { UserId = teamLeader.UserId }; 
     context.UserProfiles.Attach(user); 
    } 
    original.TeamLeaders.Add(user) 
} 
context.SaveChanges(); 

Excepto la primera consulta para cargar el original aquí hay una consulta de base de datos más involucrados.

BTW: Usted debe ser capaz de utilizar la versión inflexible con Include Código EF Primero:

Include(u => u.TeamLeaders) 

Sólo tiene using System.Data.Entity; en su archivo de código para obtener acceso a esta versión.

+0

Gracias por esto, funciona un placer. Hubiera preferido no tener que borrar/volver a agregar en el controlador; en cambio, sería bueno que hubiera una forma de configurarlo para que funcione por sí mismo con algunas configuraciones/atributos simples en el modelo/mapeo , pero esto funciona, así que me quedaré con esto por ahora. – GGG

+0

@GGG: Desafortunadamente, la actualización de gráficos de objetos separados siempre es un poco de código para escribir.No hay una opción de configuración que simplifique el trabajo. 'CurrentValues.SetValues ​​(model)' solo afecta a las propiedades escalares, no actualiza ninguna propiedad de navegación, como lo ha visto en su primer intento. – Slauma

Cuestiones relacionadas