【问题标题】:entity framework set arithabort on for each query实体框架为每个查询设置 arithabort
【发布时间】:2019-10-12 01:49:05
【问题描述】:

我有一个实体框架查询,当转换为 SQL 时会在一秒钟内返回,但是当通过实体框架运行时会在一小时后超时(!) 我追查到,在执行实际查询之前,实体框架会执行:

set arithabort off

我正在寻找一种方法来配置 EF 不这样做,或者寻找一种方法来覆盖它。

我尝试了以下方法:

public partial class MyContext : DbContext
{
    public MyContext () : base("name=MyContext ")
    {
       Context.Database.ExecuteSqlCommand("set arithabort on");
    }

    public DbContext Context
    {
        get { return this; }
    }
}

但这在开始时只执行一次,并且在执行另一个查询时被覆盖。

【问题讨论】:

标签: entity-framework-6


【解决方案1】:

感谢@fiddler,添加了一个拦截器。感觉有点hackish,但确实有效。

public partial class IfcContext : DbContext, IIfcContext
{
    public MyContext() : base("name=MyContext")
    {
        ///used to set ArithAbort to on before each query
        DbInterception.Add(new Interceptor());
    }

    public DbContext Context
    {
        get { return this; }
    }
}


public class Interceptor : IDbCommandInterceptor
{

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        command.CommandText = "SET ARITHABORT ON; " + command.CommandText;
    }

    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {

    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }
}

【讨论】:

  • 这将在自身内部造成一些性能下降,为每个查询设置ARITHABORT。至少使用一些局部变量来存储其状态并仅在需要时执行该语句。
  • 可能值得注意,你不应该在实例构造函数中这样做——在静态构造函数中这样做——这只需要添加一次。 (DbInterception.Add(new Interceptor());)
  • 不要在上下文中添加拦截器,正因为如此,我得到了set arithabort部分添加的越来越多,我查询de DB的次数越多。您需要将拦截器注册到 ctor 内部的 DbConfiguration 实现中,所以您没有这个问题
【解决方案2】:

以 Solomon Rutzy answer 为基础,使用 EF6 的 StateChange 事件:

using System.Data;
using System.Data.Common;

namespace project.Data.Models
{
    abstract class ProjectDBContextBase: DbContext
    {
        internal ProjectDBContextBase(string nameOrConnectionString) : base(nameOrConnectionString)
        {
            this.Database.Connection.StateChange += new StateChangeEventHandler(OnStateChange);
        }

        protected static void OnStateChange(object sender, StateChangeEventArgs args)
        {
            if (args.OriginalState == ConnectionState.Closed
                && args.CurrentState == ConnectionState.Open)
            {
                using (DbCommand _Command = ((DbConnection)sender).CreateCommand())
                {
                    _Command.CommandType = CommandType.Text;
                    _Command.CommandText = "SET ARITHABORT ON;";
                    _Command.ExecuteNonQuery();
                }
            }
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        ...

这使用 System.Data.Common 的 DbCommand 代替 SqlCommand,使用 DbConnection 代替 SqlConnection。

SQL Profiler 跟踪确认,在连接打开时发送 SET ARITHABORT ON,然后在事务中执行任何其他命令。

【讨论】:

  • 我认为事件处理程序不会为您在后续查询之前执行提供任何保证,是吗?我会用它你...
  • @Shockwaver,如果在后续命令中连接仍然打开,则 SET ARITHABORT 保持打开状态。如果连接关闭然后重新打开,则事件将在执行任何其他命令之前再次触发。我希望我能理解你的问题。
  • 我的意思不同。如果您打开连接然后发送查询,我认为您无法保证事件处理程序(其中包含 ARITHABORT 查询)将在其他查询之前执行。这取决于处理程序的调用方式,我没有仔细研究 EF 代码,我可能是错的
猜你喜欢
  • 2022-01-01
  • 2020-01-25
  • 2011-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 2013-03-17
相关资源
最近更新 更多