【Entity Framework】创建并配置模型
文章目录
- 【Entity Framework】创建并配置模型
- 一、概述
- 二、使用fluent API配置模型
- 三、分组配置
- 四、对实体类型使用`EntityTypeConfigurationAttribute`
- 四、使用数据注释来配置模型
- 五、实体类型
- 5.1 在模型中包含类型
- 5.2 从模型中排除类型
- 5.3 从迁移中排除
- 六、表名称与表架构
一、概述
EF Core使用元数据模型来描述如何将应用程序的实体类型映射到基础数据库。此模型是使用一组约定构建的,这些约定是寻找常用模式的启发式方法。然后,可以使用映射特性自定义模型和在OnModelCreating
中调用ModelBuilder
方法(也称为Fluent API),这两者都将替代约定执行的配置。
大多数配置可以应用于面向任何数据存储的模型。提供程序还可以启用特定于特定数据存储的配置,也可以忽略不支持或不适合的配置。
二、使用fluent API配置模型
可在派生上下文中替代OnModelCreating
方法,并使用Fluent API
类配置模型。此配置方法最为有效,并可在不修改实体类的情况下指定配置。此配置方法最为有效,并可在不修改实体类的情况下指定配置。Fluent API
配置具有最高优先级,并将替代约定和数据注释。配置按调用方法的顺序应用,如果存在任何冲突,最新调用将替代以前指定的配置。
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.FluentAPI.Required;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
#region Required
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
#endregion
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
三、分组配置
为了减少OnModelCreating
方法的大小,可以将实体类型的所有配置提取到实现IEntityTypeConfiguration<TEntity>
的单独类中。
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
然后,只需从 OnModelCreating
调用 Configure
方法
new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());
四、对实体类型使用EntityTypeConfigurationAttribute
与其显示调用Configure
,不如改为在实体类型上放置EntityTypeConfigurationAttribute
,以便EF Core可以查找并使用适当的配置。
[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Isbn { get; set; }
}
此特性意味着,每方模型中包含Book
实体类型时,EF Core都将使用指定的IEntityTypeConfiguration
实现。实体类型包含在使用普通机制其中一种机制的模型中,例如,通过为实体类型创建DbSet<TEntity>
属性:
public class BooksContext : DbContext
{
public DbSet<Book> Books { get; set; }
}
或者将其注册到OnModelCreating
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>();
}
四、使用数据注释来配置模型
可以将某些特性(称为数据注释)应用于类和属性。数据注释替代约定,但会被Fluent API
配置替代。
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.DataAnnotations.Annotations;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
[Table("Blogs")]
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
五、实体类型
在上下文中包含一种类型的DbSet意味着它包含在EF Core
的模型中;我们通常将此类类型称为实体。EF Core
可以从数据库中读取和写入实体实例,如果使用的是关系数据库,EF Core可以通过迁移为实例创建表。
5.1 在模型中包含类型
按照约定,上下文的DbSet属性中公开的类型作为实体包含在模型中。还包括在OnModelCreating
方法中指定的实体类型,以及通过递归探索其他发现的实体类型的导航属性找到的任何类型。
下面的代码示例中包含了所有类型:
- 包含
Blog
,因为它在上下文的 DbSet 属性中公开。 - 包含
Post
,因为它是通过Blog.Posts
导航属性发现的。 - 包含
AuditEntry
因为它是OnModelCreating
中指定的。
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditEntry>();
}
}
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; }
}
public class AuditEntry
{
public int AuditEntryId { get; set; }
public string Username { get; set; }
public string Action { get; set; }
}
5.2 从模型中排除类型
如果不希望在模型中包含某一类型,可以使用NotMapped
特性
[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
下面代码演示从Fluent API排除
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<BlogMetadata>();
}
5.3 从迁移中排除
将相同的实体类型映射到多个DbContext
类型中,非常有用。在使用绑定上下文时,尤其如此,对于每段绑定上下文,使用不同DbContext
类型的情况很常见。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}
此配置迁移不会创建AspNetUsers
该表,但IdentityUser
扔包含在模型中,并且可正常使用。
如果需要再次使用迁移来管理表,则应创建不包括AspNetUsers
的新迁移。下一次迁移将包含对表所做的任何更改。
六、表名称与表架构
按照约定,每个实体类型都将设置为映射到公开实体的DbSet属性名称相同的数据库表,如果给定实体不存在DbSet,则使用类名称。
可以手动配置表名:
[Table("blogs")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs");
}
使用关系数据库时,表按约定在数据库的默认架构中创建
[Table("blogs", Schema = "blogging")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Fluent API
[Table("blogs", Schema = "blogging")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}