暂时设置DbContext的CommandTimeout

时间:2014-08-01 08:13:54

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

我知道我可以为所有查询设置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'.

2 个答案:

答案 0 :(得分:5)

这是因为您依赖于您不应该了解的实施细节(您的IMyDbContext也实施IObjectContextAdapter这一事实)。在单元测试中,IMyDbContext实例实际上是由模拟框架生成的代理,并且不会实现IObjectContextAdapter

由于CommandTimeout对这个假DbContext没有意义,我建议你只有在演员成功时才尝试施放和设置CommandTimeout

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

这样,CommandTimeout将在实际执行环境中设置,但不在单元测试中设置(这并不重要,因为模拟实际上并不查询数据库)< / p>


编辑:实际上,更好更清洁的选择是修改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;

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

答案 1 :(得分:2)

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

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