如何在Entity Framework Core中运行存储过程?

ead*_*dam 49 entity-framework-core asp.net-core-mvc asp.net-core

我在asp.net核心应用程序中使用EF7(实体框架核心).能否请您指出执行存储过程的正确方法?旧的方法与ObjectParameters((IObjectContextAdapter)this).ObjectContext.ExecuteFunction不工作.

Arv*_*vin 61

现在解决了对EF7中存储过程的支持,这也支持多个结果集的映射.

点击此处查看修复详情

你可以这样称呼它 - var userType = dbContext.Set().FromSql("dbo.SomeSproc @Id = {0}, @Name = {1}", 45, "Ada");

  • 看到这个,如果你不喜欢我从sql找不到:http://dotnetthoughts.net/how-to-execute-storedprocedure-in-ef-core/"要获取FromSql命令,你需要添加"Microsoft.EntityFrameworkCore"的引用.Relational"包在你的project.json文件中." 还使用Microsoft.EntityFrameworkCore添加; (7认同)
  • 我的2美分:var UserId = 1; _dbCtx.Set <YourDTOViewModelWhatever>().FromSql($"dbo.StoredProcedureName @ UserId = {UserId}"); //只是语法糖...... (3认同)
  • 在这里找到另一个很好的例子 - http://www.dotnetjalps.com/2015/11/stored-procedure-entity-framework-codefirst.html 这是不言自明的。 (2认同)

bri*_*lam 20

存储过程支持尚未在EF7中实现(从7.0.0-beta3开始).您可以使用问题#245跟踪此功能的进度.

现在,您可以使用ADO.NET以传统方式执行此操作.

var connection = (SqlConnection)context.Database.AsSqlServer().Connection.DbConnection;

var command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "MySproc";
command.Parameters.AddWithValue("@MyParameter", 42);

command.ExecuteNonQuery();
Run Code Online (Sandbox Code Playgroud)

  • 那是 [#624](https://github.com/aspnet/EntityFramework/issues/624)。;) 您始终可以在 ADO.NET 连接上执行此操作:`context.Database.AsRelational().Connection.DbConnection`。 (2认同)
  • @JimWooley这似乎有点不道德......但是,现在使用`FromSql`. (2认同)
  • @JimWooley大声笑,不确定您是否知道它,但您对EF团队中一位主要开发人员的回答不满意... :) (2认同)
  • @ RichardMarskell-Drackir StackOverflow的答案非常重要。您是否真的希望每个人在剩下的时间里都能保持所有答案的最新状态?询问者应该选择其他答案,或者社区应该赞成随之而来的更好的答案。下票的形式不正确。 (2认同)

Roh*_*ith 18

要执行存储过程,请使用执行RAW SQL查询的FromSql方法

例如

var products= context.Products
    .FromSql("EXECUTE dbo.GetProducts")
    .ToList();
Run Code Online (Sandbox Code Playgroud)

用于参数

var productCategory= "Electronics";

var product = context.Products
    .FromSql("EXECUTE dbo.GetProductByCategory {0}", productCategory)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

要么

var productCategory= new SqlParameter("productCategory", "Electronics");

var product = context.Product
    .FromSql("EXECUTE dbo.GetProductByName  @productCategory", productCategory)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

执行RAW SQL查询或存储过程有一些限制.您不能将它用于INSERT/UPDATE/DELETE.如果要执行INSERT,UPDATE,DELETE查询,请使用ExecuteSqlCommand

var categoryName = "Electronics";
dataContext.Database
           .ExecuteSqlCommand("dbo.InsertCategory @p0", categoryName);
Run Code Online (Sandbox Code Playgroud)

  • 方法 `ExecuteSqlCommand` 现已过时。请参阅 [ExecuteSqlCommand 方法](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.executesqlcommand?view=efcore-3.1) 可能的替代方法是“ExecuteSqlRaw”和“ExecuteSqlInterpolated”。 (2认同)

Mar*_*ero 10

EF Core 中对存储过程的支持类似于早期版本的 EF Code first。

您需要通过从 EF 继承 DbContext 类来创建 DbContext 类。存储过程使用 DbContext 执行。

第一步是编写一个从 DbContext 创建 DbCommand 的方法。

public static DbCommand LoadStoredProc(
  this DbContext context, string storedProcName)
{
  var cmd = context.Database.GetDbConnection().CreateCommand();
  cmd.CommandText = storedProcName;
  cmd.CommandType = System.Data.CommandType.StoredProcedure;
  return cmd;
}
Run Code Online (Sandbox Code Playgroud)

