作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Pankaj Kansodariya的头像

Pankaj Kansodariya

Pankaj是一名后端开发人员和微软认证专家,在微软生态系统中拥有超过18年的经验, including C#, VB.. NET、SQL Server以及使用Microsoft Azure的云计算. 他做过 .。NET开发人员,包括Granicus, Gartner和Jacobs.

Expertise

Previously At

Gartner
Share

.NET developers 通常需要从c#服务器层调用数据库存储过程(SP). Microsoft’s 实体框架(EF)核心 可用于将sp映射或导入为函数,但是, unfortunately, EF Core本身不支持从存储过程中检索复杂的结果. 这是由于EF Core的开箱即用解决方案的局限性:

  • 将存储过程的结果限制为 Entity type.
  • 不能返回复杂类型以响应 JOIN command.
  • 使创建、更新和删除操作不可用.

我们可以使用 C#, .NET, Microsoft SQL Server和EF Core合作. 此解决方案可用于任何 .. net支持的数据库或 .支持EF Core的。NET语言,前提是实用程序代码被翻译成该语言. 我们将通过一个示例存储过程来了解一些简单的调整如何克服EF Core的限制.

具有复杂结果的假设存储过程

Let's consider GetEmployeesWithDepartment, 返回包含来自两个相关数据库表的信息的复杂结果的存储过程, Employee and Department:

两个相关的数据库表,其中可以产生返回包含信息的复杂结果的存储过程.

The Employee 表通过 foreign key from its ManagerId field. 它还引用了 Department table from the Employee.DepartmentId 字段连接到 Department table’s Id column. 这些表之间的序数关系是:

关系=员工(1):部门(1),部门(1):员工(N)

Now let’s look at GetEmployeesWithDepartment,一个返回an的SP Employee 与输入参数匹配的表行 Employee.Id. 我们的SP返回 Id 值及其所有相关信息,例如员工的 Department and Name values:

创建或修改过程[dbo].(GetEmployeesWithDepartment) 	
    @id INT
AS
BEGIN
    SET NOCOUNT ON;

    SELECT [E].*, [D].[姓名]AS[部门]
    FROM [dbo].[Employee] [E]
        INNER JOIN [dbo].[部门][D] ON [E].[departmentd] = [D].[Id]
    WHERE [E].[Id] >= @id
END

假设我们想要确定与a中列出的第一个员工相关联的部门 简单测试数据库 (在我们的示例中,列出的第一个员工是工程部的John). 我们希望从c#代码中执行这个SP,所以让我们配置EF Core来支持调用 GetEmployeesWithDepartment 作为参数化SP.

注意:在继续之前, 构建数据库 using the Scaffold-DbContext 命令,或使用 创建上下文脚手架 command in .NET Core CLI.

步骤1:创建存储过程结果集模型

首先,我们将创建一个名为 GetEmployeesWithDepartment_Result.cs 然后定义复杂返回类型的结构:

公共类GetEmployeesWithDepartment_Result
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int DepartmentId { get; set; }
    public int? ManagerId { get; set; }
    public int Salary { get; set; }
    public decimal? Bonus { get; set; }
    public string Department { get; set; }
}

使用Microsoft SQL Server 作为数据库服务器,可以显式地验证SP结果列类型 sp_describe_first_result_set command:

EXEC sp_describe_first_result_set N'[dbo].(GetEmployeesWithDepartment)”

该命令显示 存储过程的列 以及相关的类型列表. 定义了结果类型后,我们继续更新 EF model.

步骤2:将模型包含在 DbContext File

我们已经准备好将结果模型合并到应用程序的EF Core中 DbContext file. EF为扩展应用程序的数据模型提供了一种优雅的方法. Such 支持扩展 使用分部类,特别是使用 OnModelCreatingPartial method. 为了防止EF Core的脚手架工具修改我们的自定义代码,我们将把结果模型添加到 EFCoreSPContext.SP.cs,一个局部c#类:

using EFCoreSP.Data.SPs;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;

名称空间EFCoreSP.Data
{
    公共部分类EFCoreSPContext: DbContext
    {
        public virtual DbSet
            GetEmployeesWithDepartment_Results { get; set; }

        //我们将在这里添加后续更改
    }
}

Here’s how EFCoreSPContext.SP.cs looks in our repository. 现在我们需要添加代码来标识模型的主键(如果存在的话).

步骤3:指定模型的密钥

我们将通过在类中配置模型来指示SP的结果集是否具有键值 OnModelCreatingPartial method in our EFCoreSPContext definition.

如果结果集有键值,则使用 HasKey 方法显式标识与该键值关联的属性:

OnModelCreatingPartial(ModelBuilder)
{
    modelBuilder.Entity(entity => 
        entity.HasKey(e => e.Id));      
}

如果实体没有键值,则使用 HasNoKey method instead:

OnModelCreatingPartial(ModelBuilder)
{
    modelBuilder.Entity(entity => 
        entity.HasNoKey());       
}

我们的模型定义现在已经完成了. 我们已经准备好调用SP并检索示例员工数据.

调用复杂的存储过程:简单如1-2-3

为了简化对SP的调用,我们将在控件中再添加一个公共方法 EFCoreSPContext file. 方法的定义接受 Employee.Id 所提供的价值传递了它 Id ,并以列表的形式检索生成的复杂结果:

public IEnumerable 
    SP_GetEmployeesWithDepartment (int id)
{
    return this.GetEmployeesWithDepartment_Results
        .FromSqlInterpolated($”[dbo].(GetEmployeesWithDepartment) {id}”)
        .ToArray();
}

Our DbContext 文件现在已准备好调用存储过程并返回复杂类型结果集 code is complete. 返回到我们的示例查询, 我们可以使用一个简单的命令来返回与数据库中第一个员工相关的部门和其他数据:

var employees = dbContext.SP_GetEmployeesWithDepartment (1);

我们应用了一个简单的, 然而聪明和强大, 从存储过程返回非数据库实体的解决方案. 这种方法需要相对较少的支持代码行,并且在使用EF Core检索复杂结果时产生可观的回报.

Toptal工程博客的编辑团队向 亚历山大Skogorev 查看本文中提供的技术内容和代码示例.


了解基本知识

  • 实体框架(EF)核心的用途是什么?

    微软从 .NET Framework to .NET Core. 凭借其轻量级架构,开发人员可以扩展跨平台框架, EF Core是一种用于访问数据层的流行工具 .. NET语言,通常是c#.

  • EF和EF Core有什么区别?

    EF是一个对象关系映射器 .NET Framework. 而EF不再积极发展, EF Core是微软目前的对象数据库映射器 .NET (i.e., .NET Core).

  • EF Core支持存储过程吗?

    是的,EF Core支持存储过程,就像它的前身实体框架一样.

  • 如何在EF Core中运行存储过程?

    您可以使用DbSet在EF Core中执行存储过程.FromSql()或DbContext.Database.ExecuteSqlCommand()命令.

  • 我可以在实体框架中调用存储过程吗?

    可以,存储过程可以作为带有实体框架的函数导入 .edmx file.

聘请Toptal这方面的专家.
Hire Now
Pankaj Kansodariya的头像
Pankaj Kansodariya

Located in 英国伦敦

Member since July 13, 2020

About the author

Pankaj是一名后端开发人员和微软认证专家,在微软生态系统中拥有超过18年的经验, including C#, VB.. NET、SQL Server以及使用Microsoft Azure的云计算. 他做过 .。NET开发人员,包括Granicus, Gartner和Jacobs.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

Previously At

Gartner

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.