注入 dbContext 时返回“找不到与给定参数匹配的构造函数”

Posted

技术标签:

【中文标题】注入 dbContext 时返回“找不到与给定参数匹配的构造函数”【英文标题】:"Could not find a constructor that would match given arguments" returned when injecting a dbContext 【发布时间】:2021-08-03 20:03:23 【问题描述】:

上下文

.NET 5 控制台应用程序使用 IHostedService 模式和 EntityFramework Core 5。

问题

dbContext 如下所示:

    public class WeatherDbContext : DbContext, IWeatherDbContext
    
        public WeatherDbContext(DbContextOptions<WeatherDbContext> options) : base(options)
        
        

       public virtual DbSet<Observation> Observations  get; set; 

主机构建器是这样配置的:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging(logging =>
        
            logging.ClearProviders();
        )
        .UseSerilog((hostContext, loggerConfiguration) =>
        
            logConfiguration.WriteTo.File(ConfigurationManager.AppSettings["LogFile"]);
        )
        .ConfigureServices((services) =>
        
            services.AddHttpClient()
                    .AddSingleton<CommandLineArguments>(new CommandLineArguments(args))
                    .AddSingleton<StringWriter>()
                    .AddDbContext<IWeatherDbContext, WeatherDbContext>(options =>
                    
                        options.UseSqlServer(ConfigurationManager.ConnectionStrings["WeatherManagerDatabase"].ConnectionString);
                    )   
                    .AddTransient<IWeatherUndergroundAPIService(x => new WeatherUndergroundAPIService(ConfigurationManager.AppSettings["StationId"],
                                                                                                      ConfigurationManager.AppSettings["WUApiKey"],
                                                                                                      x.GetRequiredService<IHttpClientFactory>()))
                   .AddHostedService<DataDownloader>();                                                                                        
        );

...主机服务是这样构造的:

private readonly int importDayLimit;
private readonly ILogger logger;
private readonly StringWriter outputWriter;
private readonly int throttleLimit = 100;
private readonly IWeatherDbContext weatherDbContext;
private readonly IWeatherUndergroundAPIService wuApiService;
private DateTime FetchUpToDate;
private DateTime MostRecentlyRecordedObservationDate;

public DataDownloader(IWeatherUndergroundAPIService wuApiService,
                      ILogger logger,
                      IWeatherDbContext weatherDbContext,
                      StringWriter outputWriter,
                      CommandLineArguments commandLineArguments)

    this.wuApiService = wuApiService;
    this.weatherDbContext = weatherDbContext;
    this.logger = logger;
    this.outputWriter = outputWriter;
    this.importDayLimit = this.ProcessCommandLineArguments(commandLineArguments.Args);

然后我有一个这样的 XUnit 测试:

public class CommandLineArgumentValidation

    [Fact]
    public async Task CommandLineArgumentNotAnIntegerAsync()
    
        // Arrange

        Mock<IWeatherUndergroundAPIService> mockWeatherUndergroundAPIService = new();

        DbContextOptions<WeatherDbContext> dbContextOptions = new DbContextOptionsBuilder<WeatherDbContext>()
            .UseInMemoryDatabase(databaseName: "testDb")
            .EnableDetailedErrors()
            .Options;

        IWeatherDbContext weatherDbContext = new WeatherDbContext(dbContextOptions);

        Mock<ILogger> mockLogger = new();
        
        StringBuilder consoleOutput = new();
        StringWriter consoleWriter = new(consoleOutput);
        
        CommandLineArguments commandLineArguments = new(new string[]  "not a positive integer" );

        DataDownloader dataDownloader = new(mockWeatherUndergroundAPIService.Object,
                                           mockLogger.Object,
                                           weatherDbContext,
                                           consoleWriter,
                                           commandLineArguments);
        
        CancellationToken cancellationToken = new(false);

        // Action

        await dataDownloader.StartAsync(cancellationToken);

        // Assertion

        Assert.Equal("Command line argument 'not a positive integer' is not a positive integer. Aborting download.", consoleOutput.ToString());
    

测试抛出异常

Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : 可以 不实例化类的代理: WeatherManagerDataDownloader.WeatherDbContext。 找不到与给定参数匹配的构造函数:

请注意,为了清楚起见,我已经简化了代码。我 am 模拟注入到 DataDownloader 的其他服务,但我没有模拟 dBContext。 编辑:我现在添加了完整的代码,因为有人指出,即使我的简化代码没有暗示,模拟似乎正在发生。

问题

为什么会出现这个测试异常?在我看来,模拟应该与传入的 dBContext 无关。

【问题讨论】:

尝试强制转换:WeatherDbContext((WeatherDbContext)dbContextOptions) @jdweng 到底在哪里?在测试中还是在 DataDownLoader 中? 我会在调用之前添加断点并检查类型。您可能会收到一个给出问题的空响应。 错误提示您正在模拟 WeatherDbContext,但您的示例代码没有显示。 @Neil 确实如此,但我不明白为什么。我已经更新了上面的代码以显示完整的测试设置。如您所见,正在发生模拟,但 dbContext 没有。 【参考方案1】:

认为这可能是答案,当然它让我继续前进。

当使用调试器运行测试时,我意识到一切都已成功注入,并且测试代码正在成功进行。但是我正在测试无效的命令行参数情况,并且在 DataDownloader 中我已经编码

Environment.Exit(1)

在输出适当的消息后停止应用程序。显然,这不是您应该在 IHostedService 中执行的操作,删除它会使模拟错误消失。然而,为什么它会导致未模拟的东西出现模拟错误仍然令人费解。

【讨论】:

以上是关于注入 dbContext 时返回“找不到与给定参数匹配的构造函数”的主要内容,如果未能解决你的问题,请参考以下文章

如何将 DBContext 注入 HTTPModule

如何使用autofac实现多个DbContext的注入

注入 DbContext 时无法访问 ASP.NET Core 中已释放的对象

将 DbContext 与依赖注入一起使用

C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET Core 中的已处置对象

如何刷新实体框架核心 DBContext?