具有以下服务构造函数
public class Service : IService
{
public Service(IOtherService service1, IAnotherOne service2, string arg)
{
}
}
使用.NET Core IOC机制传递参数有哪些选择
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( _serviceCollection.BuildServiceProvider().GetService<IOtherService>(), _serviceCollection.BuildServiceProvider().GetService<IAnotherOne >(), "" ));
还有其他方法吗?
答案 0 :(得分:29)
工厂委托的表达式参数(在这种情况下为 x )是IServiceProvider
。
使用它来解决依赖关系,
_serviceCollection.AddSingleton<IService>(x =>
new Service(x.GetRequiredService<IOtherService>(),
x.GetRequiredService<IAnotherOne>(),
""));
工厂委托是延迟调用。每当要解析类型时,它将通过完整的提供程序作为委托参数。
答案 1 :(得分:12)
应注意,推荐的方法是使用Options Pattern。但是在某些情况下,它不切实际(仅在运行时才知道参数,而在启动/编译时才知道),或者您需要动态替换依赖项。
当您需要替换单个依赖项(它是字符串,整数或另一种类型的依赖项)或使用仅接受字符串/整数参数并且需要运行时参数的第三方库时,它非常有用。
您可以尝试使用CreateInstance(IServiceProvider, Object[])作为快捷方式(不确定它是否适用于字符串参数/值类型/基元(int,float,字符串),未经测试) (仅试用了它并确认了它的工作,即使使用多个字符串参数也是如此),而不是手动解决每个依赖关系:
_serviceCollection.AddSingleton<IService>(x =>
ActivatorUtilities.CreateInstance<Service>(x, "");
);
参数(CreateInstance<T>
/ CreateInstance
的最后一个参数)定义应替换的参数(不从提供程序解析)。它们会在出现时从左到右应用(即第一个字符串将替换为要实例化类型的第一个字符串类型的参数)。
ActivatorUtilities.CreateInstance<Service>
在许多地方用于解决服务并替换此单一激活的默认注册之一。
例如,如果您有一个名为MyService
的类,并且它具有IOtherService
,ILogger<MyService>
作为依赖项,并且您想解析该服务,但要替换默认服务{{1} }(例如,其IOtherService
与OtherServiceA
一样,您可以执行以下操作:
OtherServiceB
然后,myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider, new OtherServiceB())
的第一个参数将被注入IOtherService
,而不是OtherServiceB
,但是其余参数将来自容器。
当您有很多依赖项并且只想专门对待一个依赖项时(例如,用请求期间或为特定用户配置的值替换特定于数据库的提供程序,这仅在运行时和执行期间才知道),这很有用。一个请求,而不是在构建/启动应用程序时)。
您还可以改用ActivatorUtilities.CreateFactory(Type, Type[]) Method创建工厂方法,因为它提供了更好的性能GitHub Reference和Benchmark。
当类型非常频繁地解析时(例如在SignalR和其他高请求场景中),第一类很有用。基本上,您将通过
创建一个OtherServiceA
ObjectFactory
然后将其缓存(作为变量等)并在需要的地方调用
var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new[] { typeof(IOtherService) });
我自己尝试过一次,以确认它也可以用于字符串和整数,并且确实可以工作。这是我测试过的具体示例:
MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);
打印
class Program { static void Main(string[] args) { var services = new ServiceCollection(); services.AddTransient<HelloWorldService>(); services.AddTransient(p => p.ResolveWith<DemoService>("Tseng", "Stackoverflow")); var provider = services.BuildServiceProvider(); var demoService = provider.GetRequiredService<DemoService>(); Console.WriteLine($"Output: {demoService.HelloWorld()}"); Console.ReadKey(); } } public class DemoService { private readonly HelloWorldService helloWorldService; private readonly string firstname; private readonly string lastname; public DemoService(HelloWorldService helloWorldService, string firstname, string lastname) { this.helloWorldService = helloWorldService ?? throw new ArgumentNullException(nameof(helloWorldService)); this.firstname = firstname ?? throw new ArgumentNullException(nameof(firstname)); this.lastname = lastname ?? throw new ArgumentNullException(nameof(lastname)); } public class HelloWorldService { public string Hello(string name) => $"Hello {name}"; public string Hello(string firstname, string lastname) => $"Hello {firstname} {lastname}"; } // Just a helper method to shorten code registration code static class ServiceProviderExtensions { public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class => ActivatorUtilities.CreateInstance<T>(provider, parameters); }
答案 2 :(得分:6)
如果您不喜欢新服务,则可以使用Parameter Object
模式。
因此将字符串参数提取为自己的类型
public class ServiceArgs
{
public string Arg1 {get; set;}
}
构造函数现在看起来像
public Service(IOtherService service1,
IAnotherOne service2,
ServiceArgs args)
{
}
和设置
_serviceCollection.AddSingleton<ServiceArgs>(_ => new ServiceArgs { Arg1 = ""; });
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService, Service>();
第一个好处是,如果您需要更改Service构造函数并为其添加新服务,则不必更改new Service(...
调用。另一个好处是设置有点清洁。
对于只有一个或两个参数的构造函数,这可能太多了。
答案 3 :(得分:0)
您也可以在此过程中注入依赖项
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( x.GetService<IOtherService>(), x.GetService<IAnotherOne >(), "" ));
答案 4 :(得分:-2)
即使您有多个参数,也可以使用以下方法
services.AddSingleton(_ => new YourService("yourvalue1","yourvalue2") as IYourService);
但是我觉得它的实现方式不是很有效