我有一个具有以下构造函数的类:

public MyClass(SomeRequiredService srs, string someRandomInput)
{

}

我如何用DI注册MyClass,但每次需要获取MyClass的实例时都可以传递different someRandomInput

With below construct或 how can I get access to registered services 或 IServiceProvider in MyClass ct或 while having a dynamic parameter in construct或?

public MyClass(string someRandomInput)
{
    // how access a registered service here using DI?
}

推荐答案

我如何注册MyClass与DI,但有一个选项,以传递someRandomInput每次我需要得到一个实例?

假设只需要指定一次someRandomInput,那么您可以在服务注册的implementationFactory:中这样做:

IConfiguration cfg = ...

//

services
    .AddSingleton<MyClass>( implementationFactory: ( IServiceProvider sp ) =>
        new MyClass(
            srs            : sp.GetRequiredService<SomeRequiredService>(),
            someRandomInput: cfg.GetString("Foobars")
        )
    );

同样适用于AddTransientAddScoped-问题是它们都将使用您在工厂中传递到someRandomInput的任何值-尽管您的工厂方法本身也可以使用IServiceProvider来获取another服务作为这些值的来源.


更普遍的是,这里有两种方法:

选项1:定义表示服务的数据依赖关系的接口

public interface IFoobarsServiceRequisite
{
    String GetNextStringValue();
}

public class FoobarsService
{
    public FoobarsService( SomeRequiredService srs, IFoobarsServiceRequisite requisiteData )
    {
    }
}

...然后您将注册那个Requisite服务接口的一些实现.

Option 2: Not-quite-as-bad-as AbstractSingletonProxyFactoryBean

(In case anyone missed the reference)


services
    .AddSingleton<SomeRequiredService>()
    .AddSingleton<ISomeRandomStringSource,DefaultSomeRandomStringSource>()
    .AddSingleton<IFoobarsServiceFactory,DefaultFoobarsServiceFactory>()
    .AddTransient<FoobarsService>( sp => sp.GetRequiredService<IFoobarsServiceFactory>().CreateFoobar() );


//

public interface IFoobarsServiceFactory
{
    FoobarsService CreateFoobar();
}

public class DefaultFoobarsServiceFactory : IFoobarsServiceFactory
{
    private readonly SomeRequiredService     srs;
    private readonly ISomeRandomStringSource src;

    public DefaultFoobarsServiceFactory(
        SomeRequiredService srs,
        ISomeRandomStringSource src
    )
    {
        this.srs = srs;
        this.src = src;
    }

    public FoobarsService CreateFoobar()
    {
        return new FoobarsService( this.srs, this.src.GetNextValue() );
    }
}

public class FoobarsService
{
    public FoobarsService( SomeRequiredService srs, String someRandomValue )
    {
    }
}

所有的工厂都是这样.


我感到惊讶的是,MEDI仍然缺乏一个标准化的IServiceFactory接口来处理尴尬的边缘情况,这些情况需要比简单的sp => lambda更多的工厂逻辑,但在应用程序代码中没有need显式调用它们的.CreateService()方法-特别是EF已经有了自己的IDbContextFactory类型.

更新:选项3:工厂服务与呼叫者提供的参数

在澄清了 comments 中的事情后,我认为Op的目标是这样的:

  • FoobarServiceFactory(或其interface)是一个普通的Singleton服务,它有FoobarService个依赖项,但也允许/要求FoobarServiceFactory的使用者提供他们自己的参数,说明他们何时想要获得FoobarService的实例,如下所示:
services.AddSingleton<IFoobarServiceFactory,FoobarServiceFactory>()

//

public interface IFoobarServiceFactory // Note that this interface is entirely optional: you could just register `FoobarServiceFactory` as a service.
{
    FoobarsService CreateFoobar( String callerProvidedData );
}

public class FoobarServiceFactory: IFoobarServiceFactory
{
    private readonly SomeRequiredService srs;

    public DefaultFoobarsServiceFactory( SomeRequiredService srs )
    {
        this.srs = srs;
    }

    public FoobarsService CreateFoobar( String callerProvidedData )
    {
        return new FoobarService( this.srs, callerProvidedData );
    }
}

public class FoobarService
{
    public FoobarService( SomeRequiredService srs, String someRandomValue )
    {
        // etc
    }
}

用法如下:

public class SomeConsumer
{
    private readonly IFoobarServiceFactory fbFactory;

    public SomeConsumer( IFoobarServiceFactory fbFactory )
    {
        this.fbFactory = fbFactory;
    }

    public void DoSomething()
    {
        String someRandomString = Math.Random() > 0.5 ? "abc" : "0xdeadbeef";
        
        FoobarService svc = this.fbFactory.Create( someRandomString );
        // do stuff with `svc`
    }
}

IFoobarServiceFactory的替代实现可以使用IServiceProvider,如果不可能为SomeRequiredService保留长生命周期 (或未绑定的生命周期 )(并且IMO,这是需要IServiceProvider作为实际依赖的唯一可接受的原因(除了依赖关系图中的中断循环,ofc):

public class AltFoobarServiceFactory : IFoobarServiceFactory
{
    private readonly IServiceProvider sp;

    public AltFoobarServiceFactory( IServiceProvider sp )
    {
        this.sp = sp;
    }

    public FoobarsService CreateFoobar( String callerProvidedData )
    {
        // `ActivatorUtilities` is useful! https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.activatorutilities?view=dotnet-plat-ext-8.0
        return ActivatorUtilities.CreateInstance<FoobarsService>( sp, callerProvidedData );
    }
}

Csharp相关问答推荐

EF Core Fluent API中定义的多对多关系

ListaryImportProperty的默认DllImportSearchPathsProperty行为

总是丢弃返回的任务和使方法puc无效之间有区别吗?

将委托传递到serviceccollection c#web API

一种安全的方式来存储SSH凭证(MAUI/C#应用程序)

Take()方法如何与IAsyncEnumerable一起使用

Rx.Net窗口内部可观测数据提前完成

在具有不同属性名称的两个类之间创建关系

如何在VS代码中为C#DotNet配置.json选项以调试内部终端的控制台应用程序

如果是,我怎么才能让这个加75,如果不是,我怎么才能减go 100?

C#多键字典和TryGetValue

Postgres ENUM类型在第一次运行时对Dapper不可见

单元测试类型为HttpClient with Microsoft.Extensions.Http.Resilience

未在Windows上运行的Maui项目

C# Winforms:从对象树到TreeView的递归转换重复条目

用于请求用户返回列表的C#Google API

将J数组转换为列表,只保留一个嵌套的JToken

Visual Studio,Docker容器-容器调用:连接被拒绝

多个参数的最小API删除

通过mini kube中的远程调试Pod与从emoteProcessPickerScript中解析错误输出的代码错误进行比较