要将参数传递给存储过程,请使用以下方法。

public static DbCommand WithSqlParam(
  this DbCommand cmd, string paramName, object paramValue)
{
  if (string.IsNullOrEmpty(cmd.CommandText))
    throw new InvalidOperationException(
      "Call LoadStoredProc before using this method");
  var param = cmd.CreateParameter();
  param.ParameterName = paramName;
  param.Value = paramValue;
  cmd.Parameters.Add(param);
  return cmd;
}
Run Code Online (Sandbox Code Playgroud)

最后,使用 MapToList 方法将结果映射到自定义对象列表中。

private static List<T> MapToList<T>(this DbDataReader dr)
{
  var objList = new List<T>();
  var props = typeof(T).GetRuntimeProperties();

  var colMapping = dr.GetColumnSchema()
    .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
    .ToDictionary(key => key.ColumnName.ToLower());

  if (dr.HasRows)
  {
    while (dr.Read())
    {
      T obj = Activator.CreateInstance<T>();
      foreach (var prop in props)
      {
        var val = 
          dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
          prop.SetValue(obj, val == DBNull.Value ? null : val);
      }
      objList.Add(obj);
    }
  }
  return objList;
}
Run Code Online (Sandbox Code Playgroud)

现在我们已准备好使用 ExecuteStoredProc 方法执行存储过程并将其映射到类型为 T 传入的列表。

