热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

EFCore配置模型

0前言本文的第一节,会概述配置模型的作用(对数据模型的补充描述)。第二节描述两种配置方式,即:数据注释(dataannotations)和FluentAPI方式。第三节开始,主要是

0 前言

本文的第一节,会概述配置模型的作用(对数据模型的补充描述)。

第二节描述两种配置方式,即:数据注释(data annotations)和 Fluent API 方式。

第三节开始,主要是将常用的配置记录下来,以便翻查。


1 概述

数据实体(Entity)的类名、属性等,称之为约定(conventions),约定主要是为了定义数据模型(Model)的形状。

但是光靠约定可能不足以完整描述数据模型,有时我们的数据模型与我们的数据实体可能也有差异,这时,就可以通过数据注释(data annotations)和 Fluent API 补充,具体请参考EF Core官方文档:创建并配置模型。


2 配置方式

2.1 数据注释(data annotations)

直接在数据实体上打上对应的标签,如下例子中,标识表名为 Blogs,Url 属性不能为 null

[Table("Blogs")]
public class Blog
{
    public int BlogId { get; set; }

    [Required]
    public string Url { get; set; }
}

注意:数据注释的方式的优先级高于约定(conventions)但低于 Fluent API,即数据注释的方式会被 Fluent API 覆盖。

2.2 Fluent API

对描述数据模型(Model)具有最高优先级。

通过在派生的 DbContext 中,重写 OnModelCreating 方法,并使用 ModelBuilder API 来配置模型。

internal class MyContext : DbContext
{
    public DbSet Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 写法1:链式配置
        modelBuilder.Entity()
            .ToTable("Blogs")
            .Property(b => b.Url).IsRequired();
        
        // 写法2:委托函数配置
        modelBuilder.Entity(eb => 
        {
            eb.ToTable("Posts");
            eb.Property(b => b.Title).IsRequired();
        });
    }
}

2.2.1 分组配置

可以实现类似于批量配置,具体请参考分组配置。

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);

注意:应用配置的顺序是不确定的,因此仅当顺序不重要时才应使用此方法。

3 配置数据模型

3.1 在模型中包含类型

在上下文中包含于 DbSet 的类意味着它包含在 EF Core 的模型中;我们通常将这些类称为实体。 EF Core 可以向数据库中读写实体实例,如果使用的是关系数据库,EF Core 可以通过迁移为实体创建表。

3.1.1 迁移时,创建表的情况

使用 EF Core 添加迁移时,哪些实体会被创建表呢?包含以下三种情况:

  1. 在 DbContext 的 DbSet 属性中公开的实体类
  2. 在 DbContext 的 OnModelCreating 方法中指定的实体类
  3. 以上两种情况的实体类内,通过递归探索导航属性发现的实体类

下面是一个官方示例:

下面的代码示例中,数据模型中包含的实体类有:

  • 包含 Blog,因为它在上下文的 DbSet 属性中公开。
  • 包含 Post,因为它是通过 Blog.Posts 导航属性发现的。
  • 包含 AuditEntry因为它是 OnModelCreating 中指定的。
internal class MyContext : DbContext
{
    public DbSet Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List 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; }
}

3.2 配置实体类型(Entity types)

3.2.1 数据注释(data annotations)

//从模型(model)中排除类型(class)
[NotMapped]

//指定表名称
[Table("blogs")]
//指定架构(scheme)
[Table("blogs", Schema = "blogging")]

//表注释
[Comment("Blogs managed on the website")]

3.2.2 Fluent API

//从模型(model)中排除类型(class)
modelBuilder.Ignore();
//从迁移中排除,生成迁移将不会包含 表AspNetUsers,但 IdentityUser 仍在模型中
modelBuilder.Entity().ToTable("AspNetUsers", t => t.ExcludeFromMigrations());

//指定表名称
modelBuilder.Entity().ToTable("blogs");
//指定架构(scheme)
modelBuilder.Entity().ToTable("blogs", schema: "blogging");
//通用配置:默认架构名
modelBuilder.HasDefaultSchema("blogging");

//视图映射
//映射到视图将删除默认表映射,但从 EF 5.0 开始,实体类型也可以显式映射到表。 在这种情况下,查询映射将用于查询,表映射将用于更新。
modelBuilder.Entity().ToView("blogsView", schema: "blogging");

