关系的映射属性(也称为数据注释)

映射属性用于修改或替代模型生成约定发现的配置。 映射属性执行的配置本身可由 OnModelCreating 中使用的模型生成 API 所替代。

重要

本文档仅介绍关系配置上下文中的映射属性。 内容更广泛的建模文档的相关部分介绍了映射属性的其他用法。

提示

可在 MappingAttributes.cs 中找到以下代码。

获取映射属性的位置

许多映射属性来自 System.ComponentModel.DataAnnotationsSystem.ComponentModel.DataAnnotations.Schema 命名空间。 这些命名空间中的属性作为基础框架的一部分包含在所有受支持的 .NET 版本中,因此不需要安装任何其他 NuGet 包。 这些映射属性通常被称为“数据注释”,供各种框架使用,包括 EF Core、EF6、ASP.NET Core MVC 等。 它们也用于验证。

跨多种技术以及用于映射和验证的数据注释导致了不同技术之间的语义差异。 专为 EF Core 设计的所有新映射属性现在均特定于 EF Core,从而保持其语义和用途简单明了。 这些属性包含在 Microsoft.EntityFrameworkCore.Abstractions NuGet 包中。 每当使用主要的 Microsoft.EntityFrameworkCore 包或某个关联的数据库提供程序包时,此包都将作为依赖项包含在内。 但是,抽象包是一个轻型包,可由应用程序代码直接引用,而无需引入所有 EF Core 及其依赖项。

RequiredAttribute

RequiredAttribute 应用于属性,用于指示该属性不能为 null。 在关系上下文中,[Required] 通常用于外键属性。 执行此操作会让外键不可为空,因而使关系成为必需。 例如,对于以下类型,Post.BlogId 属性将变为不可为空,关系将成为必需。

public class Blog
{
    public string Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }

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

    public Blog Blog { get; init; }
}

注意

使用 C# 不可为空引用类型时,此示例中的 BlogId 属性已变为不可为空,这意味着 [Required] 属性将没有影响。

置于依赖导航上的 [Required] 具有相同的效果。 也就是说,让外键不可为空,从而使关系成为必需。 例如:

public class Blog
{
    public string Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }

    public string BlogId { get; set; }

    [Required]
    public Blog Blog { get; init; }
}

如果在依赖导航上找到 [Required],且外键属性处于阴影状态,则阴影属性不可为空,从而使关系成为必需。 例如:

public class Blog
{
    public string Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }

    [Required]
    public Blog Blog { get; init; }
}

注意

在关系的主体导航端上使用 [Required] 不起作用。

ForeignKeyAttribute

ForeignKeyAttribute 用于将外键属性与其导航连接。 [ForeignKey] 可以放在具有依赖导航名称的外键属性上。 例如:

public class Blog
{
    public string Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }

    [ForeignKey(nameof(Blog))]
    public string BlogKey { get; set; }

    public Blog Blog { get; init; }
}

或者,可以将 [ForeignKey] 放在依赖导航或主体导航上,其名称属性要用作外键。 例如:

public class Blog
{
    public string Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }

    public string BlogKey { get; set; }

    [ForeignKey(nameof(BlogKey))]
    public Blog Blog { get; init; }
}

[ForeignKey] 置于导航之上,并且提供的名称与任何属性名称都不匹配时,将创建一个具有该名称的阴影属性来充当外键。 例如:

public class Blog
{
    public string Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }

    [ForeignKey("BlogKey")]
    public Blog Blog { get; init; }
}

InversePropertyAttribute

InversePropertyAttribute 用于将导航与其逆向导航相连接。 例如,在以下实体类型中,BlogPost 之间有两种关系。 如果不进行任何配置,EF 约定将无法确定两种类型之间的哪些导航应该配对。 将 [InverseProperty] 添加到其中一个配对导航可以消除这种不确定性,并允许 EF 生成模型。

public class Blog
{
    public int Id { get; set; }

    [InverseProperty("Blog")]
    public List<Post> Posts { get; } = new();

    public int FeaturedPostId { get; set; }
    public Post FeaturedPost { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; }

    public Blog Blog { get; init; }
}

重要

仅当在相同类型之间存在多个关系时,才需要 [InverseProperty]。 对于单个关系,两个导航会自动配对。

DeleteBehaviorAttribute

按照约定,EF 会对可选关系使用 ClientSetNullDeleteBehavior,对必需关系使用 Cascade 行为。 这可以通过将 DeleteBehaviorAttribute 置于某个关系导航上进行更改。 例如:

public class Blog
{
    public int Id { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; }

    [DeleteBehavior(DeleteBehavior.Restrict)]
    public Blog Blog { get; init; }
}

有关级联行为的详细信息,请参阅级联删除