DI, разрешить реализацию сервиса с завода

Возможно, он преобразуется в модифицированный Base64, где символы + и / изменены на - и _. См. http://en.wikipedia.org/wiki/Base64#Implementations_and_history

. Если это так, вам нужно изменить его:

string converted = base64String.Replace('-', '+');
converted = converted.Replace('_', '/');

2
задан Zabavsky 17 January 2019 в 10:02
поделиться

4 ответа

Как отмечено в комментариях, ваш код завершается ошибкой, потому что для контейнера внедрения зависимостей ваша фабрика является допустимым источником для экземпляра IFoo. Таким образом, при запросе разрешения всех служб IFoo фабрика будет также запрашивать его.

Если вы хотите, чтобы это работало, вам придется каким-то образом разделить типы. Например, вы можете создать интерфейс маркера IActualFoo, который вы будете использовать вместо регистрации своих сервисов:

services.AddTransient<IActualFoo, Foo1>()
services.AddTransient<IActualFoo, Foo2>();

Внутри вашей IFoo фабрики вы можете затем разрешить IActualFoo и привести их к IFoo. Конечно, это означает, что IActualFoo будет наследоваться от IFoo и что ваши Foo службы действительно должны реализовать IActualFoo.


Другой подход - перенести ответственность за выбор правильной реализации IFoo на фабрику. Как отмечали другие, у вашего подхода есть недостаток, заключающийся в том, что он будет создавать экземпляры для каждой отдельной реализации IFoo и только затем выбирать правильный экземпляр. Это очень расточительно.

Было бы лучше, если бы фабрика могла принять решение о выборе правильной IFoo реализации. Обычно это выглядит так:

services.AddTransient<Foo1>();
services.AddTransient<Foo2>();
services.AddTransient<IFoo>(sp =>
{
    if (someMagicCondition)
        return sp.GetService<Foo1>();
    else
        return sp.GetService<Foo2>();
});

Таким образом, фабрика создаст правильный экземпляр в зависимости от некоторой логики условий. Хотя этот подход часто используется, он требует полной ответственности на заводе и ограниченного набора возможных IFoo реализаций во время разработки. Так что это не сработает, если вы хотите динамически добавлять реализации позже.

То, что вы также можете сделать, это зарегистрировать набор IFoo фабрик . Поэтому вместо того, чтобы реализация IFoo решала, является ли она правильной реализацией, перенесите эту логику на фабрику для каждой IFoo реализации:

services.AddSingleton<IFooFactory, Foo1Factory>();
services.AddSingleton<IFooFactory, Foo2Factory>();
services.AddSingleton<IFoo>(sp =>
{
    var factories = sp.GetServices<IFooFactory>();
    return factories.FirstOrDefault(f => f.CanFoo())?.CreateFoo();
});
public interface IFooFactory
{
    bool CanFoo();
    IFoo CreateFoo();
}

public Foo1Factory : IFooFactory
{
    public bool CanFoo() => true;
    public IFoo CreateFoo() => new Foo1();
}
public Foo2Factory : IFooFactory
{
    public bool CanFoo() => false;
    public IFoo CreateFoo() => new Foo2();
}

Конечно, вместо обновления Foo Реализации внутри фабрики также можно передать поставщику услуг и разрешить их (если они зарегистрированы).

0
ответ дан poke 17 January 2019 в 10:02
поделиться

приведенный ниже код сначала регистрирует Foo1 для IFoo, а затем заменяет службу для IFoo на Foo2.

services
  .AddTransient<IFoo, Foo1>()
  .AddTransient<IFoo, Foo2>();

для вашего третьего запроса,

Вы не можете сделать это, потому что вы пытаетесь сгенерировать регистрацию для IFoo, используя IFoo, что приводит к ожидаемому StackOverflowException (как вы уже упоминали) ).

1113 То, что вы делаете, это. Сначала зарегистрируйте каждую службу отдельно,

 services
  .AddTransient<Foo1>()
  .AddTransient<Foo2>();

, затем используйте условную регистрацию, используя фабричный метод.

