Usuario
Entity Framework Code First - ¿Como mapear dos llaves foraneas a una misma entidad?

Pregunta
-
Hola, tengo una duda, tengo una entidad Rol y una entidad Usuario, cada usuario tiene asignado un rol pero en la tabla roles quiero tener un el usuario que creo el rol y el que lo modifico, el codigo es el sig:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.DataAnnotations; namespace Entidades { [Table("Usuarios")] public class Usuario { public Usuario() { } [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [StringLength(50), Required] public string NombreUsuario { get; set; } [StringLength(50), Required] public string Contrasena { get; set; } //Llaves foraneas public int FKRolId { get; set; } [ForeignKey("FKRolId")] public virtual Rol Rol { get; set; } public int? FKPerfilId { get; set; } [ForeignKey("FKPerfilId")] public virtual Perfil Perfil { get; set; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.DataAnnotations; namespace Entidades { [Table("Roles")] public class Rol { public Rol() { } [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [StringLength(50), Required] public string Nombre { get; set; } [ConcurrencyCheck, StringLength(200)] public string Descripcion { get; set; } //Enlaces foraneos public virtual ICollection<Usuario> Usuarios { get; set; } //test public int? FKUsuarioCreacionId { get; set; } [ForeignKey("FKUsuarioCreacionId")] public virtual Usuario UsuarioCreacion { get; set; } public int? FKUsuarioEdicionId { get; set; } //[ForeignKey("FKUsuarioEdicionId")] //public virtual Usuario UsuarioEdicion { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public DateTime FechaCreacion { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime FechaEdicion { get; set; } } }
Con el codigotodo se genera bien pero en la tabla de Usuarios en la base de datos me agrega una columna llamada Rol_Id, alguien sabe como resolverlo?- Cambiado Leandro TuttiniMVP sábado, 14 de abril de 2012 3:17 (De:Lenguaje C#)
Todas las respuestas
-
Hola,
Pero es que tienes que hacerlo con tres clases y la razón es la siguiente un Usuario puede tener varios Roles asociados, el problema es que estás utilizando DataAnotations y eso lógicamente tiene ciertas limitaciones. Te aconsejo que utilices FluentApi.
Fijate en el ejemplo que te voy a pasar que tienes que necesitas una tercera tabla que relaciones un usuario con con sus posibles Roles.
public class Usuario { public int Id { get; set; } public string Nombre { get; set; } public virtual ICollection<UsuarioRole> Roles { get; set; } } public class Rol { public int Id { get; set; } public string Nombre { get; set; } } public class UsuarioRole { public int Id { get; set; } public int UsuarioId { get; set; } public int RolId { get; set; } public Rol Rol { get; set; } }
y el contexto de esta forma.
public class UsuariosContext : DbContext { public DbSet<Usuario> Usuarios { get; set; } public DbSet<Rol> Roles { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Usuario>().HasMany(c => c.Roles).WithRequired().HasForeignKey(c => c.UsuarioId); modelBuilder.Entity<UsuarioRole>().HasRequired(c => c.Rol).WithMany().HasForeignKey(c => c.RolId).WillCascadeOnDelete(false); } }
Esto genera la siguiente sentencia sql para crear la bb.dd.
create table [dbo].[Rol] ( [Id] [int] not null identity, [Nombre] [nvarchar](max) null, primary key ([Id]) ); create table [dbo].[Usuario] ( [Id] [int] not null identity, [Nombre] [nvarchar](max) null, primary key ([Id]) ); create table [dbo].[UsuarioRole] ( [Id] [int] not null identity, [UsuarioId] [int] not null, [RolId] [int] not null, primary key ([Id]) ); alter table [dbo].[UsuarioRole] add constraint [Usuario_Roles] foreign key ([UsuarioId]) references [dbo].[Usuario]([Id]) on delete cascade; alter table [dbo].[UsuarioRole] add constraint [UsuarioRole_Rol] foreign key ([RolId]) references [dbo].[Rol]([Id]);
No se si esto cubre tus necesidades, sino es así comentalo y vemos otra alternativa, pero así es como yo veo Usuarios y Roles, es decir un usuario puede pertenecer a varios Roles.
Si lo que quieres controlar es el Usuario que modifico el Rol, no tienes más que agregar a UsuarioRole un usuario y configurarlo igual como yo he hecho con Roles.
Te paso un ejemplo.
public class UsuariosContext : DbContext { public DbSet<Usuario> Usuarios { get; set; } public DbSet<Rol> Roles { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Usuario>().HasMany(c => c.Roles).WithRequired().HasForeignKey(c => c.UsuarioId); modelBuilder.Entity<UsuarioRole>().HasRequired(c => c.Rol).WithMany().HasForeignKey(c => c.RolId).WillCascadeOnDelete(false); modelBuilder.Entity<UsuarioRole>().HasRequired(c => c.Usuario).WithMany().HasForeignKey(c => c.UsuarioUpdateId).WillCascadeOnDelete(false); } } public class Usuario { public int Id { get; set; } public string Nombre { get; set; } public virtual ICollection<UsuarioRole> Roles { get; set; } } public class Rol { public int Id { get; set; } public string Nombre { get; set; } } public class UsuarioRole { public int Id { get; set; } public int UsuarioId { get; set; } public int RolId { get; set; } public Rol Rol { get; set; } public int UsuarioUpdateId { get; set; } public Usuario Usuario { get; set; } }
y esto es lo que te genera en bb.dd
create table [dbo].[Rol] ( [Id] [int] not null identity, [Nombre] [nvarchar](max) null, primary key ([Id]) ); create table [dbo].[Usuario] ( [Id] [int] not null identity, [Nombre] [nvarchar](max) null, primary key ([Id]) ); create table [dbo].[UsuarioRole] ( [Id] [int] not null identity, [UsuarioId] [int] not null, [RolId] [int] not null, [UsuarioUpdateId] [int] not null, primary key ([Id]) ); alter table [dbo].[UsuarioRole] add constraint [Usuario_Roles] foreign key ([UsuarioId]) references [dbo].[Usuario]([Id]) on delete cascade; alter table [dbo].[UsuarioRole] add constraint [UsuarioRole_Rol] foreign key ([RolId]) references [dbo].[Rol]([Id]); alter table [dbo].[UsuarioRole] add constraint [UsuarioRole_Usuario] foreign key ([UsuarioUpdateId]) references [dbo].[Usuario]([Id]);
Como consejo yo me plantearía utilizar FluentApi y si puedes olvidarte de las DataAnotations.
Saludos,
-
Gracias por el aporte pero no siempre suelo usar datanotations solo en este caso me interesa que sea con datanotations y no con fluent.
Si entiendo que si un usuario puede tener muchos roles entonces hay una 3 tabla pero en este caso no, cada usuario unicamente puede tener un rol.
Tengo la tabla roles, usuarios y muchas mas, pero en todas las tablas o almenos en muchas quiero guardar un historico de el usuario que creo el registro, la fecha en la que se creo el registro, el usuario que modifico y la fecha de modificacion, uso datanotation y ademas en algunos casos uso fluent api, la razon por la que me interesa resolverlo con datanotations es por que si en 50 clases voy a tener el registro del historico no me gustaria tener que crear mucho codigo y pues un ejemplo tengo la clase Historico
public class HistoricoCompletoNull { public int? FKUsuarioCreacionId { get; set; } [ForeignKey("FKUsuarioCreacionId")] public virtual Usuario UsuarioCreacion { get; set; } public int? FKUsuarioEdicionId { get; set; } [ForeignKey("FKUsuarioEdicionId")] public virtual Usuario UsuarioEdicion { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public DateTime FechaCreacion { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime FechaEdicion { get; set; } }
y tengo la clase Roles asi:
[Table("Roles")] public class Rol { public Rol() { } [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [StringLength(50), Required] public string Nombre { get; set; } [ConcurrencyCheck, StringLength(200)] public string Descripcion { get; set; } //Enlaces foraneos public virtual ICollection<Usuario> Usuarios { get; set; } }
Ahora si quiero que esa clase tenga el registro pues solo le digo que herede de el historico y listo.
public class Rol : Core.HistoricoCompletoNull
Que es como si yo tubiera mi clase rol asi:
public class Rol { public Rol() { } [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [StringLength(50), Required] public string Nombre { get; set; } [ConcurrencyCheck, StringLength(200)] public string Descripcion { get; set; } //Enlaces foraneos public virtual ICollection<Usuario> Usuarios { get; set; } public int? FKUsuarioCreacionId { get; set; } [ForeignKey("FKUsuarioCreacionId")] public virtual Usuario UsuarioCreacion { get; set; } public int? FKUsuarioEdicionId { get; set; } [ForeignKey("FKUsuarioEdicionId")] public virtual Usuario UsuarioEdicion { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public DateTime FechaCreacion { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime FechaEdicion { get; set; } }
Cuando se crean las bases de datos la tabla roles queda bien
Pero en la tabla Usuarios se crea un campo que se llama Rol_Id y no se por que.
-
Hola,
Pero en la tabla Usuarios se crea un campo que se llama Rol_Id y no se por que.
Pero no dices que el usuario pertenece a un rol, lo normal es que esta tengas una fk a Roles.
Revista este link y para el tema del historico lo puedes utilizar y de esa forma no tienes que escribir mucho:)
Saludos,
-
Hola necesito dos fk a una misma tabla, ejemplo:
Cliente y ClienteUsuario hacen referencia a una misma tabla Usuarios.
Las FK en la BD no dan problema.
Pero en el modelo no puedo ponerlo siguiente ya que da error.
<ForeignKey("Cliente")>
<Required()>
Public Property IdCliente As Integer
Public Property Cliente As Cliente
<ForeignKey("Cliente")>
<Required()>
Public Property IdClienteUsuario As Integer
Public Property ClienteUsuario As ClienteMe podrían ayudar con esto?
Gracias