热门标签 | 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官方文档:创建模型


推荐阅读
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 从0到1搭建大数据平台
    从0到1搭建大数据平台 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 【实例简介】本文详细介绍了如何在PHP中实现微信支付的退款功能,并提供了订单创建类的完整代码及调用示例。在配置过程中,需确保正确设置相关参数,特别是证书路径应根据项目实际情况进行调整。为了保证系统的安全性,存放证书的目录需要设置为可读权限。值得注意的是,普通支付操作无需证书,但在执行退款操作时必须提供证书。此外,本文还对常见的错误处理和调试技巧进行了说明,帮助开发者快速定位和解决问题。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 通过将常用的外部命令集成到VSCode中,可以提高开发效率。本文介绍如何在VSCode中配置和使用自定义的外部命令,从而简化命令执行过程。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 本文介绍了如何利用HTTP隧道技术在受限网络环境中绕过IDS和防火墙等安全设备,实现RDP端口的暴力破解攻击。文章详细描述了部署过程、攻击实施及流量分析,旨在提升网络安全意识。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • CentOS 7 中 iptables 过滤表实例与 NAT 表应用详解
    在 CentOS 7 系统中,iptables 的过滤表和 NAT 表具有重要的应用价值。本文通过具体实例详细介绍了如何配置 iptables 的过滤表,包括编写脚本文件 `/usr/local/sbin/iptables.sh`,并使用 `iptables -F` 清空现有规则。此外,还深入探讨了 NAT 表的配置方法,帮助读者更好地理解和应用这些网络防火墙技术。 ... [详细]
  • .NET Core 托管服务优化与实践
    在.NET Core应用中,托管服务的形式主要分为进程内托管(InProcess)和进程外托管(OutOfProcess)。这两种托管方式各有优缺点,本文将深入探讨它们的特点,并结合实际案例,介绍如何根据具体需求选择合适的托管模式,以实现性能优化和资源利用的最大化。此外,文章还将分享一些实用的配置技巧和最佳实践,帮助开发者提升应用的稳定性和可维护性。 ... [详细]
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社区 版权所有