【问题标题】:Temporarily set DbContext's CommandTimeout临时设置 DbContext 的 CommandTimeout
【发布时间】:2014-08-01 08:13:53
【问题描述】:

我知道我可以为所有查询设置 DbContext 的 CommandTimeout,如下所示:

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
        // to 2 min instead of the default 30 sec
        objectContext.CommandTimeout = 120;
    }
}

但是,我想保留默认的 30 秒,除了一个方法需要更长的时间。

我应该如何为这个单一查询更改它?

我确实尝试过使用:

public void doSomething(){
    // The using had another reason, but in this case it also
    // automatically disposes of the DbContext
    using(IMyDbContext = delegateDbContext()){
        ((IObjectContextAdapter)usingDb).ObjectContext.CommandTimeout = 120;

        ... // myQuery
    }
}

一切正常,直到我使用 Mock-DbContext 运行我的 UnitTest(是的,我确实将我的委托设置为这个 Mock-DbContext)。它给了我一个InvalidCastException

System.InvalidCastException: Unable to cast object of type
'Castle.Proxies.FakeMyDbContextProxy' to type
'System.Data.Entity.Infrastructure.IObjectContextAdapter'.

【问题讨论】:

    标签: c# asp.net-mvc entity-framework unit-testing mocking


    【解决方案1】:

    那是因为您依赖于一个您不应该知道的实现细节(事实上您的IMyDbContext 也实现了IObjectContextAdapter)。在您的单元测试中,IMyDbContext 实例实际上是模拟框架生成的代理,并没有实现IObjectContextAdapter

    由于CommandTimeout 对这个假的DbContext 没有意义,我建议你尝试转换并设置CommandTimeout,只有在转换成功的情况下:

    var objectContextAdapter = usingDb as IObjectContextAdapter;
    if (objectContextAdapter != null)
        objectContextAdapter.ObjectContext.CommandTimeout = 120;
    

    这样,CommandTimeout 将被设置在真实的执行环境中,而不是在单元测试中(没关系,因为模拟实际上并不查询数据库)


    编辑:实际上,更好和更清洁的选择是修改IMyDbContext 以公开设置CommandTimeout 的方法:

    interface IMyDbContext
    {
        ...
    
        int CommandTimeout { get; set; }
    }
    
    
    class MyDbContext : IMyDbContext
    {
        ...
    
        public int CommandTimeout
        {
            get { return ((IObjectContextAdapter)this).ObjectContext.CommandTimeout; }
            set { ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = value; }
        }
    }
    

    现在你可以这样做了:

    usingDb.CommandTimeout = 120;
    

    无需担心上下文的实际类型。模拟框架只会为此属性生成一个虚拟实现。

    【讨论】:

    • 关于您的编辑:它给了我以下错误:MyDbContext 属性中ObjextContext.CommandTimeout 上的"An object reference is required for the non-static field, method, or property 'System.Data.Objects.ObjectContext.CommandTimeout.get'"
    • @KevinCruijssen,啊,那一定是因为IObjectContextAdapter是显式实现的,所以ObjectContext不被识别为this的成员。我修好了。
    【解决方案2】:

    并触及为单个语句设置超时的原始问题。直截了当的方法有时是最好的。假设您已经按照上面的建议公开了 CommandTimeout(好主意),那么在您的函数中:

    var originalTimeout = _dbContext.CommandTimeout;
    _dbContext.CommandTimeout = 120;
    // do something relevant
    _dbContext.CommandTimeout = originalTimeout;
    

    【讨论】:

    • 这确实是我现在使用它的方式。我使用@ThomasLevesque's Edit 的代码并临时保存旧值,以便设置和重置CommandTimeout。赞成与我拥有完全相同的实现。 :)
    猜你喜欢
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    • 2012-01-15
    • 2020-08-21
    相关资源
    最近更新 更多