Nul*_*uli 3 c# testing entity-framework
对于某些背景 - 我正在编写一些实体框架功能的抽象,以使那些使用数据访问层的人的工作更容易一些.我是通过代理/包装器来做这件事的,我正处于测试EF成功使用的地步Include().(但不是这与这里的具体问题相关(只是想避免人们建议"不测试EF功能")(我将Include方法委托给EF,这是我实际测试的)))
理想情况下,我想定义一个块(可能是via using)并让该块计算该块内发生的查询数.
使用一些伪代码,这是我想要的行为:
var user = new User(id);
using(var queryCounter = new QueryCounter()){
user.Books.SelectMany(b => b.Pages);
Assert.Equal(2, queryCounter.NumberOfDetectedQueries);
// the above assert would fail, due to not using the `Include` keyword.
// as the two queries should be to select books and page ids and
// then to actually select the pages
}
Run Code Online (Sandbox Code Playgroud)
有没有办法实现上述查询计数?
更新:
感谢@Ilya Chumakov提供查询拦截器的见解.我已经能够通过一个额外的类获得上面例子中的语法:
public class QueryCounter : IDisposable
{
public int Count => GlobalCounter.QueryCount;
public QueryCounter()
{
GlobalCounter.QueryCount = 0;
GlobalCounter.Active = true;
}
public void Dispose()
{
GlobalCounter.Active = false;
GlobalCounter.QueryCount = 0; //
}
}
Run Code Online (Sandbox Code Playgroud)
然后只需将一个活动字段添加到GlobalCounter
public static class GlobalCounter
{
public static int QueryCount = 0;
public static bool Active = false;
}
Run Code Online (Sandbox Code Playgroud)
并修改每个拦截器方法,如下所示:
#if DEBUG
if (!GlobalCounter.Active) return;
GlobalCounter.QueryCount++;
// or whatever output class/method works for you
Trace.Write("Current Query Count: " + GlobalCounter.QueryCount + " -- ");
Trace.WriteLine(command.CommandText);
#endif
Run Code Online (Sandbox Code Playgroud)
现在,我的测试看起来像这样:
using (var counter = new QueryCounter())
{
var pages = user.Books.First().Pages;
Assert.Equal(1, counter.Count);
}
Run Code Online (Sandbox Code Playgroud)
它可以通过拦截器来实现:
class EfCommandInterceptor : IDbCommandInterceptor
{
public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
GlobalCounter.QueryCount++;
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
GlobalCounter.QueryCount++;
}
public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
GlobalCounter.QueryCount++;
}
//other methods are empty
}
Run Code Online (Sandbox Code Playgroud)
GlobalCounter class保留全局变量:
static class GlobalCounter
{
public static int QueryCount { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
注册拦截器:
public class EntityConfigiration : DbConfiguration
{
public EntityConfigiration()
{
this.AddInterceptor(new EfCommandInterceptor());
}
}
Run Code Online (Sandbox Code Playgroud)
该EntityConfigiration班将被自动注册.您可以在配置文件中注册拦截器.
然后使用:
[Test]
public void CalculateQueryCount()
{
GlobalCounter.QueryCount = 0;
using (var context = new YourContext())
{
//queries
}
int actual = GlobalCounter.QueryCount;
}
Run Code Online (Sandbox Code Playgroud)
此EfCommandInterceptor代码是单线程的.对于多线程测试lock,Interlocked.Increment应使用锁定(等)代替++.