Queremos usar una relación opcional uno a uno usando Entity Framework Code First. Tenemos dos entidades.
public class PIIUser
{
public int Id { get; set; }
public int? LoyaltyUserDetailId { get; set; }
public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}
public class LoyaltyUserDetail
{
public int Id { get; set; }
public double? AvailablePoints { get; set; }
public int PIIUserId { get; set; }
public PIIUser PIIUser { get; set; }
}
PIIUser
puede tener un LoyaltyUserDetail
pero LoyaltyUserDetail
debe tener un PIIUser
. Probamos estas técnicas de enfoque fluido.
modelBuilder.Entity<PIIUser>()
.HasOptional(t => t.LoyaltyUserDetail)
.WithOptionalPrincipal(t => t.PIIUser)
.WillCascadeOnDelete(true);
Este enfoque no creó LoyaltyUserDetailId
una clave externa en la PIIUsers
tabla.
Después de eso, probamos el siguiente código.
modelBuilder.Entity<LoyaltyUserDetail>()
.HasRequired(t => t.PIIUser)
.WithRequiredDependent(t => t.LoyaltyUserDetail);
Pero esta vez EF no creó ninguna clave externa en estas 2 tablas.
¿Tiene alguna idea para este tema? ¿Cómo podemos crear una relación opcional uno a uno utilizando la api fluida del marco de entidad?
fuente
WithOptional
? ¿Cuál es la diferencia conWithRequiredDependent
yWithRequiredOptional
?Simplemente haz como si tuvieras una relación de uno a muchos entre ellos
LoyaltyUserDetail
yPIIUser
tu mapeo debería serEF debería crear todas las claves externas que necesita y simplemente no le importa WithMany .
fuente
Hay varios problemas con su código.
Una relación 1: 1 es: PK <-PK , donde un lado PK es también un FK, o PK <-FK + UC , donde el lado FK no es PK y tiene UC. Su código muestra que tiene FK <-FK , ya que define ambos lados para tener un FK, pero eso está mal. Recon
PIIUser
es el lado PK yLoyaltyUserDetail
es el lado FK. Esto significaPIIUser
que no tiene un campo FK, pero loLoyaltyUserDetail
tiene.Si el 1: 1 es opcional, el lado FK debe tener al menos 1 campo anulable.
pswg anterior respondió a su pregunta, pero cometió un error de que también definió un FK en PIIUser, que por supuesto es incorrecto como describí anteriormente. Así que defina el campo FK anulable en
LoyaltyUserDetail
, defina el atributo enLoyaltyUserDetail
para marcarlo como el campo FK, pero no especifique un campo FK enPIIUser
.Obtiene la excepción que describe arriba debajo de la publicación de pswg, porque ningún lado es el lado PK (final del principio).
EF no es muy bueno en 1: 1 ya que no puede manejar restricciones únicas. No soy un experto en Code primero, así que no sé si es capaz de crear una UC o no.
(editar) por cierto: A 1: 1 B (FK) significa que solo hay 1 restricción FK creada, en el objetivo de B apuntando al PK de A, no a 2.
fuente
public class User { public int Id { get; set; } public int? LoyaltyUserId { get; set; } public virtual LoyaltyUser LoyaltyUser { get; set; } } public class LoyaltyUser { public int Id { get; set; } public virtual User MainUser { get; set; } } modelBuilder.Entity<User>() .HasOptional(x => x.LoyaltyUser) .WithOptionalDependent(c => c.MainUser) .WillCascadeOnDelete(false);
esto resolverá el problema de REFERENCIA y LLAVES EXTRANJERAS
al ACTUALIZAR o BORRAR un registro
fuente
Intente agregar el
ForeignKey
atributo a laLoyaltyUserDetail
propiedad:public class PIIUser { ... public int? LoyaltyUserDetailId { get; set; } [ForeignKey("LoyaltyUserDetailId")] public LoyaltyUserDetail LoyaltyUserDetail { get; set; } ... }
Y la
PIIUser
propiedad:public class LoyaltyUserDetail { ... public int PIIUserId { get; set; } [ForeignKey("PIIUserId")] public PIIUser PIIUser { get; set; } ... }
fuente
ForeignKey
atributo a solo un extremo de la relación? es decir, solo enPIIUser
oLoyaltyUserDetail
.Lo único que resulta confuso con las soluciones anteriores es que la clave principal se define como " Id" en ambas tablas y si tiene una clave principal basada en el nombre de la tabla, no funcionaría, he modificado las clases para ilustrar lo mismo, es decir, la tabla opcional no debería definir su propia clave primaria, sino que debería utilizar el mismo nombre de clave de la tabla principal.
public class PIIUser { // For illustration purpose I have named the PK as PIIUserId instead of Id // public int Id { get; set; } public int PIIUserId { get; set; } public int? LoyaltyUserDetailId { get; set; } public LoyaltyUserDetail LoyaltyUserDetail { get; set; } } public class LoyaltyUserDetail { // Note: You cannot define a new Primary key separately as it would create one to many relationship // public int LoyaltyUserDetailId { get; set; } // Instead you would reuse the PIIUserId from the primary table, and you can mark this as Primary Key as well as foreign key to PIIUser table public int PIIUserId { get; set; } public double? AvailablePoints { get; set; } public int PIIUserId { get; set; } public PIIUser PIIUser { get; set; } }
Y luego seguido de
Haría el truco, la solución aceptada no explica claramente esto, y me confundió durante unas horas para encontrar la causa
fuente
Esto no es de utilidad para el póster original, pero para cualquiera que todavía esté en EF6 y necesite que la clave externa sea diferente de la clave principal, aquí se explica cómo hacerlo:
public class PIIUser { public int Id { get; set; } //public int? LoyaltyUserDetailId { get; set; } public LoyaltyUserDetail LoyaltyUserDetail { get; set; } } public class LoyaltyUserDetail { public int Id { get; set; } public double? AvailablePoints { get; set; } public int PIIUserId { get; set; } public PIIUser PIIUser { get; set; } } modelBuilder.Entity<PIIUser>() .HasRequired(t => t.LoyaltyUserDetail) .WithOptional(t => t.PIIUser) .Map(m => m.MapKey("LoyaltyUserDetailId"));
Tenga en cuenta que no puede usar el
LoyaltyUserDetailId
campo porque, por lo que puedo decir, solo se puede especificar usando la API fluida. (He probado tres formas de hacerlo usando elForeignKey
atributo y ninguna funcionó).fuente