【问题标题】:Count number of queries in EF Core backed Identity system计算 EF Core 支持的身份系统中的查询数
【发布时间】:2020-11-11 06:56:11
【问题描述】:

我正在运行由 EF Core 存储支持的 ASP.NET Core Identity(UserManager<TUser> 和爵士乐)。我最近在我的应用程序中遇到了一个错误,其中控制器端点导致对UserManager<T>.UpdateAsync(...) 的调用过多。

由于我有集成测试(使用WebApplicationFactory<TStartup> 设置和UseInMemoryDatabase(...)),我很想编写一个集成/冒烟测试,如果控制器端点超出其查询预算,它会警告我。

基本上,我很想写(但不能写)这个测试:

public class FooControllerTests : IClassFixture<WebApplicationFactory>
{
    private readonly WebApplicationFactory _factory;

    public FooControllerTests(WebApplicationFactory factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task Bar_WhenCalled_StaysWithinQueryBudget()
    {
        using var scope = _factory.Server.Host.Services.CreateScope();
        var services = scope.ServiceProvider;
        var dbContext = services.GetRequiredService<MyDbContext>();

        var countBefore = dbContext.SomehowGetCurrentQueryCount(); // ??

        var httpClient = _factory.CreateClient();
        var _ = httpClient.PostAsJsonAsync("/foo/bar", new { data = "something" });

        var countAfter = dbContext.SomehowGetCurrentQueryCount(); // ??

        var budgetForEndpoint = 3;
        Assert.True(countAfter - countBefore > budgetForEndpoint);
    }
}

但重要的是精神:我想要一个冒烟测试来检查我将来是否不会犯同样的错误,并且是否有一个导致太多(更新)查询的错误。

我尝试编写a DbCommandInterceptor 并将其添加到我的AddDbContext 调用中,但似乎通过AddEntityFrameworkStores&lt;TDbContext&gt;(...) 添加的Store 与此隔离,因为我的CommandCreating 处理程序从未被调用。

如果查询通过 Identity EF Core Stores 运行,我如何检查我的端点是否在查询预算范围内?

我目前正在使用 ASP.NET Core 3.1 和 EF Core 3.1,但我愿意升级以获得此功能。

【问题讨论】:

    标签: c# asp.net-core entity-framework-core asp.net-core-identity


    【解决方案1】:

    我尝试编写 DbCommandInterceptor 并将其添加到我的 AddDbContext 调用中,但似乎通过 AddEntityFrameworkStores(...) 添加的 Store 与此隔离,因为从未调用过我的 CommandCreating 处理程序。

    这就是这样做的方法。以下摘自Introduction to Identity on ASP.NET Core: Configure Identity services

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    

    只需添加拦截器即可:

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
               .AddInterceptors(new QueryCountingCommandInterceptor())
    );
    

    请注意,您提到使用UseInMemoryDatabase(),您可能只在WebApplicationFactory&lt;TStartup&gt; 类中使用。如果是这种情况,您也需要在此处添加拦截器:

    public class WebApplicationFactory : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(s => s.AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseInMemoryDatabase("integration-tests");
                options.AddInterceptors(new MyInterceptor());
            }));
        }
    }
    

    这很好,因为您可以只为测试添加拦截器,而不是让它为生产代码运行。

    【讨论】:

    • 感谢您的回答!我着手创建一个可重现的场景来回答您的最后一个问题,并且应该首先这样做:然后我自己找到了解决方案。 - 在我的WebApplicationFactory 中,我覆盖了AddDbContext 以便能够UseInMemoryDatabase(...) 但是当然你还需要在那里添加拦截器
    • 您介意我在您的答案中编辑一些额外的细节,以帮助其他登陆这里的人吗?
    • 好的,快点!
    猜你喜欢
    • 2020-01-20
    • 1970-01-01
    • 2011-11-17
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-04
    • 2020-11-05
    相关资源
    最近更新 更多