C# 面临模拟IServiceProvider扩展方法的问题
我知道这个问题可能会重复,但我面临的问题是,我无法模拟非静态方法,因为调用非静态方法的是静态方法 我的控制器逻辑调用静态方法C# 面临模拟IServiceProvider扩展方法的问题,c#,unit-testing,dependency-injection,moq,C#,Unit Testing,Dependency Injection,Moq,我知道这个问题可能会重复,但我面临的问题是,我无法模拟非静态方法,因为调用非静态方法的是静态方法 我的控制器逻辑调用静态方法ServiceProviderServiceExtensions方法GetServices(此IServiceProvider提供程序),该方法似乎反过来调用非静态方法provider.GetService(serviceType) 基本上,我有依赖注入,其中一个接口有两个实现 services.AddSingleton<IProvider, CustomProvid
ServiceProviderServiceExtensions
方法GetServices(此IServiceProvider提供程序)
,该方法似乎反过来调用非静态方法provider.GetService(serviceType)
基本上,我有依赖注入,其中一个接口有两个实现
services.AddSingleton<IProvider, CustomProvider1>();
services.AddSingleton<IProvider, CustomProvider2>();
在控制器中,我将依赖项解析为
Controller1.cs
provider = serviceProvider.GetServices<IProvider>()
.FirstOrDefault(lp => lp.GetType() == typeof(CustomeProvider1));
and
Controller2.cs
provider = serviceProvider.GetServices<IProvider>()
.FirstOrDefault(lp => lp.GetType() == typeof(CustomeProvider1));
我收到一个错误,没有注册类型System.Collections.Generic.IEnumerable
[IProvider]的服务。
我不能直接模仿
GetServicesmethod`因为它是静态的
有什么线索可以让我的测试被嘲笑吗?
谢谢
我的控制器逻辑调用静态ServiceProviderServiceExtensions方法GetServices(此IServiceProvider提供程序)
这就是事情开始出错的地方。从控制器内调用GetServices
是的应用程序。你所有的麻烦都源于这种误用
相反,你应该:
IServiceProvider
注入到您的外部任何类的构造函数中public Controller1(IProvider provider)
services.AddSingleton<CustomProvider1>();
services.AddTransient<Controller1>(c => new Controller1(
c.GetRequiredService<CustomProvider1>()));
services.AddSingleton<CustomProvider2>();
services.AddTransient<Controller2>(c => new Controller2(
c.GetRequiredService<CustomProvider2>()));
您的IProvider
是不明确的,但是让我们暂时假设这是可以的。然而,让应用程序代码处理这种模糊性是不好的。相反,您应该在组合根中单独处理这种模糊性,具体操作如下:
public Controller1(IProvider provider)
services.AddSingleton<CustomProvider1>();
services.AddTransient<Controller1>(c => new Controller1(
c.GetRequiredService<CustomProvider1>()));
services.AddSingleton<CustomProvider2>();
services.AddTransient<Controller2>(c => new Controller2(
c.GetRequiredService<CustomProvider2>()));
上述注册仅在这些控制器只有一个依赖项的情况下有效,因为此方法有效地禁用了自动布线。如果类具有更多依赖项,则更易于维护的构造如下所示:
services.AddTransient<Controller1>(c =>
ActivatorUtilities.CreateInstance<Controller1>(
c,
c.GetRequiredService<CustomProvider1>()));
services.AddTransient<Controller2>(c =>
ActivatorUtilities.CreateInstance<Controller2>(
c,
c.GetRequiredService<CustomProvider1>()));
这两个接口是否具有相同的签名无关紧要,因为LSP冲突表明这两个接口的行为实际上非常不同,因为交换实现会破坏它们的客户机
但是,请注意,即使直接使用者继续工作,也可能意味着在交换实现时应用程序开始表现不正确。这并不意味着你违反了SRP。例如,当provider1登录到磁盘,provider2登录到数据库时,预期的行为是对controller1的调用将导致追加磁盘上的日志。因此,以另一种方式使用它并不是您想要实现的,而是您希望在组合根目录中配置的内容。不表示LSP违规;在这种情况下,合同仍然按照消费者的期望行事
如果交换实现对他们的消费者没有显著影响,那么您就没有违反LSP,这意味着给定的注册是可行的。或者,您可以通过向我们提供一个“真正的”DI容器来简化事情;-)
我的控制器逻辑调用静态ServiceProviderServiceExtensions方法GetServices(此IServiceProvider提供程序)
这就是事情开始出错的地方。从控制器内调用GetServices
是的应用程序。你所有的麻烦都源于这种误用
相反,你应该:
IServiceProvider
注入到您的外部任何类的构造函数中public Controller1(IProvider provider)
services.AddSingleton<CustomProvider1>();
services.AddTransient<Controller1>(c => new Controller1(
c.GetRequiredService<CustomProvider1>()));
services.AddSingleton<CustomProvider2>();
services.AddTransient<Controller2>(c => new Controller2(
c.GetRequiredService<CustomProvider2>()));
您的IProvider
是不明确的,但是让我们暂时假设这是可以的。然而,让应用程序代码处理这种模糊性是不好的。相反,您应该在组合根中单独处理这种模糊性,具体操作如下:
public Controller1(IProvider provider)
services.AddSingleton<CustomProvider1>();
services.AddTransient<Controller1>(c => new Controller1(
c.GetRequiredService<CustomProvider1>()));
services.AddSingleton<CustomProvider2>();
services.AddTransient<Controller2>(c => new Controller2(
c.GetRequiredService<CustomProvider2>()));
上述注册仅在这些控制器只有一个依赖项的情况下有效,因为此方法有效地禁用了自动布线。如果类具有更多依赖项,则更易于维护的构造如下所示:
services.AddTransient<Controller1>(c =>
ActivatorUtilities.CreateInstance<Controller1>(
c,
c.GetRequiredService<CustomProvider1>()));
services.AddTransient<Controller2>(c =>
ActivatorUtilities.CreateInstance<Controller2>(
c,
c.GetRequiredService<CustomProvider1>()));
这两个接口是否具有相同的签名无关紧要,因为LSP冲突表明这两个接口的行为实际上非常不同,因为交换实现会破坏它们的客户机
但是,请注意,即使直接使用者继续工作,也可能意味着在交换实现时应用程序开始表现不正确。这并不意味着你违反了SRP。例如,当provider1登录到磁盘,provider2登录到数据库时,预期的行为是对controller1的调用将导致追加磁盘上的日志。因此,以另一种方式使用它并不是您想要实现的,而是您希望在组合根目录中配置的内容。不表示LSP违规;在这种情况下,合同仍然按照消费者的期望行事
如果交换实现对他们的消费者没有显著影响,那么您就没有违反LSP,这意味着给定的注册是可行的。或者,您可以通过向我们提供一个“真正的”DI容器来简化事情;-) 感谢您的详细回答@Steven。当我尝试实现services.AddTransient(c=>ActivatorUtilities.CreateInstance(c,c.GetRequiredService())时;我得到一个错误,因为我的控制器现在有两个参数化构造函数。错误是:在类型中找到多个接受所有给定参数类型的构造函数。我们能在一个构造函数本身中实现这一点吗?它似乎接受了所有必需的参数。你的组件应该只有一个构造函数。好吧,我试着把两个构造函数合并成一个大构造函数。现在我的控制器开了