.NET Core DI, способы передачи параметров в конструктор - PullRequest
0 голосов
/ 21 декабря 2018

Наличие следующего сервисного конструктора

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 >(), "" ));

Есть ли другой способ?

Ответы [ 4 ]

0 голосов
/ 14 августа 2019

Следующий способ будет работать, даже если у вас есть несколько параметров

services.AddSingleton(_ => new YourService("yourvalue1","yourvalue2") as IYourService);

Но я чувствую, что это неэффективный способ реализации

0 голосов
/ 21 декабря 2018

Если вам неудобно с новым сервисом, вы можете использовать шаблон 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(... звонки.Еще одним преимуществом является то, что настройка немного чище.

Для конструктора с одним или двумя параметрами это может быть слишком много.

0 голосов
/ 21 декабря 2018

Следует отметить, что рекомендуемый способ - использовать шаблон параметров .Но есть случаи использования, когда это непрактично (когда параметры известны только во время выполнения, а не во время запуска / компиляции) или вам нужно динамически заменить зависимость.

Это очень полезно, когда вам нужно заменить одну зависимость(будь то строка, целое число или другой тип зависимости) или при использовании сторонней библиотеки, которая принимает только строковые / целочисленные параметры, и вам требуется параметр времени выполнения.

Вы можете попробовать CreateInstance (IServiceProvider, Object[]) в качестве ярлыка (не уверен, что он работает со строковыми параметрами / типами значений / примитивами (int, float, string), без проверки) (только что попробовал и подтвердил егоработает, даже с несколькими строковыми параметрами) вместо того, чтобы разрешать каждую отдельную зависимость вручную:

_serviceCollection.AddSingleton<IService>(x => 
    ActivatorUtilities.CreateInstance<Service>(x, "");
);

Параметры (последний параметр CreateInstance<T> / CreateInstance) определяют параметры, которые должны быть заменены(не решено от провайдера).Они применяются слева направо по мере их появления (т. Е. Первая строка будет заменена первым строковым параметром того типа, который должен быть создан).

ActivatorUtilities.CreateInstance<Service> используется во многих местах для разрешения службыи замените одну из регистраций по умолчанию для этой отдельной активации.

Например, если у вас есть класс с именем MyService, и он имеет IOtherService, ILogger<MyService> в качестве зависимостей, и вы хотите разрешить службу, но замените службу по умолчанию IOtherService (скажем, OtherServiceA) с помощью OtherServiceB вы можете сделать что-то вроде:

myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider, new OtherServiceB())

Тогда первый параметр IOtherService будет вводить OtherServiceB вместо OtherServiceA, но остальные параметры будут получены изконтейнер.

Это полезно, когда у вас много зависимостей и вы хотите просто обработать одну (например, заменить поставщика конкретной базы данных значением, настроенным во время запроса, или для конкретного пользователя, то, что вы знаете только при запуске).время и во время запроса, а не когда приложение создается / запускается).

Вы также можете использовать ActivatorUtilities.CreateFactory (Type, Type []) Method для создания фабричного метода, поскольку он предлагает лучшую производительность GitHub Reference и Тест .

Позже это полезно, когда тип разрешается очень часто (например, в SignalR и других сценариях с высокими запросами).Обычно вы создаете 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);
}

Отпечатки

Output: Hello Tseng Stackoverflow
0 голосов
/ 21 декабря 2018

Параметр выражения ( x в данном случае) заводского делегата равен IServiceProvider.

Используйте это для разрешения зависимостей,

_serviceCollection.AddSingleton<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                ""));

Фабричный делегат является отложенным вызовом.Когда тип должен быть разрешен, он передает завершенного поставщика в качестве параметра делегата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...