【问题标题】:Entity framework SqlQuery failing with no reason (ObjectDisposedException)实体框架 SqlQuery 无故失败 (ObjectDisposedException)
【发布时间】: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 中,您可以使用 FromSqlFromSqlRawFromSqlInterpolated
  • 感谢您的回复@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


【解决方案1】:

通常,此错误与延迟加载有关,与此用法无关。我怀疑是代码的async 性质。

首先尝试使第一次调用同步:

var result1 = Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
            new SqlParameter("@param1", p1), param1, param2
            ).FirstOrDefault();

var result2 = await Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
           new SqlParameter("@param1", p2), param3, param4
           ).FirstOrDefaultAsync();

如果可行,请切换以使第二次调用同步:

var result1 = await Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
            new SqlParameter("@param1", p1), param1, param2
            ).FirstOrDefaultAsync();

var result2 = Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
           new SqlParameter("@param1", p2), param3, param4
           ).FirstOrDefault();

我希望如果第一个有效,第二个仍然会失败。然后尝试在第一个调用之前添加一个“虚拟”调用:

var dummy2 = await Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
           new SqlParameter("@param1", p2), param3, param4
           ).FirstOrDefaultAsync();

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

如果这在result1 执行中可靠地失败,那么似乎 SqlQuery 可能存在错误,可能与线程相关。如果是这种情况,或者有疑问,您应该能够通过为每次调用显式确定 DbContext 范围来解决此问题:

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;

string result1, result2;

using (var context = ContextFactory.Create())
{
    result1 = await Context.Database.SqlQuery<string>("[Some_Procedure] @param1,@param2,@param3",
            new SqlParameter("@param1", p1), param1, param2
            ).FirstOrDefaultAsync();
}
using (var context = ContextFactory.Create())
{
    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 };

其中 ContextFactory 是注入此服务的新依赖项,可以创建新的 DbContext。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-18
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多