//表注释
modelBuilder.Entity().HasComment("Blogs managed on the website");

//表值函数映射
modelBuilder.Entity().HasNoKey().ToFunction("BlogsWithMultiplePosts");

//共享类型实体(Shared-type entity types)
//不理解
//https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=data-annotations#shared-type-entity-types

3.3 配置实体属性(Entity properties)

3.3.1 数据注释(data annotations)

//排除属性
[NotMapped]
//备注
[Comment("The URL of the blog")]

[Column("blog_id")]
[Column(TypeName = "varchar(200)")]
[MaxLength(500)]

//精度和小数位
[Precision(14, 2)]
public decimal Score { get; set; }
[Precision(3)]
public DateTime LastUpdated { get; set; }

//nvarchar 表示 Unicode 数据,varchar 表示非 Unicode 数据
[Unicode(false)]
[Required]

//列排序
//默认情况下,在使用迁移创建表时,EF Core 首先为主键列排序,然后为实体类型和从属类型的属性排序,最后为基类型中的属性排序。(顺序:主键 >> 属性 >> 基类属性)
//在一般情况下,大多数数据库仅支持在创建表时对列进行排序。 这意味着不能使用列顺序特性对现有表中的列进行重新排序。
[Column(Order = 0)]
[Column(Order = 1)]

3.3.2 Fluent API

//排除属性
modelBuilder.Entity().Ignore(b => b.LoadedFromDatabase);
//备注
modelBuilder.Entity().Property(b => b.Url).HasComment("The URL of the blog");

modelBuilder.Entity().Property(b => b.BlogId).HasColumnName("blog_id");
modelBuilder.Entity().Property(b => b.Url).HasColumnType("varchar(200)");
modelBuilder.Entity().Property(b => b.Url).HasMaxLength(500);

//精度和小数位
modelBuilder.Entity().Property(b => b.Score).HasPrecision(14, 2);
modelBuilder.Entity().Property(b => b.LastUpdated).HasPrecision(3);

//nvarchar 表示 Unicode 数据,varchar 表示非 Unicode 数据
modelBuilder.Entity().Property(b => b.Isbn).IsUnicode(false);
modelBuilder.Entity().Property(b => b.Url).IsRequired();

//可以定义文本列的排序规则,以确定如何比较和排序。 
//排序规则:https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/collations-and-case-sensitivity
//例如,以下代码片段将 SQL Server 列配置为不区分大小写
modelBuilder.Entity().Property(c => c.Name)
    .UseCollation("SQL_Latin1_General_CP1_CI_AS");

modelBuilder.Entity().Property(b => b.Id).HasColumnOrder(0);
modelBuilder.Entity().Property(b => b.FirstName).HasColumnOrder(1);

3.4 配置主键、外键、索引

3.4.1 数据注释

//主键
[Key]
//无主键
[Keyless]

//外键
[ForeignKey]
//反向属性:https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships?tabs=data-annotations%2Cfluent-api-simple-key%2Csimple-key#manual-configuration
[InverseProperty("Author")]

//索引
[Index(nameof(Url))]
public class Blog 
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
//复合索引
[Index(nameof(FirstName), nameof(LastName))]
//唯一索引
[Index(nameof(Url), IsUnique = true)]
//索引名称
[Index(nameof(Url), Name = "Index_Url")]

3.4.2 Fluent API

//主键
modelBuilder.Entity().HasKey(c => c.LicensePlate);
//复合主键
modelBuilder.Entity().HasKey(c => new { c.State, c.LicensePlate });
//无主键
modelBuilder.Entity().HasNoKey();

//备选键:https://docs.microsoft.com/zh-cn/ef/core/modeling/keys?tabs=fluent-api#alternate-keys
//备选键 HasPrincipalKey
modelBuilder.Entity()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey(p => p.BlogUrl)
            .HasPrincipalKey(b => b.Url);
//将单个属性配置为备选键
modelBuilder.Entity()
        .HasAlternateKey(c => c.LicensePlate);
//复合备选键
modelBuilder.Entity()
        .HasAlternateKey(c => new { c.State, c.LicensePlate });
