如何在Entity Framework Core代码优先方法下创建存储过程并映射已有存储过程
嘿,针对你用ASP.NET Core + EF Core代码优先模式遇到的两个需求——创建新存储过程和映射已有存储过程,我给你整理了项目里常用的实用步骤和方法:
一、创建新的存储过程(Code First模式)
Code First模式下创建存储过程,最规范的方式是通过**迁移(Migrations)**来实现,这样能保证数据库Schema和代码的同步性,步骤如下:
生成空迁移文件
在Package Manager Console(PMC)或者终端里执行命令,创建一个空的迁移类:# PMC命令 Add-Migration CreateStoredProcedure_GetAllUsers # 终端命令 dotnet ef migrations add CreateStoredProcedure_GetAllUsers编辑迁移类的SQL逻辑
打开生成的迁移文件,在Up方法里编写创建存储过程的SQL,同时在Down方法里编写回滚逻辑(删除存储过程):protected override void Up(MigrationBuilder migrationBuilder) { // 创建获取所有用户的存储过程 migrationBuilder.Sql(@" CREATE PROCEDURE GetAllUsers AS BEGIN SELECT Id, Name, Email, CreateTime FROM Users; END "); } protected override void Down(MigrationBuilder migrationBuilder) { // 回滚时删除存储过程 migrationBuilder.Sql("DROP PROCEDURE IF EXISTS GetAllUsers"); }应用迁移到数据库
执行命令将迁移逻辑同步到数据库,存储过程就会被创建:# PMC命令 Update-Database # 终端命令 dotnet ef database update
二、映射已存在的存储过程到EF Core
如果数据库里已经有现成的存储过程,想要让EF Core调用它,有三种常用方式:
方式1:用FromSqlRaw/FromSqlInterpolated查询实体
如果存储过程的返回结果和你的现有实体类字段完全匹配,可以直接用这个方法查询。比如你有User实体,存储过程GetAllUsers返回的字段和User对应:
public class AppDbContext : DbContext { public DbSet<User> Users { get; set; } // 调用存储过程获取用户列表 public IEnumerable<User> GetAllUsers() { return Users.FromSqlRaw("EXEC GetAllUsers").ToList(); } // 带参数的存储过程调用(用FromSqlInterpolated防止SQL注入) public User GetUserById(int userId) { return Users.FromSqlInterpolated($"EXEC GetUserById {userId}").FirstOrDefault(); } }
方式2:用ExecuteSqlRaw执行非查询存储过程
如果存储过程是做插入、更新、删除这类无实体返回的操作,就用这个方法:
public class AppDbContext : DbContext { // 更新用户邮箱的存储过程调用 public int UpdateUserEmail(int userId, string newEmail) { // 用参数化方式更安全,避免SQL注入 var parameters = new[] { new SqlParameter("@UserId", userId), new SqlParameter("@NewEmail", newEmail) }; return Database.ExecuteSqlRaw("EXEC UpdateUserEmail @UserId, @NewEmail", parameters); } }
方式3:用无键实体映射自定义结果集(EF Core 3.0+)
如果存储过程返回的结果没有对应的现有实体,可以用无键实体类型来映射:
- 先创建一个无键实体类,对应存储过程的返回字段:
public class UserOrderSummary { public int UserId { get; set; } public string UserName { get; set; } public int TotalOrders { get; set; } public decimal TotalSpent { get; set; } }
- 在DbContext里配置这个无键实体:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<UserOrderSummary>() .HasNoKey() // 标记为无键实体 .ToView(null); // 因为不是视图,是存储过程,所以设为null }
- 调用存储过程获取结果:
public IEnumerable<UserOrderSummary> GetUserOrderSummaries() { return Set<UserOrderSummary>().FromSqlRaw("EXEC GetUserOrderSummaries").ToList(); }
内容的提问来源于stack exchange,提问作者Juber




