【发布时间】:2020-12-09 12:35:02
【问题描述】:
我有一个方法发生两个 SQL 查询,例如:
public async Task<Result> SomeMethod(string p1, string p2)
{
Context.Database.CommandTimeout = 60;
var param1 = new SqlParameter("@param1", SqlDbType.VarChar, 8);
param1.Direction = ParameterDirection.Output;
var param2 = new SqlParameter("@param2", SqlDbType.VarChar, 8);
param2.Direction = ParameterDirection.Output;
var param3 = new SqlParameter("@param3", SqlDbType.BigInt);
param3.Value = DBNull.Value;
var param4 = new SqlParameter("@param4", SqlDbType.BigInt);
param4.Value = DBNull.Value;
var result1 = await Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
new SqlParameter("@param1", p1), param1, param2
).FirstOrDefaultAsync();
var result2 = await Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
new SqlParameter("@param1", p2), param3, param4
).FirstOrDefaultAsync();
return new Result { Result1 = result1, Result2 = result2 };
}
不要介意变量名称只是一个例子。问题是第二个查询没有执行并抛出错误:
"System.ObjectDisposedException: 'ObjectContext 实例已被 处置,不能再用于需要一个操作 连接。'"
两个查询都有效。如果我交换查询,第二个总是失败。我不知道为什么第二个查询不起作用。我用谷歌搜索了这个错误,发现它通常出现在您尝试将查询结果分配给某个实体并在上下文范围之外使用它时,由于 EF 的延迟加载而失败,但这里的字符串是从程序返回的,而不是实体。我看不出为什么上下文应该在第一次查询之后处理。
详情请咨询我。
【问题讨论】:
-
相反,有一个非常明确的原因表明 EF 被滥用(也就是说,EF 是一个 ORM,而不是 ADO.NET 的替代品)。
SomeMethod正在使用已被释放的全局DbContext实例。如果要从 DbContext 调用存储过程,请将代码放在DbContext类本身中,并使用其内置方法执行 SQL 查询,而不是通过数据库连接。在 EF Core 中,您可以使用FromSql、FromSqlRaw或FromSqlInterpolated -
感谢您的回复@PanagiotisKanavos,我知道有些技术可能不是故意使用的,但仍然很奇怪,在执行第一个查询后,上下文在没有任何先决条件的情况下被释放。对我来说这看起来很不清楚,有什么方法可以在不对当前实现进行重大更改的情况下修复它?
-
一点都不奇怪,挺清楚的。
Context来自哪里?它是被处置的静态属性吗?这是在 ASP.NET 控制器中使用的吗?请求完成后立即释放注入的 DbContext。解决方法是首先将该方法放在上下文类中,消除关于它是否尝试使用死上下文的任何不确定性。 -
另一个常见的原因是试图从 singleton 服务(例如 BackgroundService)中使用像 DbContext 这样的范围服务。当该实例被释放时,单例会留下一个死对象。这在Consuming a scoped service in a background task 中有解释
-
@Storm,你的方法看起来不错。这种情况下的常见问题是您在其他地方错过了
await。
标签: c# asp.net entity-framework asp.net-core entity-framework-core