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

Posted

技术标签:

【中文标题】如何在 Entity Framework Core 中运行存储过程?【英文标题】:How to run stored procedures in Entity Framework Core? 【发布时间】:2015-02-19 05:45:52 【问题描述】:

我在 ASP.NET Core 应用程序中使用 EF Core 1.0。您能否指出执行存储过程的正确方法? ObjectParameters((IObjectContextAdapter)this).ObjectContext.ExecuteFunction 的旧方法不起作用。

【问题讨论】:

您需要运行 SP 并得到一些 DbSet 的结果吗?还是直接运行? 我需要找回结果! 【参考方案1】:

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

Check here for the fix details

你可以在c#中这样称呼它

var userType = dbContext.Set().FromSql("dbo.SomeSproc @Id = 0, @Name = 1", 45, "Ada");

【讨论】:

在这里找到另一个很好的例子 - dotnetjalps.com/2015/11/… 这是不言自明的。 尚不支持多个数据集。见github.com/aspnet/EntityFramework/issues/… 我的 2 美分:var UserId =1; _dbCtx.Set().FromSql($"dbo.StoredProcedureName @UserId= UserId"); //只是语法糖... 如果你喜欢我找不到fromSql,请看这个:dotnetthoughts.net/how-to-execute-storedprocedure-in-ef-core“要获取FromSql命令,你需要在你的project.json文件中添加“Microsoft.EntityFrameworkCore.Relational”包的引用。 "还使用 Microsoft.EntityFrameworkCore 添加; 遗憾的是,您似乎无法在 EF Core 1.1 中使用 Set()。只需获取The arguments for DbContext.Set<TEntity>() cannot be inferred from the usage【参考方案2】:

EF7 中尚未实现存储过程支持(截至 7.0.0-beta3)。您可以使用 issue #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();

【讨论】:

那是#624。 ;) 您始终可以在 ADO.NET 连接上执行此操作:context.Database.AsRelational().Connection.DbConnection @JimWooley 这似乎有点不道德......但是是的,现在使用FromSql @JimWooley LOL,不确定您是否意识到这一点,但您否决了 EF 团队中一位主要开发人员的回答... :) @RichardMarskell-Drackir *** 的答案在很大程度上是一个时间点的事情。您真的希望每个人在剩下的时间里保持所有答案都是最新的吗?提问者应该选择不同的答案,或者社区应该支持出现的更好的答案。否决票的错误形式。 @bricelam - 我不同意答案应该被视为时间点。我偶尔会回到旧的高投票答案,看看是否有任何变化,所以新的搜索仍然相关,但我意识到并不是每个人都这样做。很多时候,我从搜索中来到 SO 并开始使用接受的答案,只是后来才发现有一个更好的答案。我同意如果提问者更新答案会很好,但他们很可能会得到他们想要的并继续前进。无论如何,我认为来自谷歌的人知道有更好的方法来做到这一点会很有帮助。 :)【参考方案3】:

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

例如

    var products= context.Products
        .FromSql("EXECUTE dbo.GetProducts")
        .ToList();

与参数一起使用

    var productCategory= "Electronics";

    var product = context.Products
        .FromSql("EXECUTE dbo.GetProductByCategory 0", productCategory)
        .ToList();

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

    var product = context.Product
        .FromSql("EXECUTE dbo.GetProductByName  @productCategory", productCategory)
        .ToList();

执行 RAW SQL 查询或存储过程存在一定的限制。您不能将其用于 INSERT/UPDATE/DELETE。如果要执行 INSERT、UPDATE、DELETE 查询,请使用 ExecuteSqlCommand

    var categoryName = "Electronics";
    dataContext.Database
               .ExecuteSqlCommand("dbo.InsertCategory @p0", categoryName);

【讨论】:

方法ExecuteSqlCommand 现已过时。请参阅ExecuteSqlCommand Method 可能的替代方案是ExecuteSqlRawExecuteSqlInterpolated【参考方案4】:

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

