如何为 DbContext 设置 CommandTimeout

本文关键字:设置 CommandTimeout DbContext | 更新日期: 2023-09-27 18:33:56

我正在寻找一种为DbContext设置CommandTimeout的方法。搜索后,我通过将 DbContext 转换为 ObjectContext 并为 objectContext 的 CommandTimeout 属性设置值找到了方法。

var objectContext = (this.DbContext as IObjectContextAdapter).ObjectContext;

但我必须使用 DbContext。

如何为 DbContext 设置 CommandTimeout

它将适用于您的方法。

或对其进行子类(来自 msdn 论坛(

public class YourContext : DbContext
{
  public YourContext()
    : base("YourConnectionString")
  {
    // Get the ObjectContext related to this DbContext
    var objectContext = (this as IObjectContextAdapter).ObjectContext;
    // Sets the command timeout for all the commands
    objectContext.CommandTimeout = 120;
  }
}
var ctx = new DbContext();
ctx.Database.CommandTimeout = 120;

这可能会对您有所帮助。

public class MyContext : DbContext
{    
    public MyContext () : base(ContextHelper.CreateConnection("my connection string"), true)
    {
        ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
    }
}

发现更改 .tt 文件对我有用,因为我以后不会丢失更改:

添加此行:

((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;

紧接在 DbContext 创建器之后和 !loader 之前。懒惰构造:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
    public <#=code.Escape(container)#>()
        : base("name=<#=container.Name#>")
    {
        ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
<#
if (!loader.IsLazyLoadingEnabled(container))

然后,它应该出现在您生成的上下文中.cs:

public MyEntities()
            : base("name=MyEntities")
        {
            ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
        }

以下是我在使用 EDMX 文件时解决此问题的方法。此解决方案更改默认 T4 模板,使生成的类继承自自定义 DbContext 类,该类指定默认命令超时以及用于更改该超时的属性。

我使用的是Visual Studio 2012和EF 5.0。您的体验可能与其他版本不同。

创建自定义数据库上下文类

public class CustomDbContext : DbContext
{
    ObjectContext _objectContext;
    public CustomDbContext( string nameOrConnectionString )
        : base( nameOrConnectionString )
    {
        var adapter = (( IObjectContextAdapter) this);
        _objectContext = adapter.ObjectContext;
        if ( _objectContext == null )
        {
            throw new Exception( "ObjectContext is null." );    
        }
        _objectContext.CommandTimeout = Settings.Default.DefaultCommandTimeoutSeconds;
    }
    public int? CommandTimeout
    {
        get
        {
            return _objectContext.CommandTimeout;
        }
        set
        {
            _objectContext.CommandTimeout = value;
        }
    }
}

这有一个可选功能:我没有对默认命令超时进行硬编码。相反,我从项目设置中加载它,以便我可以更改配置文件中的值。如何设置和使用项目设置不在本答案的讨论范围内。

我也没有对连接字符串或连接字符串名称进行硬编码。它已经由生成的上下文类传递到构造函数中,因此在此处对其进行硬编码是没有意义的。这不是什么新鲜事;EDMX 文件已经为您生成了以下构造函数,因此我们只是传递值。

public MyEntities()
    : base("name=MyEntities")
{
}

(这将指示 EF 从配置文件加载名为"MyEntities"的连接字符串。

如果ObjectContext为空,我将抛出自定义异常。我认为永远不会,但这比获得NullReferenceException更有意义。

我将ObjectContext存储在字段中,以便我可以创建一个属性来访问它以覆盖默认值。

修改实体上下文 T4 模板

在"解决方案资源管理器"中,展开 EDMX 文件,以便看到 T4 模板。他们有一个.tt扩展名。

双击"MyModel.Context.tt"文件将其打开。在第 57 行附近,您应该看到以下内容:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext

此模板行生成继承 DbContext 的"MyEntities"类的类定义。

更改该行,以便生成的类继承 CustomDbContext,改为:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : CustomDbContext

保存此文件后,它应该会重新生成类。如果没有,您可以右键单击 EDMX 文件并选择"运行自定义工具"。如果展开 EDMX 文件下的"MyModel.Context.tt"文件,您将看到"MyModel.Context.cs"。这就是生成的文件。打开它,您应该看到它现在继承了CustomDbContext

public partial class MyEntities : CustomDbContext

仅此而已。

问题

将上下文类从 DbContext 更改为 CustomDbContext 后,如果您尝试使用"具有读/写操作和视图的控制器,使用实体框架"模板添加新的 MVC 控制器类,Visual Studio 将给出错误。它会说"不支持的上下文类型"。若要解决此问题,请打开生成的"MyModel.Context.cs"类,并暂时将其继承的类型更改回 DbContext 。添加新控制器后,您可以将其更改回 CustomDbContext

我喜欢扩展方法:

public static class DbContextExtensions
{
   public static void SetCommandTimeout(this ObjectContext dbContext,
       int TimeOut)
   {
       dbContext.CommandTimeout = TimeOut;
   }
}

然后简单地

((IObjectContextAdapter)cx).ObjectContext.SetCommandTimeout(300);

如果它可以提供帮助,这是 VB.Net 解决方案:

Dim objectContext As Objects.ObjectContext = CType(Me,IObjectContextAdapter).ObjectContext
objectContext.commandTimeout = connectionTimeout

这类似于上面@Glazed使用的方法,但我的方法也是使用自定义 DbContext 类,但我正在做相反的事情。 我没有修改 T4 模板(.edmx 下的 .tt 文件(,而是实际上从生成的 MyEntities 类继承,如下所示:

由 T4 模板生成的 MyEntities 类:

public partial class MyEntities : DbContext
{
    public MyEntities()
        : base("name=MyConnectionStringName")
    {
    }
...
}

然后创建一个新的自定义类作为 MyEntities 的包装器,如下所示:

public class MyEntitiesContainer : MyEntities
{
    private static readonly int _DEFAULT_TIMEOUT = 100;
    public MyEntitiesContainer()
    {
        ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = _DEFAULT_TIMEOUT;
    }
    //Use this method to temporarily override the default timeout
    public void SetCommandTimeout(int commandTimeout)
    {
        ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = commandTimeout;
    }
    //Use this method to reset the timeout back to default
    public void ResetCommandTimeout()
    {
        ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = _COMMAND_TIMEOUT;
    }
}

在代码中,实例化 Container 类,如果需要对特定命令使用自定义超时,请使用提供的方法手动设置它。

using (var db = new MyEntitiesContainer()) {
    db.SetCommandTimeout(300);
    db.DoSomeLongCommand();
    db.ResetCommandTimeout();
    db.DoShorterCommand1();
    db.DoShorterCommand2();
    ...
}

此方法的好处是,您还可以为 Container 类创建一个接口,并将该接口的实例与依赖项注入一起使用,然后除了可以更轻松地控制命令超时和可以为其创建方法的对象上下文的其他属性(例如延迟加载、 等(。

我来这里寻找一个为单个命令设置超时的示例,而不是这样的全局设置。

我认为它可能会帮助某人举例说明我是如何实现这一目标的:

var sqlCmd = new SqlCommand(sql, context.Database.Connection as SqlConnection);
sqlCmd.Parameters.Add(idParam);
sqlCmd.CommandTimeout = 90;
if (sqlCmd.Connection.State == System.Data.ConnectionState.Closed)
{
    sqlCmd.Connection.Open();
}
sqlCmd.ExecuteNonQuery();
sqlCmd.Connection.Close();

@PerryTribolet的答案看起来不错,但它确实适用于EF5。 对于 EF,这里有一种方法可以做到这一点:创建一个 ObjectContext,在其上设置 CommandTimeout,然后从 ObjectContext 创建一个 DBContext。我将标志设置为将两个对象一起处理。 下面是 VB.NET 中的一个示例:

        Dim context As New ObjectContext("name=Our_Entities")
        Dim dbcontext As New System.Data.Entity.DbContext(context, True)
        With context
            .CommandTimeout = 300 'DBCommandTimeout
        End With

当然,您不必使用"With"。