在实体框架核心中使用 SQL 视图

例如,我有这样的模型:

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


public BlogImage BlogImage { get; set; }
}


public class BlogImage
{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; }


public int BlogId { get; set; }
public Blog Blog { get; set; }
}

我想返回在 图片浏览视图 厄尔形象

我需要在哪里创建和定义 SQL 视图?

138781 次浏览

实体框架核心目前不支持视图。请参阅 https://github.com/aspnet/EntityFramework/issues/827

也就是说,您可以通过将您的实体映射到视图来欺骗 EF 使用视图,就好像它是一个表一样。这种方法有其局限性。例如,你不能使用迁移,你需要手动为 EF 指定一个键来使用,有些查询可能无法正常工作。为了绕过最后一部分,您可以手工编写 SQL 查询

context.Images.FromSql("SELECT * FROM dbo.ImageView")

EF Core 不会在上下文类中为 SQL 视图自动创建 DBset,我们可以手动添加它们,如下所示。

public partial class LocalDBContext : DbContext
{


public LocalDBContext(DbContextOptions<LocalDBContext> options) : base(options)
{


}


public virtual DbSet<YourView> YourView { get; set; }


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourView>(entity => {
entity.HasKey(e => e.ID);
entity.ToTable("YourView");
entity.Property(e => e.Name).HasMaxLength(50);
});
}


}

示例视图定义如下,只有很少的属性

using System;
using System.Collections.Generic;


namespace Project.Entities
{
public partial class YourView
{
public string Name { get; set; }
public int ID { get; set; }
}
}

在上下文类中为视图和数据库集添加类之后,可以通过控制器中的上下文对象使用视图对象。

这里有一种在 EFCore 中使用 SQL 视图的新方法: 查询类型

实体框架核心2.1中,我们可以像 YuriyN 建议的那样使用 查询类型

关于如何使用它们的更详细的文章可以找到 给你

根据这篇文章的例子,最直接的方法是:

例如,我们有以下实体模型来管理出版物

public class Magazine
{
public int MagazineId { get; set; }
public string Name { get; set; }
public string Publisher { get; set; }
public List<Article> Articles { get; set; }
}


public class Article
{
public int ArticleId { get; set; }
public string Title { get; set; }
public int MagazineId { get; set; }
public DateTime PublishDate { get;  set; }
public Author Author { get; set; }
public int AuthorId { get; set; }
}
public class Author
{
public int AuthorId { get; set; }
public string Name { get; set; }
public List<Article> Articles { get; set; }
}

2. 我们有一个名为 AuthorArticleCounts 的视图,它定义为返回作者撰写的文章的名称和数量

SELECT
a.AuthorName,
Count(r.ArticleId) as ArticleCount
from Authors a
JOIN Articles r on r.AuthorId = a.AuthorId
GROUP BY a.AuthorName

我们去创建一个用于视图的模型

public class AuthorArticleCount
{
public string AuthorName { get; private set; }
public int ArticleCount { get; private set; }
}

4. 之后,我们在我的 DbContext 中创建一个 DbQuery 属性来使用 Model 中的视图结果

public DbQuery<AuthorArticleCount> AuthorArticleCounts{get;set;}

4.1.您可能需要重写 OnModelCreate ()并设置视图,特别是当您的视图名称与 Class 不同时。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Query<AuthorArticleCount>().ToView("AuthorArticleCount");
}

最后,我们可以很容易地得到这样的结果。

var results=_context.AuthorArticleCounts.ToList();

更新 根据索格内兹的评论

值得注意的是,EF 不再支持 DbQuery Core 3.0. < strong > 点击这里查看 href = “ https://Learn.microsoft.com/en-us/ef/Core/model/keyless-tity-types”rel = “ noReferrer”>

QueryType 是 EF Core 2.1的标准答案,但是在从数据库优先方法迁移时,我使用了另一种方法(视图已经在数据库中创建) :

  • 定义模型以匹配视图列(要么将模型类名匹配到视图名,要么使用 Table 属性。确保[ Key ]属性至少应用于一列,否则数据获取将失败
  • 在上下文中添加 DbSet
  • 添加迁移(添加-迁移)
  • 根据提供的模型删除或注释掉要创建/删除的“表”的创建/删除代码
  • 更新数据库(更新-数据库)

EF 核心支持的意见,给你是细节。

这个特性是在 EF Core 2.1中以查询类型的名义添加的。在 EF Core 3.0中,这个概念被重命名为无键实体类型。[ Keyless ]数据注释在 EFCore 5.0中可用。

它的工作方式与普通实体没有太大不同,但有一些特殊之处:

  • 不能定义键。
  • 永远不会跟踪 DbContext 中的更改,因此永远不会在数据库中插入、更新或删除。
  • 从来没有被惯例所发现。
  • 只支持导航映射功能的子集,特别是:
  • 他们可能永远不会成为一段关系的主要目标。
  • 它们可能没有导航到所有实体
  • 它们只能包含指向常规实体的引用导航属性。
  • 实体不能包含无键实体类型的导航属性。
  • 需要使用[ Keyless ]数据注释或.HasNoKey ()方法调用进行配置。
  • 可以映射到定义查询。定义查询是在模型中声明的查询,充当无键实体类型的数据源

它的工作原理如下:

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

如果数据库中没有现成的视图,应该创建如下:

db.Database.ExecuteSqlRaw(
@"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");

然后您应该有一个类来保存数据库视图中的结果:

  public class BlogPostsCount
{
public string BlogName { get; set; }
public int PostCount { get; set; }
}

然后使用 HasNoKey 在 OnModelCreate 中配置无键实体类型:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<BlogPostsCount>(eb =>
{
eb.HasNoKey();
eb.ToView("View_BlogPostCounts");
eb.Property(v => v.BlogName).HasColumnName("Name");
});
}

然后配置 DbContext 以包含 DbSet:

public DbSet<BlogPostsCount> BlogPostCounts { get; set; }

这是可能的 搭脚手架看风景。只要使用-Tables,就像搭建表一样,只使用视图的名称。例如,如果视图的名称是“ vw _ Inventory”,那么在包管理器控制台中运行以下命令(将您自己的信息替换为“ My...”) :

PM> Scaffold-DbContext "Server=MyServer;Database=MyDatabase;user id=MyUserId;password=MyPassword" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Temp -Tables vw_inventory

此命令将在项目的 Temp 目录中创建模型文件和上下文文件。您可以将模型文件移动到您的模型目录(请记住更改名称空间名称)。您可以从上下文文件中复制所需内容,并将其粘贴到项目中适当的现有上下文文件中。

注意: 如果希望在使用本地数据库的集成测试中使用视图,则需要创建视图作为数据库设置的一部分。如果要在多个测试中使用视图,请确保添加视图是否存在的检查。在这种情况下,由于 SQL‘ Create View’语句必须是批处理中的唯一语句,因此您需要以动态 SQL 的形式在存在检查语句中运行 Create 视图。或者,您可以运行单独的“如果存在拖放视图...”,然后运行“创建视图”语句,但是如果多个测试同时运行,那么如果另一个测试正在使用该视图,则不希望该视图被拖放。 例如:

  void setupDb() {
...
SomeDb.Command(db => db.Database.ExecuteSqlRaw(CreateInventoryView()));
...
}
public string CreateInventoryView() => @"
IF OBJECT_ID('[dbo].[vw_inventory]') IS NULL
BEGIN EXEC('CREATE VIEW [dbo].[vw_inventory] AS
SELECT ...')
END";