您需要通过从 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;

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

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;

最后,使用 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;

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

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();
    
  

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

List<MyType> myTypeList = new List<MyType>();
using(var context = new MyDbContext())

  myTypeList = context.LoadStoredProc("StoredProcedureName")
  .WithSqlParam("firstparamname", firstParamValue)
  .WithSqlParam("secondparamname", secondParamValue).
  .ExecureStoredProc<MyType>();

【讨论】:

【参考方案5】:

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

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

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

public class MySPModel

    public int Id get; set;
    public string Name get; set;

第 2 步。

然后您必须为您的 SP 将一个 DbQuery 属性添加到您的 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
        ...

第 3 步。

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

var result = await _context.Query<MySPModel>().AsNoTracking().FromSql(string.Format("EXEC 0 1", functionName, parameter)).ToListAsync();

我使用的是通用 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();

希望对某人有所帮助!!!

【讨论】:

我希望避免这样做。我编写了自己的库(前 EF)来处理动态数据集(例如连接等),然后完美地工作。每个人都在抱怨,所以我搬到了 EF,你现在不能这样做。你猜怎么着,他们让我重写我的库。我的反应并不愉快。【参考方案6】:

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

".AsSqlServer()" -- 不存在。

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

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

【讨论】:

【参考方案7】:

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

我在this post 中写了更多关于它的内容。

【讨论】:

【参考方案8】:

目前 EF 7 或 EF Core 不支持在设计器中导入存储过程并直接调用它们的旧方法。您可以查看路线图以了解未来将支持的内容: EF core roadmap.

所以现在最好使用 SqlConnection 来调用存储过程或任何原始查询,因为您不需要整个 EF 来完成这项工作。这里有两个例子:

调用返回单个值的存储过程。在这种情况下为字符串。

CREATE PROCEDURE [dbo].[Test]
    @UserName nvarchar(50)
AS
BEGIN
    SELECT 'Name is: '+@UserName;
END

调用返回列表的存储过程。

CREATE PROCEDURE [dbo].[TestList]
AS
BEGIN
    SELECT [UserName], [Id] FROM [dbo].[AspNetUsers]
END

要调用这些存储过程,最好创建一个包含所有这些函数的静态类,例如,我称之为DataAccess类,如下:

public static class DataAccess

    
        private static string connectionString = ""; //Your connection string
        public static string Test(String userName)
        
            using (SqlConnection conn = new SqlConnection(connectionString))
            
                conn.Open();

                // 1.  create a command object identifying the stored procedure
                SqlCommand cmd = new SqlCommand("dbo.Test", conn);

                // 2. set the command object so it knows to execute a stored procedure
                cmd.CommandType = CommandType.StoredProcedure;

                // 3. add parameter to command, which will be passed to the stored procedure
                cmd.Parameters.Add(new SqlParameter("@UserName", userName));

                // execute the command
                using (var rdr = cmd.ExecuteReader())
                
                    if (rdr.Read())
                    
                        return rdr[0].ToString();
                    
                    else
                    
                        return null;
                    
                
            
        

        public static IList<Users> TestList()
        
            using (SqlConnection conn = new SqlConnection(connectionString))
            
                conn.Open();

                // 1.  create a command object identifying the stored procedure
                SqlCommand cmd = new SqlCommand("dbo.TestList", conn);

                // 2. set the command object so it knows to execute a stored procedure
                cmd.CommandType = CommandType.StoredProcedure;

                // execute the command
                using (var rdr = cmd.ExecuteReader())
                
                    IList<Users> result = new List<Users>();
                    //3. Loop through rows
                    while (rdr.Read())
                    
                        //Get each column
                        result.Add(new Users()  UserName = (string)rdr.GetString(0), Id = rdr.GetString(1) );
                    
                    return result;
                
            

        
    

而用户类是这样的:

