entity framework - name - Código EF Primera clave externa sin propiedad de navegación
fluent api entity framework foreign key (3)
Aunque esta publicación es para Entity Framework
no Entity Framework Core
, podría ser útil para alguien que quiera lograr lo mismo utilizando Entity Framework Core (estoy usando V1.1.2).
No necesito propiedades de navegación (aunque son agradables) porque estoy practicando DDD y quiero que Parent
e Child
sean dos raíces agregadas separadas. Quiero que puedan hablar entre ellos a través de una clave externa, no a través de las propiedades de navegación de Entity Framework
específicas de la infraestructura.
Todo lo que tienes que hacer es configurar la relación en un lado.
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
......
builder.Entity<Parent>(e => {
e.HasKey(x => x.Id);
e.ToTable("Parent");
});
builder.Entity<Child>(e => {
e.HasKey(x => x.Id);
e.Property(x => x.ParentId).IsRequired();
e.HasOne<Parent>()
.WithMany()
.HasForeignKey(x => x.ParentId);
e.ToTable("Child");
});
......
}
}
Estoy dando ejemplos sobre cómo configurar las propiedades de la entidad también, pero la más importante aquí es HasOne<>
, WithMany()
y HasForeignKey()
.
Espero eso ayude.
Digamos que tengo las siguientes entidades:
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
}
¿Cuál es la primera sintaxis de API fluida del código para exigir que ParentId se cree en la base de datos con una restricción de clave externa en la tabla Padres, sin la necesidad de tener una propiedad de navegación ?
Sé que si agrego una propiedad de navegación de padre a hijo, puedo hacer esto:
modelBuilder.Entity<Child>()
.HasRequired<Parent>(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
Pero no quiero la propiedad de navegación en este caso particular.
Con EF Code First Fluent API es imposible. Siempre necesita al menos una propiedad de navegación para crear una restricción de clave externa en la base de datos.
Si está utilizando Code First Migrations, tiene la opción de agregar una nueva migración basada en código en la consola del administrador de paquetes ( add-migration SomeNewSchemaName
). Si cambió algo con su modelo o mapeo, se agregará una nueva migración. Si no add-migration -IgnoreChanges SomeNewSchemaName
nada, forza una nueva migración usando add-migration -IgnoreChanges SomeNewSchemaName
. La migración solo contendrá métodos vacíos Up
y Down
en este caso.
Luego puede modificar el método Up
añadiéndole lo siguiente:
public override void Up()
{
// other stuff...
AddForeignKey("ChildTableName", "ParentId", "ParentTableName", "Id",
cascadeDelete: true); // or false
CreateIndex("ChildTableName", "ParentId"); // if you want an index
}
Al ejecutar esta migración ( update-database
en la consola de administración de paquetes) se ejecutará una declaración SQL similar a esta (para SQL Server):
ALTER TABLE [ChildTableName] ADD CONSTRAINT [FK_SomeName]
FOREIGN KEY ([ParentId]) REFERENCES [ParentTableName] ([Id])
CREATE INDEX [IX_SomeName] ON [ChildTableName] ([ParentId])
Alternativamente, sin migraciones, podría simplemente ejecutar un comando SQL puro usando
context.Database.ExecuteSqlCommand(sql);
donde context
es una instancia de su clase de contexto derivado y sql
es solo el comando SQL anterior como cadena.
Tenga en cuenta que con todo esto EF no tiene ni idea de que ParentId
es una clave externa que describe una relación. EF lo considerará solo como una propiedad escalar ordinaria. De alguna manera, todo lo anterior es solo una forma más complicada y lenta en comparación con solo abrir una herramienta de administración de SQL y agregar la restricción manualmente.
Una pequeña sugerencia para aquellos que desean usar DataAnotations y no quieren exponer la propiedad de navegación - use protected
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
protected virtual Parent Parent { get; set; }
}
Eso es todo: la clave externa con cascade:true
después Add-Migration
se haya creado Add-Migration
.