一. 查询类型
此功能是 EF Core 2.1 中的新功能. EF Core 除了实体类型之外, EF Core 模型还可以包含查询类型, 这些查询类型是针对 "未映射到实体类型" 的数据获取. 比如视图, 或只读数据表.
1.1 下面介绍下, 查询类型与实体类型共同与不同点:
(1) 可以在 OnModelCreating 中或通过派生 DbContext 上的 "set" 属性添加到 EF Core 模型中.
(2) 支持许多相同的映射功能, 在关系数据库上, 如继承映射和导航属性. 也可以配置目标数据库对象和列通过 fluent API 方法或数据注释.
但是, 它们不同于实体中的类型:
(1) 不需要定义的键.
(2) 不会跟踪 DbContext 上的更改, 因此不会在数据库中插入, 更新或删除.
(3) 永远不会由约定发现.
(4) 仅支持一部分导航映射功能
它们可能永远不会作为关系的主体端(没有主体实体和依赖实体的关系).
它们仅可包含指向的实体的引用导航属性.
实体不能包含查询类型的导航属性.
(5) 在 ModelBuilder 上使用 Query 方法而不是 Entity 方法进行寻址.
(6) 是否通过类型 DbQuery<T > 而不是 DbSet<T > 的属性映射到 DbContext 上.
(7) 映射到使用的数据库对象 ToView 方法, 而非 ToTable.
(8) 可以映射到定义查询. 定义查询是在模型中声明的辅助查询, 充当查询类型的数据.
1.2 查询类型使用方案(应用场景)
(1) 作为返回类型的即席 FromSql()查询.
(2) 映射到数据库视图.
(3) 映射到不具有定义的主键的表.
(4) 映射到模型中定义的查询.
1.3 映射到数据库对象
查询类型映射到的数据库对象, 通过实现 ToView 的 fluent API. 此 ToView 方法中指定的数据库对象, 从 EF Core 的角度来看是视图, 这意味着它将被视为只读查询源和不能作为目标的更新, 插入或删除操作(也可以将被视为只读表). 相反, 对于实体类型, EF Core 假定数据库对象中指定 ToTable 方法可以视为表或视图, 意味着它可以用作查询源, 还可以做更新, 删除和插入操作.
- // 查询类型的 Blog , 参数: view or table.
- modelBuilder.Query<Blog>().ToView("xx");
- // 实体类型的 Blog 参数: view or table. 注意如果是视图, 更新数据, 只能是一个表的视图.
- modelBuilder.Entity<Blog>().ToTable("xx");
1.4 案例
下面的示例演示如何使用查询类型来查询数据库视图. 完整代码可查看官方示例(下面有链接).
(1)首先定义一个简单的 Blog 和 Post 实体类型.
- public class Blog
- {
- public int BlogId { get; set; }
- public string Name { get; set; }
- public string Url { get; set; }
- public ICollection<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; }
- }
(2) 使用 BloggingContext 上下文生成一个简单的数据库视图, 这样就可以查询与每个 Blog 的帖子 (Posts) 数
- BloggingContext.Database.ExecuteSqlCommand(
- @"CREATE VIEW View_BlogPostCounts AS
- SELECT b.Name, Count(p.PostId) as PostCount
- FROM Blogs b
- JOIN Posts p on p.BlogId = b.BlogId
- GROUP BY b.Name");
(3) 接下来, 我们定义一个查询类来保存数据库视图的结果
- public class BlogPostsCount
- {
- public string BlogName { get; set; }
- public int PostCount { get; set; }
- }
(4) 我们配置中的查询类型在 OnModelCreating 中使用 modelBuilder.Query<T>API. 我们使用标准的 fluent 配置 API 来配置查询类型的映射:
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder
- .Query<BlogPostsCount>().ToView("View_BlogPostCounts")
- .Property(v => v.BlogName).HasColumnName("Name");
- }
(5) 最后, 我们可以采用标准方式来查询数据库视图:
var postCounts = BloggingContext.BlogPostCounts.ToList();
二. 关系数据库建模
一般而言, 本部分中的配置适用于关系数据库. 安装关系数据库提供程序时, 下面显示的扩展方法将变为可用, 原因在于共享 Microsoft.EntityFrameworkCore.Relational 包
2.1 表映射
表映射是指 (实体与数据表的映射) 包括查询的表数据, 并将其保存到数据库中.
按照约定, 每个实体都将映射到一个表, 该表的名称与 DbSet<TEntity > 在派生上下文中公开实体的属性相同. 例如在 EF 上下文中公开 Blog 类型, 生成的表名与属性名相同.
- // 生成 Blogs 表名
- public DbSet<Blog> Blogs { get; set; }
- // 除了约定还可以使用数据注释, 表 blogs 与实体 Blog 映射
- [Table("blogs")]
- public class Blog
- {
- public int BlogId { get; set; }
- public string Url { get; set; }
- }
- // 还可以使用 Fluent API 配置, 功能实现与上面一样
- modelBuilder.Entity<Blog>().ToTable("blogs");
2.2 列映射
按照约定, 实体中的每个属性都会映射到表中, 具有相同名称的数据字段. 还可以使用数据注释或 Fluent API 配置:
- public class Blog
- {
- // 数据注释将表 Blog 的 blog_id 字段映射到 BlogId 属性.
- [Column("blog_id")]
- public int BlogId { get; set; }
- public string Url { get; set; }
- }
- // 使用 Fluent API 配置, 功能实现与上面一样
- modelBuilder.Entity<Blog>().Property(b => b.BlogId).HasColumnName("blog_id");
2.3 数据类型
数据类型是指: 数据库的数据类型与 CLR 属性类型映射. 可以使用数据注释来指定精确的数据类型的列(一般用在 code first 模式).
- // 数据注释的案例
- public class Blog
- {
- public int BlogId { get; set; }
- [Column(TypeName = "varchar(200)")]
- public string Url { get; set; }
- [Column(TypeName = "decimal(5, 2)")]
- public decimal Rating { get; set; }
- }
- //Fluent API 配置, 功能实现与上面一样
- modelBuilder.Entity<Blog>(eb =>
- {
- eb.Property(b => b.Url).HasColumnType("varchar(200)");
- eb.Property(b => b.Rating).HasColumnType("decimal(5, 2)");
- });
2.4 主键
对于每个实体类型的键是引入了主键约束. 按照约定, 主键名称是 PK_<type name>, 例如 Posts 实体中有 BlogId 属性, 那表中主键约束名是 PK_Posts. 可以使用 Fluent API 配置数据库中的主键约束的名称.
- // 将默认的 PK_Posts 改为了 PrimaryKey_BlogId 主键约束名
- modelBuilder.Entity<Blog>()
- .HasKey(b => b.BlogId)
- .HasName("PrimaryKey_BlogId");
2.5 默认架构
如果对象没有显式配置架构, 则默认架构是创建对象的数据库架构. 按照约定, 数据库提供程序将选择最合适的默认架构. 例如, Microsoft SQL Server 将使用该 dbo 模式. 无法使用数据注释设置默认架构, 可以使用 Fluent API 指定默认架构.
-- 修改 sqlserver 表默认架构
ALTER SCHEMA [blogging] TRANSFER dbo.Blogs
-- 查询表
- select * from [blogging].Blogs
- // 设置 ef core 对应数据库表查询的架构名
- modelBuilder.HasDefaultSchema("blogging");
2.6 默认值
如果插入新行, 但未指定列值, 可设置一个默认值. 不可以使用约定或数据注释提供默认值. 可以使用 Fluent API 配置默认值.
- // 例如插入数据时, 设置 Created 字段取 sqlserver 的当前时间值.
- modelBuilder.Entity<Blog>()
- .Property(b => b.Created)
- .HasDefaultValueSql("getdate()");
2.7 索引
关系数据库中的索引映射到相同的概念的 EF core 中的索引. 按照约定, 索引名为 IX_<type name>_<property name>, 对于复合索引, 下划线分隔的属性名称列表. 可以使用 Fluent API 配置索引的名称.
- // 设置 url 索引名称为 Index_Url
- modelBuilder.Entity<Blog>()
- .HasIndex(b => b.Url)
- .HasName("Index_Url");
- // 还可以指定索引过滤器
- modelBuilder.Entity<Blog>()
- .HasIndex(b => b.Url)
- .HasFilter("[Url] IS NOT NULL");
参考文献:
官方文档: EF 查询类型
查询类型示例
关系数据库建模
来源: https://www.cnblogs.com/MrHSR/p/10411284.html