【问题标题】:ASP.NET Core dependency injection: passing arguments at runtimeASP.NET Core 依赖注入:在运行时传递参数
【发布时间】:2019-07-09 23:45:42
【问题描述】:

我正在使用 ASP.NET 核心依赖注入在我的测试项目中,来设置我的测试使用的文本上下文(所以我不能在这里使用构造函数注入)。在 ConfigureServices 中,我注册了服务,效果很好:

  public void ConfigureServices(IServiceCollection services)
  {
      // Scans assemblies and adds MediatR handlers, preprocessors, and postprocessors implementations to the container.            
      services.AddMediatR(
       typeof(Application.Logic.Queries.FindUserByEmailAddressHandler));            
      services.AddTransient<ILocalDb, LocalDb>(l => new LocalDb(null));
      services.AddTransient<IUnitOfWork, UnitOfWork>(uow => new UnitOfWork(""));            
      services.AddTransient<IUserRepository, UserRepository>();
  }

但是,在尝试获取我的工作单元的实例时,我遇到了问题:

   var localDb = serviceProvider.GetService<ILocalDb>();
   var unitOfWork = serviceProvider.GetService<IUnitOfWork>(); <- need to pass constructor parameter

你看,UnitOfWork 构造函数接受一个连接字符串,我必须传递这个来自 localDb 的连接字符串(LocalDb 即时创建一个测试数据库)。

在 StructureMap 中,我可以在获取实例时将参数传递给构造函数,如下所示:

  x.For<IUnitOfWork>().Use<UnitOfWork>().Ctor<string>().Is(localDb.ConnectionString); });

如何使用 ASP.NET Core 依赖注入来做到这一点?

【问题讨论】:

  • var localDb = serviceProvider.GetService&lt;ILocalDb&gt;(); 开头是错误的,称为服务定位器反模式。你到底为什么要这么做?另一个问题是服务 (UnitOfWork) 正在接收连接字符串,这仅意味着服务本身正在构建另一个依赖项。总的来说,结构是完全错误的
  • 制作一些从 IUnitOfWork 派生的标记接口并制作一些基础存储库,现在您可以拥有一些具有相同功能和独立单元的基础服务...
  • 或许我这里的回答可以帮到你:stackoverflow.com/questions/54490808/…
  • @Camilo Terevinto 我这样做是因为它在我的测试项目中,当我准备文本上下文时。我不能在这里使用构造函数注入。所以我不同意这是“完全错误的”,但也许你可以解释一下如何正确地做到这一点。
  • 我认为最好使用 UnitOfWorkFactory 来创建您的 UnitOfWork

标签: c# asp.net-core dependency-injection asp.net-core-webapi


【解决方案1】:

我找到了解决办法。

我在自己的文本上下文中执行每个测试,例如(简化示例):

  using (var context = IntegrationTestEnvironment.Setup())
  {
      User bobbyUserDo = new User();
      Profile bobbyPatientDo = new Patient(bobbyUserDo, "bobbyPatient@hotmail.com");
      var bobbyUserDb = await context.UserRepository.AddOrUpdateAsync(bobbyUserDo);
      bobbyUserDb.Should().NotBeNull();
      bobbyUserDb.Id.Should().BeGreaterThan(0);
      bobbyUserDb.Profiles.Should().HaveCount(1);
      bobbyUserDb.Profiles.First().Id.Should().BeGreaterThan(0);
  }

在我的设置方法中,我准备环境:

  public static IntegrationTestContext Setup(bool prefillWithTestData)
  {           
        var webHost = WebHost.CreateDefaultBuilder()                
            .UseStartup<Startup>()
            .Build();
        var serviceProvider = new DependencyResolverHelpercs(webHost);
  }

Startup 方法包含 ConfigureServices 方法,我在其中配置测试所需的所有服务:

  public void ConfigureServices(IServiceCollection services)
    {            
        // Scans assemblies and adds MediatR handlers, preprocessors, and postprocessors implementations to the container.            
        services.AddMediatR(typeof(Application.Logic.Queries.FindUserByEmailAddressHandler));

        var localDb = new LocalDb();
        services.AddSingleton<ILocalDb, LocalDb>(uow => localDb);
        services.AddSingleton<IUnitOfWork, UnitOfWork>(uow => new UnitOfWork(localDb.ConnectionString));
        services.AddSingleton<IUserRepository, UserRepository>();
    }

那时,我创建了一个 LocalDb(它创建了一个本地数据库),然后我可以简单地将连接字符串传递给我的工作单元。

测试可以在我正确配置依赖注入的上下文中运行,并且在该测试中有效。这就是我使用 Singleton 的原因:实例在特定测试的上下文中是相同的;测试结束后,一切都被处理掉了,本地数据库也被删除了。

【讨论】:

    【解决方案2】:

    不需要像上面那样传递 UnitOfWork 参数。使用这样的配置定义您的 UnitOfWork 结构

                var mySqlConn = Configuration.GetSection("MySqlConnection").Get<MySqlConnection>();
                services.AddDbContext<MySQLContext>(options => options.UseMySql(mySqlConn.MySqlConnectionString));
    
                services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));
                services.AddTransient(typeof(IUnitOfWork<>), typeof(UnitOfWork<>));
    

    Visual Studio 将在运行时解决它

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-09
      • 2016-11-16
      • 2019-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多