public class Users

     public string UserName  set; get; 
     public string Id  set; get; 

顺便说一句,您无需担心为每个对 sql 的请求打开和关闭连接的性能,因为 asp.net 正在为您管理这些。 我希望这会有所帮助。

【讨论】:

【参考方案9】:

我发现这个扩展非常有用:StoredProcedureEFCore

那么用法是这样的

List<Model> rows = null;

ctx.LoadStoredProc("dbo.ListAll")
   .AddParam("limit", 300L)
   .AddParam("limitOut", out IOutParam<long> limitOut)
   .Exec(r => rows = r.ToList<Model>());

long limitOutValue = limitOut.Value;

ctx.LoadStoredProc("dbo.ReturnBoolean")
   .AddParam("boolean_to_return", true)
   .ReturnValue(out IOutParam<bool> retParam)
   .ExecNonQuery();

bool b = retParam.Value;

ctx.LoadStoredProc("dbo.ListAll")
   .AddParam("limit", 1L)
   .ExecScalar(out long l);

【讨论】:

【参考方案10】:

由于我的团队已经同意我们将使用通用 UnitOfWork 模式,所以我在创建自己的解决方案时采取了一些大家的解决方案。

我还发布了一些 UnitOfWork 代码,以便您了解我为什么需要这样实现它。

public interface IUnitOfWork : IDisposable

    DbContext Context  get; 
    Task<List<T>> ExecuteStoredProc<T>(string storedProcName, Dictionary<string, object> procParams) where T : class;

接口实现:

public class UnitOfWork : IUnitOfWork

    public DbContext Context  get; private set; 

/// <summary>
/// Execute procedure from database using it's name and params that is protected from the SQL injection attacks.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="storedProcName">Name of the procedure that should be executed.</param>
/// <param name="procParams">Dictionary of params that procedure takes. </param>
/// <returns>List of objects that are mapped in T type, returned by procedure.</returns>
    public async Task<List<T>> ExecuteStoredProc<T>(string storedProcName, Dictionary<string, object> procParams) where T : class
    
        DbConnection conn = Context.Database.GetDbConnection();
        try
        
            if(conn.State != ConnectionState.Open)
                await conn.OpenAsync();

            await using (DbCommand command = conn.CreateCommand())
            
                command.CommandText = storedProcName;
                command.CommandType = CommandType.StoredProcedure;

                foreach (KeyValuePair<string, object> procParam in procParams)
                
                    DbParameter param = command.CreateParameter();
                    param.ParameterName = procParam.Key;
                    param.Value = procParam.Value;
                    command.Parameters.Add(param);
                

                DbDataReader reader = await command.ExecuteReaderAsync();
                List<T> objList = new List<T>();
                IEnumerable<PropertyInfo> props = typeof(T).GetRuntimeProperties();
                Dictionary<string, DbColumn> colMapping = reader.GetColumnSchema()
                    .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
                    .ToDictionary(key => key.ColumnName.ToLower());

                if (reader.HasRows)
                
                    while (await reader.ReadAsync())
                    
                        T obj = Activator.CreateInstance<T>();
                        foreach (PropertyInfo prop in props)
                        
                            object val =
                                reader.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
                            prop.SetValue(obj, val == DBNull.Value ? null : val);
                        
                        objList.Add(obj);
                    
                
                reader.Dispose();

                return objList;
            
        
        catch (Exception e)
        
            System.Diagnostics.Debug.WriteLine(e.Message, e.InnerException);
        
        finally
        
            conn.Close();
        

        return null; // default state
    

示例用法如下:

public class MyService : IMyService 

        private readonly IUnitOfWork _uow;
        public MyService(IUnitOfWork uow)
        
            _uow = uow;
        
        
        public async Task<List<TreeViewModel>> GetTreeOptions()
        
            var procParams = new Dictionary<string, object>()
            
                "@Id", 2
            ;
            var result = await _uow.ExecuteStoredProc<TreeViewModel>("FetchTreeProcedure", procParams);
            return result;
        

