如何为 DbContext 设置 CommandTimeout?

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

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

但是我必须使用 DbContext。

103145 次浏览

用你的方法就行。

或者子类化它(从 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;
}
}

这个可能对你有帮助。

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

如果可以的话,这就是 VB.Net 的解决方案:

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

我喜欢这种扩展方式:

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

然后简单地

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

下面是我在使用 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 从配置文件中加载名为“ MyEntities”的连接字符串。)

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

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

修改实体上下文 T4模板

在解决方案资源管理器中,展开 EDMX 文件,以便看到 T4模板。

双击“ MyModel.Context.tt”文件打开它:

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

此模板行生成“ MyEntities”类的类定义,该类继承 DbContext。

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

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

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

public partial class MyEntities : CustomDbContext

就是这样。

问题

一旦您将上下文类从 DbContext更改为 CustomDbContext,如果您尝试使用“ Controller with read/write action and views using Entity Framework”模板添加一个新的 MVC 控制器类,Visual Studio 将给您一个错误。它会显示“不受支持的上下文类型”.为了解决这个问题,打开生成的“ MyModel”。类,并将其继承的类型临时更改为 DbContext。添加新控制器后,可以将其更改回 CustomDbContext

我发现更改.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;
}
var ctx = new DbContext();
ctx.Database.CommandTimeout = 120;

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

我想这可能会帮助一些人了解我是如何做到这一点的:

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 的回答对 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

你当然不用用“和”。

这与上面@Glaze 使用的方法类似,但是我的方法也是使用自定义 DbContext 类,但是我正在做相反的事情。不需要修改 T4模板(。你名下的文件。Edmx) ,我实际上是从生成的 MyEntities 类继承的,如下所示:

T4模板生成的 MyEntity 类:

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 类创建一个接口,并使用带有依赖注入的接口实例,然后你可以在单元测试中模拟你的数据库,此外还可以更容易地控制命令超时和对象上下文的其他属性,你可以为这些属性创建方法(比如延迟加载等)。