Как заставить Fun c встраивать строго зависимые зависимости в net core 3? - PullRequest
0 голосов
/ 29 апреля 2020

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

Вот интерфейс для обработки сообщений:

public interface IMessageHandler
{
    void HandleMessageAsync(object sender, MyEventArgs e);
}

И прототип моего метода расширения в lib:

public static IServiceCollection UseTheSuperMessageBroker(
    this IServiceCollection services,
    IConfiguration config,
    params Func<IServiceCollection>[] handlers)

Затем клиент может использовать метод расширения следующим образом:

services.UseTheSuperMessageBroker(Configuration,
handlers: new Func<IServiceCollection>[] {
    () => services.AddSingleton<IMessageHandler, myMessageHandler1>(),
    () => services.AddSingleton<IMessageHandler, myMessageHandler2>()
});

Но ничто не мешает клиенту предоставлять любое внедрение зависимостей, не связанное с интерфейсом IMessageHandler, действительно, последний параметр позволяет нам сделать это:

services.UseTheSuperMessageBroker(
    Configuration,
    handlers: new Func<IServiceCollection>[] {
        () => services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(),
        () => services.AddSingleton(typeof(IMongoRepository<>), typeof(MongoRepository<>))
    });

Это компилируется, но не ожидается.

Так есть ли какой-нибудь механизм, который я мог бы использовать, чтобы заставить Func использовать внедрение зависимостей, относящееся только к интерфейсу IMessageHandler?

Ответы [ 2 ]

1 голос
/ 29 апреля 2020

Нет. Нет никакого способа остановить это, потому что вы просто используете IServiceCollection напрямую. Фактически, этот код здесь даже не использует лямбда-локали, а скорее локали содержащего метода, то есть services. По сути, все, что может быть сделано здесь, при условии, что для этого есть средства в ConfigureServices.

Было бы гораздо лучше, если бы это было вашим требованием, просто взять коллекцию типов IMessageHandler и затем зарегистрируйте их в методе, вместо использования коллекции Func.

public static IServiceCollection UseTheSuperMessageBroker(
    this IServiceCollection services,
    IConfiguration config,
    params Type[] handlers)

services.UseTheSuperMessageBroker(Configuration,
    handlers: new[] {
        typeof(myMessageHandler1),
        typeof(myMessageHandler2)
    });

Затем внутри этого метода:

foreach (var handler in handlers)
{
    services.AddSingleton(typeof(IMessageHandler), handler);
}

Это выиграло технически не мешайте им добавлять типы, которые не реализуют IMessageHandler, но, поскольку вы явно привязываетесь к IMessageHandler, произойдет сбой, если они это сделают.

0 голосов
/ 29 апреля 2020

РЕДАКТИРОВАТЬ:

Я на самом деле легко может быть "взломан", но я не могу понять, почему мы можем это сделать? Здесь я убираю IMessageHandler требование к интерфейсу для параметра TService generi c, поэтому я могу использовать любой интерфейс с его реализацией.

public class DelegateHandler
{
    public delegate void MyMessagebrokerDelegateHandler<TService, TImplementation>(IServiceCollection services) 
        where TService : class, IMessageHandler
        where TImplementation : class, TService, new();

public static void MyMessagebrokerHandlerSupplier<TService, TImplementation>(IServiceCollection services)
    where TService : class //, IMessageHandler <== REMOVED !!!
    where TImplementation : class, TService, new()
{
    services.AddSingleton<TService, TImplementation>();
}

}

Так Я могу сделать:

  services.UseTheSuperMessageBroker(Configuration,
  handlers: new MyMessagebrokerDelegateHandler<IMessageHandler, BaseMessageHandler>[] {
                MyMessagebrokerHandlerSupplier<IHttpContextAccessor, HttpContextAccessor>, // <== HACKED !
                MyMessagebrokerHandlerSupplier<IMessageHandler, myMessageHandler2>
            });

Но ПОЧЕМУ я могу использовать обещание, которое не уважает прототип делегата? TService : class, IMessageHandler в делегате и TService : where TService : class в методе stati c ??

Исходное сообщение:

Я наконец нашел решение, основанное на generic delegates, я не знаю если то же самое можно сделать и с Func. Вот мой новый метод расширения:

        public static IServiceCollection UseTheSuperMessageBroker<TService, TImplementation>(this IServiceCollection services, IConfiguration config,  params MyMessagebrokerDelegateHandler<TService, TImplementation>[] handlers)
        where TService : class, IMessageHandler
        where TImplementation : class, TService, new()
       {
          handlers.ToList().ForEach(h =>
          {
            // Resolve the promise
            h.Invoke(services);
          });
        ...

Мой обработчик generi c:

public class DelegateHandler
{
    public delegate void MyMessagebrokerDelegateHandler<TService, TImplementation>(IServiceCollection services) 
        where TService : class, IMessageHandler
        where TImplementation : class, TService, new();

    public static void MyMessagebrokerHandlerSupplier<TService, TImplementation>(IServiceCollection services)
        where TService : class, IMessageHandler
        where TImplementation : class, TService, new()
    {
        services.AddSingleton<TService, TImplementation>();
    }
}

Базовый класс, используемый только для объявления клиента ( знаете ли вы какие-либо способ избавиться от этого? )

public sealed class BaseMessageHandler : IMessageHandler
{
    public Subscribers _sub { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

    public void HandleMessageAsync(object sender, BasicDeliverEventArgs e)
    {
        throw new NotImplementedException();
    }
}

И, наконец, клиент звонит:

      services.UseTheSuperMessageBroker(Configuration,
      handlers: new MyMessagebrokerDelegateHandler<IMessageHandler, BaseMessageHandler>[] {
                    MyMessagebrokerHandlerSupplier<IMessageHandler, myMessageHandler1>,
                    MyMessagebrokerHandlerSupplier<IMessageHandler, myMessageHandler2>
                });

Работает как шарм!

...