【讨论】:

【参考方案11】:

使用 mysql 连接器和 Entity Framework Core 2.0

我的问题是我遇到了像 fx. Ex.Message = "'FromSql' 操作的结果中不存在所需的列 'body'。"。因此,为了以这种方式通过存储过程获取行,您必须返回与 DBSet 关联的实体类型的所有列,即使您不需要为当前请求访问所有列。

var result = _context.DBSetName.FromSql($"call storedProcedureName()").ToList(); 

或带参数

var result = _context.DBSetName.FromSql($"call storedProcedureName(optionalParam1)").ToList(); 

【讨论】:

【参考方案12】:

我将 Entity Framework Core 与我的 ASP.Net Core 3.x WebAPI 一起使用。我希望我的终点之一只是执行一个特定的存储过程,这就是我需要的代码:

namespace MikesBank.Controllers

    [Route("api/[controller]")]
    [ApiController]
    public class ResetController : ControllerBase
    
        private readonly MikesBankContext _context;

        public ResetController(MikesBankContext context)
        
            _context = context;
        

        [HttpGet]
        public async Task<ActionResult> Get()
        
            try
            
                using (DbConnection conn = _context.Database.GetDbConnection())
                
                    if (conn.State != System.Data.ConnectionState.Open)
                        conn.Open();
                    var cmd = conn.CreateCommand();
                    cmd.CommandText = "Reset_Data";
                    await cmd.ExecuteNonQueryAsync();
                
                return new OkObjectResult(1);
            
            catch (Exception ex)
            
                return new BadRequestObjectResult(ex.Message);
            
        
    

注意我需要如何获取已注入的DbContext,但我还需要Open() 这个连接。

【讨论】:

【参考方案13】:

我使用了 https://github.com/verdie-g/StoredProcedureEFCore,EnterpriseLibrary.Data.NetCore,EFCor.SqlServer,EFCore.Tools 的 StoredProcedureEFCore nuget 包

我尝试使用 Repository pattern 的 DbFirst 方法.. 我想是的

startup.cs

ConfigureServices(IServiceCollection services)
    services.AddDbContext<AppDbContext>(opt => opt
                   .UseSqlServer(Configuration.GetConnectionString("SampleConnectionString")));
    services.AddScoped<ISomeDAL, SomeDAL>();


            
    public  class AppDbContext : DbContext
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    

ISomeDAl 接口有GetPropertiesResponse GetAllPropertiesByCity(int CityId);

public class SomeDAL : ISomeDAL

     private readonly AppDbContext context;

     public SomeDAL(AppDbContext context)
         
             this.context = context;
         
     public  GetPropertiesResponse GetAllPropertiesByCity(int CityId)
     
         //Create Required Objects for response 
         //wont support ref Objects through params
         context.LoadStoredProc(SQL_STATEMENT)
            .AddParam("CityID", CityId).Exec( r =>
             
                  while (r.Read())
                  

                       ORMapping<GenericRespStatus> orm = new  ORMapping<GenericRespStatus>();
                       orm.AssignObject(r, _Status);
                  

                  if (r.NextResult())
                  

                       while (r.Read())
                       
                           Property = new Property();
                           ORMapping<Property> orm = new ORMapping<Property>();
                           orm.AssignObject(r, Property);
                           _propertyDetailsResult.Add(Property);
                       
                      
           );
    return new GetPropertiesResponseStatus=_Status,PropertyDetails=_propertyDetailsResult; 
    


public class GetPropertiesResponse

     public GenericRespStatus Status;
     public List<Property> PropertyDetails;
     public GetPropertiesResponse()
         
             PropertyDetails = new List<Property>();
         

public class GenericRespStatus

     public int ResCode  get; set; 
     public string ResMsg  get; set; 

