¿Alguien puede decirme cómo puedo arreglar mi modelo (usando FluentAPI) para poder asignar un usuario diferente a mi User.FriendCollection?

StackOverflow https://stackoverflow.com//questions/9710655

Pregunta

Soy nuevo en CodeFirst y estoy intentando crear una colección de amigos para que dos usuarios puedan tenerse entre sí en su colección de amigos.

Aquí está mi clase de usuario:

public class User
{
    public User()
    {
        if (FriendCollection == null) FriendCollection = new Collection<Friend>();
    }
    public long ID { get; set; }
    public virtual ICollection<Friend> FriendCollection { get; set; }
}

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        HasKey(x => x.ID);
    }
}

Aquí está mi clase de amigos:

public class Friend
{
    public Friend()
    {
        DateAdded = DateTime.UtcNow;
    }
    public long ID { get; set; }
    public DateTime DateAdded { get; set; }
    public long UserID { get; set; }
    public virtual User User { get; set; }
}

public class FriendConfiguration : EntityTypeConfiguration<Friend>
{
    public FriendConfiguration()
    {
        HasKey(x => x.ID);
        HasRequired(x => x.User).WithMany(x => x.FriendCollection).HasForeignKey(x => x.UserID);
    }
}

Aquí está mi clase de entidad:

    public Entities()
    {
        Database.SetInitializer<Entities>(new DropCreateDatabaseAlways<Entities>());
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Friend> Friends { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new UserConfiguration());
        modelBuilder.Configurations.Add(new FriendConfiguration());

        base.OnModelCreating(modelBuilder);
    }
}

Y aquí está mi código de consola e idealmente lo que me gustaría lograr:

        using (Entities db = new Entities ())
        {
            var u1 = new User();
            db.Users.Add(u1);

            var u2 = new User();
            db.Users.Add(u2);

            u1.FriendCollection.Add(new Friend { User = u2 });
            u2.FriendCollection.Add(new Friend { User = u1 });

            try
            {
                var cnt = db.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
                    }
                }
            }

SaveChanges arroja un error cada vez que intento completar la clase Amigo con un objeto Usuario que dice:

Se violó la restricción de multiplicidad.El rol 'Friend_User_Target' de la relación 'Entities.Friend_User' tiene multiplicidad 1 o 0...1.

Si asigno al ID de usuario así:

u1.FriendCollection.Add(nuevo amigo {ID de usuario = u2.ID});

Esto me permite guardar las entidades, ¡pero no correctamente!

Cuando reviso el código puedo aislar el problema y esto es lo que encuentro:

b.FriendCollection.Add(new Friend { UserID = a.ID });
//Friend.UserID = 1
db.SaveChanges();
//Friend.UserID = 2 matching the b.ID

Así que ahora puedo volver a mi ejemplo anterior y SaveChanges() sin generar el error si asigno Friend.User al usuario que llama.Por ejemplo:

b.FriendCollection.Add(new Friend { User = b }); 
db.SaveChanges()

Pero cuando intento asignar un usuario diferente aparece el error.Por ejemplo:

b.FriendCollection.Add(new Friend { User = a }); 
db.SaveChanges()

Yo obtengo:

Se violó la restricción de multiplicidad.El rol 'Friend_User_Target' de la relación 'Entities.Friend_User' tiene multiplicidad 1 o 0...1.

¿Alguien puede decirme cómo puedo arreglar mi modelo (usando FluentAPI) para poder asignar un usuario diferente a mi User.FriendCollection?

¿Fue útil?

Solución

El mapeo no es correcto, al menos no es correcto para el significado de las relaciones en su modelo.

Su mapeo crea una única relación y las dos propiedades de navegación. User.FriendCollection y Friend.User son los dos extremos de esta única relación.Las has definido como propiedades inversas.

Pero esto significaría que si tienes un usuario UsuarioA que tiene un AmigoB en el FriendCollection el User en FriendB debe ser UsuarioA y no cualquier otro usuario.Su modelo sólo puede expresar que un usuario es amigo suyo, por así decirlo.Debido a que está agregando otro usuario a la colección, obtiene excepciones.

En mi opinión la entidad Friend no representa un amigo = otro usuario, pero en realidad expresa una relación.Es una especie de entidad intermedia para una relación de muchos a muchos entre usuarios y esta relación es una Friendship (Preferiría llamarlo así).

En cualquier caso, su modelo necesita dos relaciones.Puedes dejar las entidades como están pero usa este mapeo:

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        HasKey(x => x.ID);
        HasMany(x => x.FriendCollection)
            .WithRequired();
    }
}

public class FriendConfiguration : EntityTypeConfiguration<Friend>
{
    public FriendConfiguration()
    {
        HasKey(x => x.ID);
        HasRequired(x => x.User)
            .WithMany()
            .HasForeignKey(x => x.UserID)
            .WillCascadeOnDelete(false);
    }
}

(Es necesario deshabilitar la eliminación en cascada en una de las relaciones para evitar la infame "excepción de ruta de eliminación en cascada múltiple".)

Tenga en cuenta que ahora tiene dos claves externas en el Friend mesa, UserID para el segundo y User_ID para el primer mapeo.Puede cambiar el nombre de la columna predeterminada agregando Map(m => m.MapKey("SomeUserID")) al primer mapeo.

Con este mapeo el código de tu consola debería funcionar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top