暂时设置DbContext的CommandTimeout

Kev*_*sen 7 c# asp.net-mvc unit-testing entity-framework 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我希望保持默认的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
    }
}
Run Code Online (Sandbox Code Playgroud)

一切都很完美,直到我用Mock-DbContext运行我的UnitTest(是的,我确实将我的委托设置为这个Mock-DbContext).它给了我一个InvalidCastException:

System.InvalidCastException: Unable to cast object of type
'Castle.Proxies.FakeMyDbContextProxy' to type
'System.Data.Entity.Infrastructure.IObjectContextAdapter'.
Run Code Online (Sandbox Code Playgroud)

Tho*_*que 5

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

由于CommandTimeout这个假的没有意义DbContext,我建议你尝试施放并设置CommandTimeout唯一的如果演员成功:

var objectContextAdapter = usingDb as IObjectContextAdapter;
if (objectContextAdapter != null)
    objectContextAdapter.ObjectContext.CommandTimeout = 120;
Run Code Online (Sandbox Code Playgroud)

这样,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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

usingDb.CommandTimeout = 120;
Run Code Online (Sandbox Code Playgroud)

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