internal class ORMapping<T>

    public void AssignObject(IDataReader record, T myClass)
    
        PropertyInfo[] propertyInfos = typeof(T).GetProperties();
        for (int i = 0; i < record.FieldCount; i++)
        
            if (propertyInfos.Any(obj => obj.Name == record.GetName(i))) //&& record.GetValue(i) != DBNull.Value
            
                propertyInfos.Single(obj => obj.Name == record.GetName(i)).SetValue(myClass, Convert.ChangeType(record.GetValue(i), record.GetFieldType(i)));
            
        
    

【讨论】:

【参考方案14】:

如果您使用 EntityFrameworkCore 从 Informix 执行存储过程,则需要包含命令 EXECUTE PROCEDURE

var spresult = _informixContext.procdata.FromSql("EXECUTE PROCEDURE dummyproc (0,1,2)", parameters: new[]  p0, p1,p2 ).ToList();

【讨论】:

【参考方案15】:

无需执行任何操作...当您为代码优先方法初始化创建 dbcontext 时 fluent API 区域下方的命名空间列出 sp 并在您想要的另一个地方使用它。

public partial class JobScheduleSmsEntities : DbContext

    public JobScheduleSmsEntities()
        : base("name=JobScheduleSmsEntities")
    
        Database.SetInitializer<JobScheduleSmsEntities>(new CreateDatabaseIfNotExists<JobScheduleSmsEntities>());
    

    public virtual DbSet<Customer> Customers  get; set; 
    public virtual DbSet<ReachargeDetail> ReachargeDetails  get; set; 
    public virtual DbSet<RoleMaster> RoleMasters  get; set; 

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        //modelBuilder.Types().Configure(t => t.MapToStoredProcedures());

        //modelBuilder.Entity<RoleMaster>()
        //     .HasMany(e => e.Customers)
        //     .WithRequired(e => e.RoleMaster)
        //     .HasForeignKey(e => e.RoleID)
        //     .WillCascadeOnDelete(false);
    
    public virtual List<Sp_CustomerDetails02> Sp_CustomerDetails()
    
        //return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Sp_CustomerDetails02>("Sp_CustomerDetails");
        //  this.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails");
        using (JobScheduleSmsEntities db = new JobScheduleSmsEntities())
        
           return db.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails").ToList();

        

    


public partial class Sp_CustomerDetails02

    public long? ID  get; set; 
    public string Name  get; set; 
    public string CustomerID  get; set; 
    public long? CustID  get; set; 
    public long? Customer_ID  get; set; 
    public decimal? Amount  get; set; 
    public DateTime? StartDate  get; set; 
    public DateTime? EndDate  get; set; 
    public int? CountDay  get; set; 
    public int? EndDateCountDay  get; set; 
    public DateTime? RenewDate  get; set; 
    public bool? IsSMS  get; set; 
    public bool? IsActive  get; set; 
    public string Contact  get; set; 

【讨论】:

【参考方案16】:

根据存储过程的 Select 查询中的字段创建特殊类。 例如我将这个类称为 ResulData

添加到你 EF 的上下文中

modelBuilder.Entity<ResultData>(e =>
        
            e.HasNoKey();
        );

这是一个使用存储过程获取数据的示例函数

public async Task<IEnumerable<ResultData>> GetDetailsData(int id, string name)

    var pId = new SqlParameter("@Id", id);
  var pName = new SqlParameter("@Name", name);
    return await _context.Set<ResultData>()
             .FromSqlRaw("Execute sp_GetDeailsData  @Id @Name", parameters: new[]  pId, pName )
            .ToArrayAsync();

【讨论】:

【参考方案17】:

我正在使用 Entity Framework Core。对存储过程和即席查询的支持不像在 Framework 中那样流畅。

以下是一些示例供将来参考:

根据存储过程的结果填充实体列表:

[dbo].[GetUnarchivedJobs] 存储过程返回与Job 实体匹配的记录列表。

