Relaciones
Configuración de propiedades de navegación
Nota
Esta característica se incluyó por primera vez en EF Core 5.0.
Una vez creada la propiedad de navegación, es posible que tenga que configurarla aún más.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne();
modelBuilder.Entity<Blog>()
.Navigation(b => b.Posts)
.UsePropertyAccessMode(PropertyAccessMode.Property);
}
Nota
Esta llamada no se puede usar para crear una propiedad de navegación. Solo se usa para configurar una propiedad de navegación que se ha creado previamente mediante la definición de una relación o a partir de una convención.
Clave externa
Puede usar la API Fluent para configurar qué propiedad debe usarse como propiedad de clave externa para una relación determinada:
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogForeignKey);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogForeignKey { get; set; }
public Blog Blog { get; set; }
}
Sombra de clave externa
Puede usar la sobrecarga de cadena de para configurar una propiedad de sombra como HasForeignKey(...) una clave externa (vea Propiedades de HasForeignKey(...) para obtener más información). Se recomienda agregar explícitamente la propiedad shadow al modelo antes de usarlo como clave externa (como se muestra a continuación).
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Add the shadow property to the model
modelBuilder.Entity<Post>()
.Property<int>("BlogForeignKey");
// Use the shadow property as a foreign key
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey("BlogForeignKey");
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; }
}
Nombre de restricción de clave externa
Por convención, al tener como destino una base de datos relacional, las restricciones de clave externa se denominan FK_ nombre de tipo dependiente _ nombre de tipo principal _ nombre de propiedad <> de clave externa <><> . Para las claves externas compuestas, el nombre de propiedad de clave < externa se convierte en una lista separada por caracteres de subrayado de nombres de propiedad de clave > externa.
También puede configurar el nombre de restricción de la siguiente manera:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.HasConstraintName("ForeignKey_Post_Blog");
}
Sin propiedad de navegación
No es necesario proporcionar necesariamente una propiedad de navegación. Simplemente puede proporcionar una clave externa en un lado de la relación.
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne<Blog>()
.WithMany()
.HasForeignKey(p => p.BlogId);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
}
Clave principal
Si desea que la clave externa haga referencia a una propiedad que no sea la clave principal, puede usar la API Fluent para configurar la propiedad de clave principal para la relación. La propiedad que configure como clave principal se configurará automáticamente como una clave alternativa.
internal class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RecordOfSale>()
.HasOne(s => s.Car)
.WithMany(c => c.SaleHistory)
.HasForeignKey(s => s.CarLicensePlate)
.HasPrincipalKey(c => c.LicensePlate);
}
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public List<RecordOfSale> SaleHistory { get; set; }
}
public class RecordOfSale
{
public int RecordOfSaleId { get; set; }
public DateTime DateSold { get; set; }
public decimal Price { get; set; }
public string CarLicensePlate { get; set; }
public Car Car { get; set; }
}
Relaciones obligatorias y opcionales
Puede usar la API Fluent para configurar si la relación es obligatoria u opcional. En última instancia, esto controla si la propiedad de clave externa es obligatoria u opcional. Esto resulta muy útil cuando se usa una clave externa de estado de sombra. Si tiene una propiedad de clave externa en la clase de entidad, la obligación de la relación se determina en función de si la propiedad de clave externa es obligatoria u opcional (vea Propiedades obligatorias y opcionales para obtener más información).
Las propiedades de clave externa se encuentran en el tipo de entidad dependiente, por lo que si están configuradas como necesarias, significa que todas las entidades dependientes deben tener una entidad principal correspondiente.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.IsRequired();
}
Nota
La IsRequired(false) llamada a también hace que la propiedad de clave externa sea opcional a menos que se configure de lo contrario.
Eliminación en cascada
Puede usar la API Fluent para configurar explícitamente el comportamiento de eliminación en cascada para una relación determinada.
Consulte Eliminación en cascada para obtener una explicación detallada de cada opción.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.OnDelete(DeleteBehavior.Cascade);
}
Otros patrones de relación
Uno a uno
Las relaciones de uno a uno tienen una propiedad de navegación de referencia en ambos lados. Siguen las mismas convenciones que las relaciones uno a varios, pero se introduce un índice único en la propiedad de clave externa para asegurarse de que solo un dependiente está relacionado con cada entidad de seguridad.
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public BlogImage BlogImage { get; set; }
}
public class BlogImage
{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Nota
EF elegirá una de las entidades dependientes en función de su capacidad para detectar una propiedad de clave externa. Si se elige la entidad incorrecta como dependiente, puede usar la API Fluent para corregirlo.
Al configurar la relación con la API Fluent, use los métodos HasOneWithOne y .
Al configurar la clave externa, debe especificar el tipo de entidad dependiente: observe el parámetro genérico proporcionado en HasForeignKey la lista siguiente. En una relación uno a varios, está claro que la entidad con la navegación de referencia es dependiente y la que tiene la colección es la entidad de seguridad. Pero esto no es así en una relación uno a uno, de ahí la necesidad de definirla explícitamente.
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogImage> BlogImages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public BlogImage BlogImage { get; set; }
}
public class BlogImage
{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; }
public int BlogForeignKey { get; set; }
public Blog Blog { get; set; }
}
El lado dependiente se considera opcional de forma predeterminada, pero se puede configurar según sea necesario. Sin embargo, EF no validará si se proporcionó una entidad dependiente, por lo que esta configuración solo marcará una diferencia cuando la asignación de base de datos permita que se aplique. Un escenario común para esto son los tipos de propiedad de referencia que usan la división de tablas de forma predeterminada.
modelBuilder.Entity<Order>(
ob =>
{
ob.OwnsOne(
o => o.ShippingAddress,
sa =>
{
sa.Property(p => p.Street).IsRequired();
sa.Property(p => p.City).IsRequired();
});
ob.Navigation(o => o.ShippingAddress)
.IsRequired();
});
Con esta configuración, las columnas ShippingAddress correspondientes a se marcarán como no acepta valores NULL en la base de datos.
Nota
Si usa tipos de referencia que no aceptan valores NULL, no es necesario llamar a .
Nota
La capacidad de configurar si el dependiente es necesario se introdujo en EF Core 5.0.
Varios a varios
Muchas o muchas relaciones requieren una propiedad de navegación de colección en ambos lados. Se detectarán por convención como otros tipos de relaciones.
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
}
La manera en que esta relación se implementa en la base de datos es mediante una tabla de combinación que contiene claves externas para Post y Tag . Por ejemplo, esto es lo que EF creará en una base de datos relacional para el modelo anterior.
CREATE TABLE [Posts] (
[PostId] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Content] nvarchar(max) NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId])
);
CREATE TABLE [Tags] (
[TagId] nvarchar(450) NOT NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([TagId])
);
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] nvarchar(450) NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([PostId]) ON DELETE CASCADE,
CONSTRAINT [FK_PostTag_Tags_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([TagId]) ON DELETE CASCADE
);
Internamente, EF crea un tipo de entidad para representar la tabla de combinación a la que se hará referencia como tipo de entidad de combinación. Dictionary<string, object> actualmente se usa para controlar cualquier combinación de propiedades de clave externa; consulte tipos de entidad de bolsa Dictionary<string, object> para obtener más información. Puede haber más de una relación de varios a varios en el modelo, por lo que al tipo de entidad de combinación se le debe dar un nombre único, en este caso PostTag . La característica que lo permite se denomina tipo de entidad de tipo compartido.
Importante
El tipo CLR usado para los tipos de entidad de combinación por convención puede cambiar en versiones futuras para mejorar el rendimiento. No dependa de que el tipo de combinación sea a menos que se haya configurado Dictionary<string, object> explícitamente, como se describe en la sección siguiente.
Las navegacións de varios a varios se denominan navegación de skip, ya que omiten eficazmente el tipo de entidad de combinación. Si usa la configuración masiva, todas las navegacións de skip se pueden obtener en GetSkipNavigations.
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var skipNavigation in entityType.GetSkipNavigations())
{
Console.WriteLine(entityType.DisplayName() + "." + skipNavigation.Name);
}
}
Configuración de tipo de entidad de combinación
Es habitual aplicar la configuración al tipo de entidad de combinación. Esta acción se puede realizar a través de UsingEntity .
modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity(j => j.ToTable("PostTags"));
Los datos de ed. de modelo se pueden proporcionar para el tipo de entidad de combinación mediante tipos anónimos. Puede examinar la vista de depuración del modelo para determinar los nombres de propiedad creados por convención.
modelBuilder
.Entity<Post>()
.HasData(new Post { PostId = 1, Title = "First" });
modelBuilder
.Entity<Tag>()
.HasData(new Tag { TagId = "ef" });
modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity(j => j.HasData(new { PostsPostId = 1, TagsTagId = "ef" }));
Se pueden almacenar datos adicionales en el tipo de entidad de combinación, pero para ello es mejor crear un tipo CLR a medida. Al configurar la relación con un tipo de entidad de combinación personalizada, ambas claves externas deben especificarse explícitamente.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Configuración de relaciones de unión
EF usa dos relaciones uno a varios en el tipo de entidad de combinación para representar la relación de varios a varios. Puede configurar estas relaciones en los UsingEntity argumentos.
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<Dictionary<string, object>>(
"PostTag",
j => j
.HasOne<Tag>()
.WithMany()
.HasForeignKey("TagId")
.HasConstraintName("FK_PostTag_Tags_TagId")
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne<Post>()
.WithMany()
.HasForeignKey("PostId")
.HasConstraintName("FK_PostTag_Posts_PostId")
.OnDelete(DeleteBehavior.ClientCascade));
Nota
La capacidad de configurar relaciones de varios a varios se introdujo en EF Core 5.0, para la versión anterior, use el enfoque siguiente.
Relaciones indirectas de varios a varios
También puede representar una relación de varios a varios simplemente agregando el tipo de entidad de combinación y asignando dos relaciones uno a varios independientes.
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Nota
Todavía no se ha agregado compatibilidad con la aplicación de scaffolding a relaciones de varios a varios desde la base de datos. Vea la incidencia de seguimiento.
Recursos adicionales
- EF Core Community sesión de standup, con un análisis profundo de varios a varios y la infraestructura que la subyuta.
Una relación define cómo se relacionan dos entidades entre sí. En una base de datos relacional, esto se representa mediante una restricción de clave externa.
Nota
La mayoría de los ejemplos de este artículo usan una relación uno a varios para demostrar conceptos. Para obtener ejemplos de relaciones uno a uno y varios a varios, consulte la sección Otros patrones de relación al final del artículo.
Definición de términos
Hay una serie de términos que se usan para describir las relaciones.
Entidad dependiente: Esta es la entidad que contiene las propiedades de clave externa. A veces se conoce como "secundario" de la relación.
Entidad principal: Esta es la entidad que contiene las propiedades de clave principal o alternativa. A veces se conoce como "primario" de la relación.
Clave principal: Propiedades que identifican de forma única la entidad principal. Puede ser la clave principal o una clave alternativa.
Clave externa: Propiedades de la entidad dependiente que se usan para almacenar los valores de clave principal de la entidad relacionada.
Propiedad de navegación: Propiedad definida en la entidad principal o dependiente que hace referencia a la entidad relacionada.
Propiedad de navegación de la colección: Propiedad de navegación que contiene referencias a muchas entidades relacionadas.
Propiedad de navegación de referencia: Propiedad de navegación que contiene una referencia a una sola entidad relacionada.
Propiedad de navegación inversa: Al analizar una propiedad de navegación determinada, este término hace referencia a la propiedad de navegación en el otro extremo de la relación.
Relación de autoferencia: Relación en la que los tipos de entidad dependiente y principal son iguales.
El código siguiente muestra una relación uno a varios entre y . BlogPost
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Postes la entidad dependienteBloges la entidad principalBlog.BlogIdes la clave principal (en este caso es una clave principal en lugar de una clave alternativa).Post.BlogIdes la clave externaPost.Bloges una propiedad de navegación de referenciaBlog.Postses una propiedad de navegación de colecciónPost.Bloges la propiedad de navegación inversaBlog.Postsde (y viceversa)
Convenciones
De forma predeterminada, se creará una relación cuando haya una propiedad de navegación detectada en un tipo. Una propiedad se considera una propiedad de navegación si el proveedor de base de datos actual no puede asignar el tipo al que apunta como un tipo escalar.
Nota
Las relaciones detectadas por convención siempre tendrán como destino la clave principal de la entidad principal. Para establecer como destino una clave alternativa, se debe realizar una configuración adicional mediante Fluent API.
Relaciones totalmente definidas
El patrón más común para las relaciones es tener definidas propiedades de navegación en ambos extremos de la relación y una propiedad de clave externa definida en la clase de entidad dependiente.
Si se encuentra un par de propiedades de navegación entre dos tipos, se configurarán como propiedades de navegación inversas de la misma relación.
Si la entidad dependiente contiene una propiedad con un nombre que coincida con uno de estos patrones, se configurará como clave externa:
<navigation property name><principal key property name><navigation property name>Id<principal entity name><principal key property name><principal entity name>Id
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
En este ejemplo se usarán las propiedades resaltadas para configurar la relación.
Nota
Si la propiedad es la clave principal o es de un tipo no compatible con la clave principal, no se configurará como clave externa.
Ninguna propiedad de clave externa
Aunque se recomienda tener una propiedad de clave externa definida en la clase de entidad dependiente, no es necesaria. Si no se encuentra ninguna propiedad de clave externa, se introducirá una propiedad shadow foreign key con el nombre o si no hay ninguna navegación en el tipo <principal entity name><principal key property name> dependiente.
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; }
}
En este ejemplo, la sombra de la clave externa se debe a que anteponer BlogId el nombre de navegación sería redundante.
Nota
Si ya existe una propiedad con el mismo nombre, el nombre de la propiedad de sombra tendrá el sufijo con un número.
Propiedad de navegación única
Incluir solo una propiedad de navegación (sin navegación inversa y ninguna propiedad de clave externa) es suficiente para tener una relación definida por convención. También puede tener una propiedad de navegación única y una propiedad de clave externa.
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
Limitaciones
Cuando hay varias propiedades de navegación definidas entre dos tipos (es decir, más de un par de navegación que apuntan entre sí), las relaciones representadas por las propiedades de navegación son ambiguas. Deberá configurarlos manualmente para resolver la ambigüedad.
Eliminación en cascada
Por convención, la eliminación en cascada se establecerá en Cascade para las relaciones necesarias y ClientSetNull para las relaciones opcionales. En cascada significa que también se eliminan las entidades dependientes. ClientSetNull significa que las entidades dependientes que no se cargan en la memoria permanecerán sin cambios y deben eliminarse manualmente o actualizarse para que apunten a una entidad de seguridad válida. En el caso de las entidades que se cargan en memoria, EF Core intentará establecer las propiedades de clave externa en NULL.
Consulte la sección Relaciones obligatorias y opcionales para ver la diferencia entre las relaciones obligatorias y opcionales.
Consulte Eliminación en cascada para obtener más detalles sobre los distintos comportamientos de eliminación y los valores predeterminados usados por convención.
Configuración manual
Para configurar una relación en la API Fluent, empiece por identificar las propiedades de navegación que la integran. HasOne o HasMany identifica la propiedad de navegación en el tipo de entidad en el que está empezando la configuración. A continuación, encadena una WithOne llamada a o para identificar la navegación WithMany inversa. HasOne/WithOnese usan para las propiedades de navegación de referencia HasMany/WithMany y se usan para las propiedades de navegación de colección.
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; }
}
Propiedad de navegación única
Si solo tiene una propiedad de navegación, hay sobrecargas sin parámetros de WithOne y WithMany . Esto indica que conceptualmente hay una referencia o colección en el otro extremo de la relación, pero no hay ninguna propiedad de navegación incluida en la clase de entidad.
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}