我正在寻找一种为 DbContext 设置 CommandTimeout 的方法。搜索后,我通过将 DbContext 转换为 ObjectContext 并为 objectContext 的 CommandTimeout 属性设置值找到了方法。
var objectContext = (this.DbContext as IObjectContextAdapter).ObjectContext;
但我必须使用 DbContext。
我正在寻找一种为 DbContext 设置 CommandTimeout 的方法。搜索后,我通过将 DbContext 转换为 ObjectContext 并为 objectContext 的 CommandTimeout 属性设置值找到了方法。
var objectContext = (this.DbContext as IObjectContextAdapter).ObjectContext;
但我必须使用 DbContext。
它将与您的方法一起使用。
或将其子类化(来自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.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;
}
以下是我在使用 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
在一个字段中,以便我可以创建一个属性来访问它以覆盖默认值。
在解决方案资源管理器中,展开 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 的答案对于 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”。