//配置备选键的索引和唯一约束的名称
modelBuilder.Entity()
        .HasAlternateKey(c => c.LicensePlate)
        .HasName("AlternateKey_LicensePlate");

//配置关系
modelBuilder.Entity()
    		.HasOne(p => p.Blog)
    		.WithMany(b => b.Posts);
//配置外键
modelBuilder.Entity()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey(p => p.BlogForeignKey);
//配置外键约束的名称
modelBuilder.Entity()
        .HasOne(p => p.Blog)
        .WithMany(b => b.Posts)
        .HasForeignKey(p => p.BlogId)
        .HasConstraintName("ForeignKey_Post_Blog");
//关系:https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships

//索引
modelBuilder.Entity().HasIndex(b => b.Url);
modelBuilder.Entity().HasIndex(p => new { p.FirstName, p.LastName });
//唯一索引
modelBuilder.Entity().HasIndex(b => b.Url).IsUnique();
//索引名称
modelBuilder.Entity().HasIndex(b => b.Url).HasDatabaseName("Index_Url");
//索引筛选器:https://docs.microsoft.com/zh-cn/ef/core/modeling/indexes?tabs=fluent-api#index-filter
modelBuilder.Entity().HasIndex(b => b.Url).HasFilter("[Url] IS NOT NULL");
//包含列:SQL Server 的 Include 关键字
modelBuilder.Entity().HasIndex(p => p.Url)
        .IncludeProperties(p => new { p.Title, p.PublishedOn });
//检查约束
modelBuilder.Entity().HasCheckConstraint("CK_Prices", "[Price] > [DiscountedPrice]", c => c.HasName("CK_Product_Prices"));

3.5 值转换(value conversions)

3.5.1 基本配置

假设将一个枚举和实体类型定义为:

public class Rider
{
    public int Id { get; set; }
    public EquineBeast Mount { get; set; }
}

public enum EquineBeast
{
    Donkey,
    Mule,
    Horse,
    Unicorn
}

可以将枚举值(如字符串 "Donkey"、"Mule"等)存储在数据库中。

需要配置两个函数:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity()
        .Property(e => e.Mount)
        .HasConversion(
            v => v.ToString(),
            v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
}

3.5.2 批量配置

public class CurrencyConverter : ValueConverter
{
    public CurrencyConverter()
        : base(
            v => v.Amount,
            v => new Currency(v))
    {
    }
}

配置

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder
        .Properties()
        .HaveConversion();
}

3.5.3 ValueConverter 类

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var cOnverter= new ValueConverter(
        v => v.ToString(),
        v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));

    modelBuilder
        .Entity()
        .Property(e => e.Mount)
        .HasConversion(converter);
}

3.5.4 内置转换器

下面是一个示例,更多的请翻查官方文档:内置转换器

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity()
        .Property(e => e.Mount)
        .HasConversion();
}

4 数据种子(data seeding)

OnModelCreating 中配置种子数据:

modelBuilder.Entity().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });

匿名对象:

modelBuilder.Entity().HasData(
    new { BlogId = 1, PostId = 2, Title = "Second post", COntent= "Test 2" });

多行数据

modelBuilder.Entity().OwnsOne(p => p.AuthorName).HasData(
    new { PostId = 1, First = "Andriy", Last = "Svyryd" },
    new { PostId = 2, First = "Diego", Last = "Vega" });

参考来源

EF Core官方文档:创建模型


推荐阅读
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文比较了eBPF和WebAssembly作为云原生VM的特点和应用领域。eBPF作为运行在Linux内核中的轻量级代码执行沙箱,适用于网络或安全相关的任务;而WebAssembly作为图灵完备的语言,在商业应用中具有优势。同时,介绍了WebAssembly在Linux内核中运行的尝试以及基于LLVM的云原生WebAssembly编译器WasmEdge Runtime的案例,展示了WebAssembly作为原生应用程序的潜力。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文详细介绍了PHP中与URL处理相关的三个函数:http_build_query、parse_str和查询字符串的解析。通过示例和语法说明,讲解了这些函数的使用方法和作用,帮助读者更好地理解和应用。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
author-avatar
___周麗
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有