如何获取Entity Framework在执行savechanges()前或执行后所生成的SQL语句

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何获取Entity Framework在执行savechanges()前或执行后所生成的SQL语句相关的知识,希望对你有一定的参考价值。

是程序获取,不是追踪查看。需要或取得不是查询功能的,是插入、更新、删除相关的语句

参考技术A oracle 10g的DBMS_XPLAN包中display_cursor函数不同于display函数,display_cursor用于显示SQL语句的真实的执行计划,在大多数情况下,
显示真实的执行计划有助于更好的分析SQL语句的全过程,尤其是运行此SQL语句实时的I/O开销。通过对比预估的I/O与真实的I/O开销来判断
SQL语句所存在问题,如缺少统计信息,SQL语句执行的次数,根据实际中间结果集的大小来选择合适的连接方式等。本文仅仅讲述
display_cursor函数的使用。

一、display_cursor函数用法
1、display_cursor函数语法

DBMS_XPLAN.DISPLAY_CURSOR(
sql_id IN VARCHAR2 DEFAULT NULL,
cursor_child_no IN NUMBER DEFAULT NULL,
format IN VARCHAR2 DEFAULT 'TYPICAL');

2、display_cursor函数参数描述
sql_id
指定位于库缓存执行计划中SQL语句的父游标。默认值为null。当使用默认值时当前会话的最后一条SQL语句的执行计划将被返回
可以通过查询V$SQL 或V$SQLAREA的SQL_ID列来获得SQL语句的SQL_ID。
cursor_child_no
指定父游标下子游标的序号。即指定被返回执行计划的SQL语句的子游标。默认值为0。如果为null,则sql_id所指父游标下所有子游标
的执行计划都将被返回。
format
控制SQL语句执行计划的输出部分,即哪些可以显示哪些不显示。使用与display函数的format参数与修饰符在这里同样适用。
除此之外当在开启statistics_level=all时或使用gather_plan_statistics提示可以获得执行计划中实时的统计信息
有关详细的format格式描述请参考:dbms_xplan之display函数的使用 中format参数的描述

下面给出启用统计信息时format新增的修饰符
iostats 控制I/O统计的显示
last 默认,显示所有执行计算过的统计。如果指定该值,则只显示最后一次执行的统计信息
memstats 控制pga相关统计的显示
allstats 此为iostats memstats的快捷方式,即allstats包含了iostats和memstats
run_stats_last 等同于iostats last。只能用于oracle 10g R1
run_stats_tot 等同于iostats。只能用于oracle 10g R1

抓一个最近一小时最消耗IO的SQL:
SELECT sql_id, COUNT(*)
FROM gv$active_session_history ash, gv$event_name evt
WHERE ash.sample_time > SYSDATE - 1 / 24
AND ash.session_state = 'WAITING'
AND ash.event_id = evt.event_id
AND evt.wait_class = 'User I/O'
GROUP BY sql_id
ORDER BY COUNT(*) DESC;

执行上面的SQL:
SQL> SELECT sql_id, COUNT(*)
FROM gv$active_session_history ash, gv$event_name evt
2 3 WHERE ash.sample_time > SYSDATE - 1 / 24
4 AND ash.session_state = 'WAITING'
5 AND ash.event_id = evt.event_id
6 AND evt.wait_class = 'User I/O'
7 GROUP BY sql_id
8 ORDER BY COUNT(*) DESC;

SQL_ID COUNT(*)
------------- ----------
g7fu6qba82m6b 668
63r47zyphdk06 526
9f5m4wd88nc1h 514
593p47drw5fhk 232
br91w16jzy4fu 120
4fvwyjpnh6tp7 78
gm0nrbfuj8kzr 70
2184k363hw4xd 68
gc4dajs7g5myy 46
8vrk9sfuwfdgq 42
ccpnb4dwdmq21 40

查看SQL的执行计划:
SELECT * FROM TABLE(dbms_xplan.display_cursor('g7fu6qba82m6b'));

在SQLPLUS中执行:
SQL> set pagesize 2000
SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor('g7fu6qba82m6b'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
SQL_ID g7fu6qba82m6b, child number 0
-------------------------------------
UPDATE "CPDDS_PDATA"."CDM_LEDGER" SET "CSTM_NAME" = :a1,"CSTM_NO" =
:a2,"PAPER_TYPE" = :a3,"PAPER_NO" = :a4,"CURR_TYPE" = :a5,"SVT_NO" =
:a6,"BAL_DIR" = :a7,"BAL" = :a8,"AVAL_BAL" = :a9,"NORM_FRATIO" =
:a10,"PK_BAL" = :a11,"DR_ACCU" = :a12,"CR_ACCU" = :a13,"LAST_TRAN_DATE" =
:a14,"LAST_TRAN_TIME" = :a15,"PRT_LINE_NUM" = :a16,"NOREG_PK_REC_NUM" =
:a17,"PK_NO" = :a18,"PWD" = :a19,"FLAG" = :a20,"FRZ_FLAG" =
:a21,"CARD_HOLD_FLAG" = :a22,"PK_HOLD_FLAG" = :a23,"BGN_INT_DATE" =
:a24,"OPEN_DATE" = :a25,"ACC_HOLD_FLAG" = :a26,"CLS_DATE" =
:a27,"OPEN_TLR" = :a28,"CLS_TLR" = :a29,"CLS_INT" = :a30,"OPEN_INST" =
:a31,"ADD_NUM" = :a32,"DAC" = :a33,"FRZ_TIMES1" = :a34,"FRZ_TIMES2" =
:a35,"HOST_SEQNO" = :a36,"D_UPDATE_DATE" = :a37 WHERE "ACC" = :b0

Plan hash value: 319441092

-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | 3 (100)| |
| 1 | UPDATE | CDM_LEDGER | | | | |
|* 2 | INDEX UNIQUE SCAN| I_CDM_LEDGER | 1 | 269 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ACC"=:B0)

29 rows selected.

总结
1、与display函数不同,display_cursor显示的为真实的执行计划
2、对于format参数,使用与display函数的各个值,同样适用于display_cursor函数
3、当statistics_level为all或使用gather_plan_statistics提示可以获得执行时的统计信息
4、根据真实与预估的统计信息可以初步判断SQL效率低下的原因,如统计信息的准确性、主要的开销位于那些步骤等

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

【中文标题】如何在 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在执行savechanges()前或执行后所生成的SQL语句的主要内容,如果未能解决你的问题,请参考以下文章

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

Entity Framework Core 中的动态查询执行

如何获取Entity Framework生成的SQL语句

如何使用 Entity Framework Core 获取主键值

Entity Framework 6 中具有多个记录集的存储过程

Entity Framework 学习系列 - 认识理解Entity Framework