public static async Task<List<T>> ExecuteStoredProc<T>(this DbCommand command)
{
  using (command)
  {
    if (command.Connection.State == System.Data.ConnectionState.Closed)
    command.Connection.Open();
    try
    {
      using (var reader = await command.ExecuteReaderAsync())
      {
        return reader.MapToList<T>();
      }
    }
    catch(Exception e)
    {
      throw (e);
    }
    finally
    {
      command.Connection.Close();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

例如,要执行一个名为“StoredProcedureName”的存储过程,并带有两个名为“firstparamname”和“secondparamname”的参数,这就是实现。

List<MyType> myTypeList = new List<MyType>();
using(var context = new MyDbContext())
{
  myTypeList = context.LoadStoredProc("StoredProcedureName")
  .WithSqlParam("firstparamname", firstParamValue)
  .WithSqlParam("secondparamname", secondParamValue).
  .ExecureStoredProc<MyType>();
}
Run Code Online (Sandbox Code Playgroud)


Abh*_*ine 8

我尝试了所有其他解决方案,但对我不起作用。但是我找到了一个合适的解决方案,它可能对这里的人有所帮助。

要调用存储过程并将结果放入 EF Core 中的模型列表,我们必须遵循 3 个步骤。

第 1 步。 您需要添加一个新类,就像您的实体类一样。其中应该具有 SP 中所有列的属性。例如,如果您的 SP 返回两列调用IdName那么您的新类应该类似于

public class MySPModel
{
    public int Id {get; set;}
    public string Name {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

第2步。

然后您必须DbQuery为您的 SP添加一个属性到您的 DBContext 类中。

public partial class Sonar_Health_AppointmentsContext : DbContext
{
        public virtual DbSet<Booking> Booking { get; set; } // your existing DbSets
        ...

        public virtual DbQuery<MySPModel> MySP { get; set; } // your new DbQuery
        ...
}
Run Code Online (Sandbox Code Playgroud)

第 3 步。

现在,您将能够从 DBContext 调用并从 SP 获取结果。

var result = await _context.Query<MySPModel>().AsNoTracking().FromSql(string.Format("EXEC {0} {1}", functionName, parameter)).ToListAsync();
Run Code Online (Sandbox Code Playgroud)

我正在使用通用的 UnitOfWork & Repository。所以我执行 SP 的功能是

/// <summary>
/// Execute function. Be extra care when using this function as there is a risk for SQL injection
/// </summary>
public async Task<IEnumerable<T>> ExecuteFuntion<T>(string functionName, string parameter) where T : class
{
    return await _context.Query<T>().AsNoTracking().FromSql(string.Format("EXEC {0} {1}", functionName, parameter)).ToListAsync();
}
Run Code Online (Sandbox Code Playgroud)

希望对某人有帮助!!!


Ash*_*h K 6

令人尴尬的是,缺乏这方面的文档。

我花了很多时间和尝试才弄清楚这一点,所以我想在这里记录下来,这样其他人就不必花时间来弄清楚这一点。我在用着Entity Framework Core Version 6.0.10

概括

有两种返回结果的存储过程。调用它们并获取结果的方法也各不相同。

  1. 返回基于表的输出的 SP。

  2. 在输出参数中返回输出的 SP。


返回基于表的输出的 SP

如果你的 SP 看起来像这样:

CREATE PROCEDURE dbo.MyTestSPToGetShopType @ShopId AS VARCHAR(25)
AS
SELECT ShopId, ShopType
FROM Shop
WHERE SHOPID = @ShopId
Run Code Online (Sandbox Code Playgroud)

第 1 步: 根据您的输出创建一个类

public class MyTestSpResult
{
    public string ShopId { get; set; }
    public string ShopType { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

第 2 步: 在 DbContext 的方法中注册protected override void OnModelCreating(ModelBuilder builder)

builder.Entity<MyTestSpResult>(e => e.HasNoKey());
Run Code Online (Sandbox Code Playgroud)

步骤3: 从代码中调用SP,并得到结果:

var myTestSpResult = await _dbContext.Set<MyTestSpResult>()
                                     .FromSqlInterpolated($"EXEC dbo.MyTestSPToGetShopType @ShopId = \"A03\"")
                                     .ToArrayAsync();
var myShopType = myTestSpResult[0].ShopType;
Run Code Online (Sandbox Code Playgroud)

另一种方式

步骤1和2与上面相同。

步骤 3: 在 DbContext 中注册步骤 1 中的类:

public virtual DbSet<MyTestSpResult> MyTestSpResult { get; set; }
Run Code Online (Sandbox Code Playgroud)

第四步: 从代码中调用SP,并得到结果:

var mySPResult = await _dbContext.MyTestSpResult
                                 .FromSqlInterpolated($"EXEC dbo.MyTestSPToGetShopType @ShopId = \"A03\"")
                                 .ToArrayAsync();
var myShopType = mySPResult[0].ShopType;
Run Code Online (Sandbox Code Playgroud)

在输出参数中返回输出的 SP

如果你的 SP 看起来像这样:

CREATE PROCEDURE dbo.MyOtherTestSPToGetShopType
(
    @ShopId VARCHAR(25), @ShopType VARCHAR(25) OUTPUT
)
AS
SELECT @ShopType = ShopType
FROM Shop
WHERE SHOPID = @ShopId
Run Code Online (Sandbox Code Playgroud)

步骤 1: 创建 SQL 参数:

var shopId = new SqlParameter()
{
    ParameterName = "@ShopId",
    Value = "A03",
    Direction = System.Data.ParameterDirection.Input,
    SqlDbType = System.Data.SqlDbType.VarChar,
    Size = 25
};
   
var shopType = new SqlParameter()
{
    ParameterName = "@ShopType",
    Direction = System.Data.ParameterDirection.Output,
    SqlDbType = System.Data.SqlDbType.VarChar,
    Size = 25
};
Run Code Online (Sandbox Code Playgroud)

步骤 2: 使用这些参数从代码中调用 SP,并获取结果:

await _dbContext.Database
                .ExecuteSqlInterpolatedAsync($"EXEC dbo.MyOtherTestSPToGetShopType @ShopId = {shopId}, @ShopType = {shopType} OUT");
var myShopType = shopType.Value as string;
Run Code Online (Sandbox Code Playgroud)


小智 5

"(SqlConnection)context" -- 这种类型转换不再有效。你可以做:"SqlConnection context;

".AsSqlServer()" - 不存在。

"command.ExecuteNonQuery();" -- 不返回结果。 reader=command.ExecuteReader()确实有效。

使用 dt.load(reader)... 那么您必须将框架从 5.0 切换回 4.51,因为 5.0 尚不支持数据表/数据集。注意:这是VS2015 RC。


Bry*_*yan 5

我有很多的麻烦ExecuteSqlCommandExecuteSqlCommandAsync,IN参数很容易,但OUT参数是非常困难的。

我不得不DbCommand像这样恢复使用-

DbCommand cmd = _context.Database.GetDbConnection().CreateCommand();

cmd.CommandText = "dbo.sp_DoSomething";
cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add(new SqlParameter("@firstName", SqlDbType.VarChar) { Value = "Steve" });
cmd.Parameters.Add(new SqlParameter("@lastName", SqlDbType.VarChar) { Value = "Smith" });

cmd.Parameters.Add(new SqlParameter("@id", SqlDbType.BigInt) { Direction = ParameterDirection.Output });
Run Code Online (Sandbox Code Playgroud)

我在这篇文章中写了更多关于它的内容