如何获取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.SetSet()
。只需获取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 可能的替代方案是ExecuteSqlRaw
和ExecuteSqlInterpolated
。【参考方案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 返回两个名为 Id
和 Name
的列,那么您的新类应该类似于
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】:ExecuteSqlCommand
和ExecuteSqlCommandAsync
遇到了很多麻烦,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 来自 以上是关于如何获取Entity Framework在执行savechanges()前或执行后所生成的SQL语句的主要内容,如果未能解决你的问题,请参考以下文章
如何在 .NET Core 3.0 Entity Framework 中执行组加入?
Entity Framework Core 中的动态查询执行
如何使用 Entity Framework Core 获取主键值