serviceCollection.AddTransient(factory =>
{
    //get the key here. you can use factory.GetService
    // to get another service and extract Key from it.
     switch(key)
     {
         case "Foo1":
              return factory.GetService<Foo1>();
              break;
         case "Foo2":
              return factory.GetService<Foo2>();
              break;
         default:
              return null;
      }
}
0
ответ дан Derviş Kayımbaşıoğlu 17 January 2019 в 10:02
поделиться

Вы можете зарегистрировать фабрику, как

        serviceCollection.AddTransient(factory =>
       {
           Func<string, IFoo> mytype = key =>
           {
               switch (key)
               {
                   case "Foo1":
                       return factory.GetService<Foo1>();
                   case "Foo2":
                       return factory.GetService<Foo2>();
                   default:
                       return null;
               }
           };
           return mytype;
       })

Откуда бы вы ни пытались использовать тип, который вы можете объявить и ввести его

private Func<string, IFoo> newType;

Вы можете позвонить затем

[ 112]
0
ответ дан Rahul 17 January 2019 в 10:02
поделиться

Похоже, что фактическая проблема состоит в том, как выбрать реализацию сервиса на основе некоторых критериев. Исключение выдается, потому что GetServices возвращает все сервисов , произведенных регистрациями, включая сам фабричный метод. Когда фабрика вызывает GetServices, она в конечном итоге вызывает себя рекурсивно, пока не возникнет исключение StackOverflowException.

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

Рекурсии можно избежать, если службы регистрируются напрямую, и фабричный метод вызывает GetService с нужным типом, как только он решает, какой из них требуется: [

services
    .AddTransient<Foo1>()
    .AddTransient<Foo2>();

// register the factory last
services.AddTransient<IFoo>(provider =>
{
    var type=PickFooType();

    return provider.GetService(type);
};

. Трюк теперь выбирает правильный тип. Это зависит от фактического критерия. Одно дело выбрать тип на основе, например, некоторой конфигурации или легкодоступного состояния, это совсем другое - выбрать его на основе свойств класса реализации.

В «простом» случае давайте предположим, что нужно выбрать правильный тип, основываясь на флаге или настройке конфигурации. Выбор типа может быть простым switch.

Если эта настройка доступна при запуске, можно избежать заводских настроек и просто зарегистрировать нужный тип.

В некоторых случаях настройка может измениться во время выполнения. Например, в случае сбоя одного удаленного поставщика услуг нам может потребоваться переключиться на другого. В простейшем случае можно использовать простой переключатель:

Type PickPaymentProvider()
{
    var activeProvider=LoadActiveProvider();
    switch (activeProvider)
    {
        case 'Paypal':
            return typeof(Foo1);
        case 'Visa' :
            return typeof(Foo2);
        ...
    }
}        

Обновление - разрешение зависимостей на основе контекста

Из обновления вопроса кажется, что проблема не не как создать фабрику. Это как выбрать сервис на основе контекста каждого отдельного запроса (значения параметров, переменные среды, данные и т. Д.). Это называется контекстным разрешением зависимостей. Он доступен в усовершенствованных контейнерах DI, таких как Autofac , но не в реализации и абстракциях DI .NET Core.

Правильный способ его добавления потребует добавления промежуточного программного обеспечения, которое заменяет шаг разрешения DI перед каждым запросом. Быстрый и грязный способ - добавить саму фабричную функцию в качестве службы и вызывать ее при необходимости. Это то, что Рахул показывает в своем ответе.

Это означает, что AddTransient придется зарегистрировать функцию , которая принимает параметры, необходимые для разрешения:

services.AddTransient(provider =>
{
    IFoo resolver(MyParam1 param1,MyParam2 param2)
    {
        var type=PickFooType(param1,param2);    
        return provider.GetService(type);
    }

    return resolver;
};

Это регистрирует Func<MyParam1,MyParam2,IFoo>. Контроллер может запросить эту функцию либо через конструктор, либо через внедрение действия :

public IActionResult MyAction([FromServices] resolver,int id,MyParam1 param1...)
{
    MyParam2 param2=LoadFromDatabase(id);
    IFoo service=resolver(param1,param2);
    var result=service.DoSomeJob();
    return OK(result);
}

. Регистрируя фабрику вместо интерфейса IFoo, мы можем зарегистрировать услуги по интерфейсу снова.

services
    .AddTransient<IFoo,Foo1>()
    .AddTransient<IFoo,Foo2>();

Если нам нужно позвонить в каждую зарегистрированную службу и спросить ее, может ли она обслуживать наш запрос, мы можем снова использовать GetServices:

services.AddTransient(provider =>
{
    IFoo resolver(MyParam1 param1,MyParam2 param2)
    {
        var firstMatch=provider.GetServices<IFoo>()
                               .First(svc=>svc.CanFoo(param1,param2));
        return firstMatch;
    }

    return resolver;
};
0
ответ дан Panagiotis Kanavos 17 January 2019 в 10:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: