C# 如何在测试期间处理DatabaseFacade(单元或集成)

C# 如何在测试期间处理DatabaseFacade(单元或集成),c#,asp.net-core,entity-framework-core,C#,Asp.net Core,Entity Framework Core,寻找一些建议 public class SomeController : Controller { private readonly SomeContextClass _context; public SomeController(SomeContextClass context) { _context = context; } } //Context Class public SomeContextClass : DbCont

寻找一些建议

public class SomeController : Controller
{
    private readonly SomeContextClass _context;
     
    public SomeController(SomeContextClass context)
    {
        _context = context;
    }   
}

//Context Class
public SomeContextClass : DbContext
{
    public SomeContextClass(DbContextOptions<SomeContextClass> op) : base(op){

        Database.SetCommandTimeout(9000);
    }
}

//Test
[Fact]
public void SomeController_Test()
{
    //Trying In-Memory Implementation
    var options = new DbContextOptions<SomeContextClass>().UseInMemoryDatabase(databaseName: "SomeDB").Options;

    using (var context = new SomeContextClass(options))
    {
        var sc = new SomeController(context);
    }
}
public类SomeController:Controller
{
私有只读SomeContextClass_上下文;
公共SomeController(SomeContextClass上下文)
{
_上下文=上下文;
}   
}
//上下文类
公共SomeContextClass:DbContext
{
公共SomeContextClass(DbContextOptions op):基本(op){
SetCommandTimeout(9000);
}
}
//试验
[事实]
公共控制器测试()
{
//尝试在内存中实现
var options=new DbContextOptions().UseInMemoryDatabase(databaseName:“SomeDB”).options;
使用(var context=newsomecontextclass(选项))
{
var sc=新的SomeController(上下文);
}
}
由于setcommandtimeoutcall,在尝试初始化somecontext类时,此错误将被忽略

有没有一种方法可以用MOQ来模拟这个呼叫?
如何在内存测试中处理它?

今晚我看了一下,因为我预料它会抛出一个无效操作异常

特定于关系的方法只能在上下文使用关系数据库提供程序时使用

当您尝试使用关系方法(如FromSql、ExecuteSqlCommand等)时,这些方法在内存中提供程序中很常见。大多数情况下,它们可以使用模拟包装器进行模拟(我正是为我的库这样做的),但这通常需要大量工作

SetCommandTimeout是一个扩展。您需要深入研究扩展,并在数据库外观上模拟一些东西,以使其正常工作

然而,在我们走这条路之前,在这种情况下,嘲弄对你来说是行不通的。当您模拟一个实现时,模拟库需要能够创建一个要代理的实例,这意味着它在构造函数中执行代码。SetCommandTimeout将在模拟创建和再次barf期间传入的DbContextOptions生成的任何内容上执行

关于您的选择:

  • 不要在构造函数中调用set命令超时;将它移动到另一个方法,记住调用它等等

  • 使用支持关系操作的不同实体框架核心提供程序

  • 不能说我喜欢第一个选项,但它可以让你嘲笑它。第二种要美味得多。我在SQLite方面做得不多,但在LINQPad中快速发挥会产生预期的结果:

    void Main()
    {
        using (var connection = new SqliteConnection("Filename=:memory:"))
        {
            connection.Open();
            var options = new DbContextOptionsBuilder<TestDbContext>().UseSqlite(connection).Options;
            var testDbContext = new TestDbContext(options);
            Console.WriteLine(testDbContext.Database.GetCommandTimeout());
        }
    }
    
    public class TestDbContext : DbContext
    {
        public TestDbContext(DbContextOptions<TestDbContext> op) : base(op)
        {
            Database.SetCommandTimeout(9000);
        }
    }
    
    void Main()
    {
    使用(var connection=newsqliteconnection(“文件名=:内存:”)
    {
    connection.Open();
    var options=new DbContextOptionsBuilder().UseSqlite(connection).options;
    var testDbContext=newtestdbcontext(选项);
    WriteLine(testDbContext.Database.GetCommandTimeout());
    }
    }
    公共类TestDbContext:DbContext
    {
    公共TestDbContext(DbContextOptions op):基本(op)
    {
    SetCommandTimeout(9000);
    }
    }
    
    结果:


    您可以在DbContext配置中对超时进行硬编码,而不是硬编码。这样,上下文类保持不变

    从链接文档的示例中,可以使用以下命令设置特定于提供程序的命令超时:

    optionsBuilder
        .UseSqlServer(connectionString, 
            providerOptions=>providerOptions.CommandTimeout(90));
    
    这可以在生产或集成测试中用于设置真实数据库的命令超时,而无需修改DbContext:

    var options = new DbContextOptions<SomeContextClass>()
            .UseSqlServer(connectionString, 
                providerOptions=>providerOptions.CommandTimeout(90))
            .Options;
    
    var options=new DbContextOptions()
    .UseSqlServer(连接字符串,
    providerOptions=>providerOptions.CommandTimeout(90))
    .选择;
    
    超时值可以来自配置,允许在不修改代码的情况下进行更改

    但是,使用内存中提供程序的单元测试不需要该设置,因此可以省略它

    var options = new DbContextOptions<SomeContextClass>()
                     .UseInMemoryDatabase(databaseName: "SomeDB")
                     .Options;
    
    var options=new DbContextOptions()
    .UseInMemoryDatabase(数据库名称:“SomeDB”)
    .选择;
    
    这不是一个真正的解决方案,但我想在这里留下一个指针作为解决方法,因为它节省了我的时间。我只需要DatabaseFacade中的一件东西,我的代码就可以进行单元测试,最后我用我可以模拟的数据库上下文的虚拟方法来包装它:

       public virtual bool CanConnectToDatabase()
       {
           return Database.CanConnect();
       }
    

    内存中提供程序有限制。。。您得到的错误是什么?这不是DatabaseFacade。DbContext作为一个工作单元,作为存储库的DbSet为什么不将超时作为DbContextOptions的一部分传递,而不是在DbContext中硬编码?OP使用的是内存上下文,它不支持通过选项设置命令超时。这就是问题所在-因为DbContext尝试设置提供程序是否支持该设置。但是,如果该设置是由调用方设置的,那么DbContext就不必猜测了。