我们可以使用Jobs 属性上的FromSqlInterpolated() 方法来调用存储过程并返回Job 的列表。

NoTracking() 用于加速性能,在这种情况下我们不会更新实体。

using Microsoft.EntityFrameworkCore;

public class DbContext : Microsoft.EntityFrameworkCore.DbContext

    protected DbSet<Job> Jobs  get; set; 

    // Populate a list of entities from the results of a stored procedure
    public Task<List<Job>> GetUnarchivedJobs(int maxQty, CancellationToken cancellationToken) =>
        Jobs.FromSqlInterpolated($"EXEC [dbo].[GetUnarchivedJobs] @MaxQty = maxQty")
            .AsNoTracking()
            .ToListAsync(cancellationToken)
            ;

    public DbContext(DbContextOptions<DbContext> options) : base(options)  

将整数数组发送到存储过程:

[dbo].[SetJobListArchiveFlags] 存储过程有一个 integer_list_tbltype 类型的参数。

我们需要创建一个DataTable 来匹配integer_list_tbltype 类型,它有一个名为n 的列。

int 值需要添加到DataTable

SqlParameter 用于将填充的DataTable 传递给存储过程。

因为我们没有填充任何实体,所以我们需要使用 Database 属性上的方法来调用存储过程。

using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Data;

public class DbContext : Microsoft.EntityFrameworkCore.DbContext

    // Send an array of integers to a stored procedure
    public async Task<int> MarkJobsAsArchived(IEnumerable<int> jobIds, CancellationToken cancellationToken)
    
        // This DataTable matches the `integer_list_tbltype` db type
        var table = new DataTable();
        table.Columns.Add("n", typeof(int));
        foreach (var id in jobIds) table.Rows.Add(id);

        var parameter = new SqlParameter("@jobIds", SqlDbType.Structured);
        parameter.Value = table;
        parameter.TypeName = "integer_list_tbltype";

        var rowsUpdatedCount = await Database.ExecuteSqlRawAsync("EXEC [dbo].[SetJobListArchiveFlags] @jobIds", new[]  parameter , cancellationToken);
        return rowsUpdatedCount;
    

【讨论】:

【参考方案18】:

我们应该在模型中为 db 上下文创建一个带有 DbQuery 而不是 DbSet 的属性,如下所示...

public class MyContextContext : DbContext

    public virtual DbQuery<CheckoutInvoiceModel> CheckoutInvoice  get; set;  

比一个可以用来返回结果的方法

public async Task<IEnumerable<CheckoutInvoiceModel>> GetLabReceiptByReceiptNo(string labReceiptNo)

    var listing = new List<CheckoutInvoiceModel>();
    try
    
        var sqlCommand = $@"[dbo].[Checkout_GetLabReceiptByReceiptNo] labReceiptNo";
        
        listing = await db.Set<CheckoutInvoiceModel>().FromSqlRaw(sqlCommand).ToListAsync();                       
                        
    
    catch (Exception ex)
    
        return null;
    
    return listing;

从上面的例子中,我们可以使用任何你喜欢的选项。

希望对您有所帮助!

【讨论】:

您从不使用此属性CheckoutInvoice。而且还不清楚LoadStoredProc来自哪里。 是的,这可以在 DbContext 中添加 CheckoutInvoice,但应该是 DbQuery,而不是 DbSet。好吧,LoadStoredProc 来自 注意:我使用的是核心版本:3.18 谢谢@GertArnold

以上是关于如何在 Entity Framework Core 中运行存储过程?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Entity Framework Core 2 中播种?

如何处置 Entity Framework Core 内存数据库

如何在 Entity Framework Core 中将连接字符串传递给 DBContext?

如何使用 Entity Framework Core 正确保存 DateTime?

如何在 .NET Core 3.0 Entity Framework 中执行组加入?

请问在 .NET Core 中如何让 Entity Framework Core 在日志中记录由 LINQ 生成的SQL语句?