如何为DbContext设置CommandTimeout?

时间:2012-05-11 10:26:37

标签: c# entity-framework-4 dbcontext command-timeout

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

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

但我必须使用DbContext。

10 个答案:

答案 0 :(得分:89)

它适用于您的方法。

或者将其子类化(来自msdn forum

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;
  }
}

答案 1 :(得分:20)

这可能会对你有帮助。

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

答案 2 :(得分:19)

var ctx = new DbContext();
ctx.Database.CommandTimeout = 120;

答案 3 :(得分:6)

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

添加以下行:

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

在DbContext创建者之后和!loader.IsLazy构造之前:

<#=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))

它应该出现在您生成的Context.cs中:

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

答案 4 :(得分:3)

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

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

创建自定义DbContext类

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从配置文件加载名为&#34; MyEntities&#34;的连接字符串。)

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

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

修改实体上下文T4模板

在Solution Explorer中,展开EDMX文件,以便看到T4模板。他们有.tt扩展名。

双击&#34; MyModel.Context.tt&#34;文件打开它。在第57行附近你应该看到:

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

此模板行生成&#34; MyEntities&#34;的定义。 class,继承DbContext。

更改行,以便生成的类继承CustomDbContext,而不是:

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

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

public partial class MyEntities : CustomDbContext

这就是它的全部内容。

问题

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

答案 5 :(得分:2)

我喜欢扩展方法:

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

然后简单地

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

答案 6 :(得分:1)

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

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

答案 7 :(得分:1)

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

我认为它可能会帮助某人有一个我如何实现这个目标的例子:

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

答案 8 :(得分:1)

这类似于上面@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 类创建一个接口,并通过依赖注入使用该接口的实例,然后除了可以更轻松地控制命令之外,您还可以在单​​元测试中模拟您的数据库您可以为其创建方法的对象上下文的超时和其他属性(例如延迟加载等)。

答案 9 :(得分:0)

@PerryTribolet的答案对于EF6看起来不错,但对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”。