Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET核心DI,将参数传递给构造函数的方法_C#_Asp.net Core_Dependency Injection_.net Core_Ioc Container - Fatal编程技术网

C# .NET核心DI,将参数传递给构造函数的方法

C# .NET核心DI,将参数传递给构造函数的方法,c#,asp.net-core,dependency-injection,.net-core,ioc-container,C#,Asp.net Core,Dependency Injection,.net Core,Ioc Container,具有以下服务构造函数 public class Service : IService { public Service(IOtherService service1, IAnotherOne service2, string arg) { } } 使用.NET核心IOC机制传递参数有哪些选择 _serviceCollection.AddSingleton<IOtherService , OtherService>(); _serviceCollect

具有以下服务构造函数

public class Service : IService
{
     public Service(IOtherService service1, IAnotherOne service2, string arg)
     {

     }
}
使用.NET核心IOC机制传递参数有哪些选择

_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( _serviceCollection.BuildServiceProvider().GetService<IOtherService>(), _serviceCollection.BuildServiceProvider().GetService<IAnotherOne >(), "" ));
\u serviceCollection.AddSingleton();
_serviceCollection.AddSingleton();
_serviceCollection.AddSingleton(x=>新服务(_serviceCollection.BuildServiceProvider().GetService(),_serviceCollection.BuildServiceProvider().GetService(),“”);
还有别的办法吗

工厂委托的表达式参数(在本例中为x)是一个
IServiceProvider

用它来解决依赖关系

_serviceCollection.AddSingleton<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                ""));
\u serviceCollection.AddSingleton(x=>
新服务(x.GetRequiredService(),
x、 GetRequiredService(),
""));

工厂委托是一个延迟调用。当需要解析类型时,它会将已完成的提供程序作为委托参数传递。

如果您对更新服务感到不舒服,可以使用
参数对象
模式

因此,将字符串参数提取到它自己的类型中

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>();
\u serviceCollection.AddSingleton(=>newserviceArgs{Arg1=”“;});
_serviceCollection.AddSingleton();
_serviceCollection.AddSingleton();
_serviceCollection.AddSingleton();
第一个好处是,如果您需要更改服务构造函数并向其中添加新服务,那么您不必更改
新服务(…
调用)。另一个好处是设置更简洁


但是,对于只有一个或两个参数的构造函数来说,这可能太多了。

实现这一点的建议方法是使用-注意,这适用于所有.NET核心应用程序,而不仅仅是ASP.NET核心。但在某些使用情况下,这是不切实际的(例如,当参数仅在运行时已知,而不是在启动/编译时)或者您需要动态地替换依赖项

当您需要替换单个依赖项(可以是字符串、整数或其他类型的依赖项)或使用仅接受字符串/整数参数且需要运行时参数的第三方库时,它非常有用

您可以尝试作为快捷方式,而不是手动解析每个依赖项:

_serviceCollection.AddSingleton<IService>(x => 
    ActivatorUtilities.CreateInstance<Service>(x, "");
);
然后,
IOtherService
的第一个参数将被注入
OtherServiceB
,而不是
OtherServiceA
——但其余参数将来自服务提供商

如果您有许多依赖项,并且只想专门处理单个依赖项(即,使用在请求期间或为特定用户配置的值替换特定于数据库的提供程序,这是您仅在运行时和/或请求期间才知道的,而不是在构建/启动应用程序时知道的)

如果性能是一个问题,您可以使用来创建工厂方法。和

当类型解析非常频繁时(例如在信号器和其他高请求场景中),这非常有用

var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new object[] { 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 string HelloWorld()
    {
        return this.helloWorldService.Hello(firstName, 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);
}

您还可以将依赖项注入此流程

_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( x.GetService<IOtherService>(), x.GetService<IAnotherOne >(), "" ));
\u serviceCollection.AddSingleton();
_serviceCollection.AddSingleton();
_serviceCollection.AddSingleton(x=>新服务(x.GetService(),x.GetService(),“”);

是的,我现在就是这样做的,但还有其他方法吗?可能更优雅?我的意思是,如果有其他注册服务的参数,看起来会有点奇怪。我正在寻找更像正常注册服务的方法,只传递非服务参数,在本例中是arg。类似Autofac的方法es
.WithParameter(“参数”,“参数”)
No您正在手动生成提供程序,这是错误的。委托是一个延迟的调用。当需要解析类型时,它将传递完成的提供程序作为委托参数。@MCR这是默认的方法,核心DI是现成的。@Nkosi:看看,它是Microsoft.Extensions.DependencyInj的一部分section.Abstractionspackage(因此没有特定于容器的依赖项)这也是ASP.NET Core实例化控制器的方式。默认情况下,它们不是直接从IoC解析的(除非使用了
.addControllerAsservices
,它将
ControllerActivatorProvider
替换为
基于服务的ControllerActivator
,值得注意的是,在DI框架之外使用它(在我看来,这个问题中的用法很好)将是@Wouter:不,您不能。所讨论的构造函数有3个构造函数参数。当您
new
它时,您需要传递每个构造函数参数。对于ActivatorUtils,您只传递不应该来自容器的参数。当字符串/int参数在r处确定时,这尤其有用直到时间,而不是开始时间(这是配置DI容器的地方,以后不能更改)。代码用于演示,以显示您可以注入运行时参数(甚至服务实例),而其余的来自DI容器,其要点是避免必须
provider.GetRequiredService()
给定类的每一个依赖项,当您向构造函数添加额外参数时,也会破坏代码。AddTransient(p=>p.ResolveWith(“Tseng”,“Stackoverflow”);似乎毫无意义,因为您可以只使用服务。AddTransient(sp=>new DemoService(p.GetRequiredService(),“Tseng”、“Stackoverflow”))更改您的设计。将arg提取到参数对象中并将其注入。对于复杂参数,使用会更直观,并且是选项模式的推荐方式,但不太适合仅在运行时知道的参数(即从req)
Output: Hello Tseng Stackoverflow
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( x.GetService<IOtherService>(), x.GetService<IAnotherOne >(), "" ));