Не удается разрешить общий сервис, используя перехват вручную с ASP.NET Core 2 и Castle.Core - PullRequest
0 голосов
/ 26 июня 2018

Я знаю, что есть много вопросов, похожих на этот, но на самом деле никто из них не решил мою проблему.Я создал новое приложение Asp.Net Core 2.Сейчас я пытаюсь использовать перехватчик для конкретной службы для извлечения некоторых данных в эту службу ( Я использую Castle.Core пакет nuget ).У меня есть общий IConfigurationInterceptor<> и реальная реализация ConfigurationInterceptor<>

Вот интерфейс:

public interface IConfigurationInterceptor<T> : IInterceptor where T : class { }

public class ConfigurationInterceptor<T> : IConfigurationInterceptor<T> where T : class
{
     public ConfigurationInterceptor(ConfigurationInfo<T> configurationInfo, 
        some other services)
    {
        _configurationInfo = configurationInfo;  
       //.....
    }
    public void Intercept(IInvocation invocation)
    {
        invocation.ReturnValue = somefunc(someconfig, invocation.Arguments);
    }
}

Тогда у меня есть метод расширения, как показано ниже:

public static void AddSingletonConfiguration<TInterface, TImplementation>(
    this IServiceCollection services, string featureName) 
    where TImplementation : class, TInterface where TInterface : class
{
    var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
    var generator = new ProxyGenerator();

    services.AddSingleton(x =>
    {
        var ic = x.GetService<Func<ConfigurationInfo<TImplementation>, 
            IConfigurationInterceptor<TImplementation>>>();

        var icTemp = ic.Invoke(info);

        return (TInterface) generator.CreateInterfaceProxyWithoutTarget(
            info.ServiceType, icTemp);
    });
}

Но когда я добираюсь до этой строки кода:

 var ic = x.GetService<Func<ConfigurationInfo<TImplementation>, 
    IConfigurationInterceptor<TImplementation>>>();

он возвращает мне нулевое значение для ic:

ConfigurationInfo class - это просто простой класс, который я создаю дляхранение некоторых дополнительных данных.

public sealed class ConfigurationInfo<TImpl>
{
    public Type ServiceType { get; }
    public string FeatureName { get; }

    public ConfigurationInfo(string featureName, Type serviceType)
    {
        FeatureName = featureName;
        ServiceType = serviceType;
    }

    public override string ToString() 
        => $"{FeatureName} ({ServiceType} -> {typeof(TImpl)})";
}

В моем ConfigureServices есть обе эти строки:

services.AddSingleton(typeof(IConfigurationInterceptor<>), 
    typeof(ConfigurationInterceptor<>));
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
    "SomeFeatureKey");

Я не уверен, почему переменная ic равна нулю, потому что ранее другой проект былиспользуя Autofac и работал отлично, но при запуске вы найдете что-то вроде этого:

builder.RegisterGeneric(typeof(ConfigurationInterceptor<>))
    .As(typeof(IConfigurationInterceptor<>)).SingleInstance();

builder.RegisterConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
    "SomeFeatureKey");

, а метод расширения был такой:

public static void RegisterConfiguration<TInterface, TImplementation>(
    this ContainerBuilder builder, string featureName) 
    where TImplementation : class, TInterface
{
    var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
    var generator = new ProxyGenerator();

    builder
        .Register(c =>
        {
            var ic = c.Resolve<Func<ConfigurationInfo<TImplementation>, 
                IConfigurationInterceptor<TImplementation>>>()(info);
            return generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, ic);
        })
        .As<TInterface>()
        .SingleInstance();
}

Любая помощь будет оценена.

РЕДАКТИРОВАНИЕ 1:

Теперь я изменил метод GetService<> на метод GetRequiredService<> и выдает исключение, как показано ниже:

Нет службы для типа 'Система.Func'2 [StaticDataProvider.DomainModel.ConfigurationInfo'1 [StaticDataProvider.Services.StaticDataConfiguration], StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1 [StaticDataProvider.Services.StaticDataConfiguration]] 'зарегистрирован.

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

Чтобы обернуть это, вот проблема: Вмой текущий проект в ядре Asp.Net я не могу получить Func<X, B>, в то время как в проекте Asp.Net MVC 5 (это совершенно другой проект) я могу получить Func<X, B> с помощью Autofac.Я думаю, что это связано с параметризованной функцией создания экземпляров в Autofac, предоставляемой по умолчанию: здесь

Теперь я не знаю, есть ли в контейнере DI по умолчанию в Asp.Net Core что-то вроде этого «параметризованной реализации'функция, которая позволяет мне разрешать Func<X, B> вместо B.

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Я оставлю свой предыдущий ответ для потомков, но ... Поставщик Microsoft IoC по умолчанию очень прост и не поддерживает все функции Autofac. Вы не получите параметризованное разрешение или автоматически сгенерированные фабрики.

0 голосов
/ 26 июня 2018

Вот что я должен был сделать: Модифицированный ConfigureService метод, как показано ниже:

public void ConfigureServices(IServiceCollection services)
{
    IConfigurationInterceptor<T> GetConfigurationInterceptor<T>(ConfigurationInfo<T> info) where T : class 
    { 
        return new ConfigurationInterceptor<T>(info, services.GetService<IConfigurationProvider>(), Configuration);
    }

    services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>("someFeatureKey", GetConfigurationInterceptor);
 }

Затем изменили методы расширения, как показано ниже:

public static void AddSingletonConfiguration<TInterface, TImplementation>(this IServiceCollection services, 
    string featureName, Func<ConfigurationInfo<TImplementation>, IConfigurationInterceptor<TImplementation>> ic) where TImplementation : class, TInterface where TInterface : class
{ 
    var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
    var generator = new ProxyGenerator();
    services.AddSingleton(x =>
    {
        var icTemp = ic.Invoke(info);
        return (TInterface) generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, icTemp);
    });
}
public static TInterface GetService<TInterface>(this IServiceCollection services) where TInterface : class
{ 
    var serviceProvider = services.BuildServiceProvider();
    return serviceProvider.GetRequiredService<TInterface>();
}

Теперь все работает нормально, но идея в том, что мне пришлось создать Func<X, B> самостоятельно и передать в качестве параметра в метод расширения.

0 голосов
/ 26 июня 2018

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

Если вы используете перехватчики с Autofac, было бы лучше использовать Autofac.Extras.DynamicProxy2пакет и подключают перехватчики, используя встроенную функциональность Autofac вместо того, чтобы пытаться связать несколько разрешений вместе с функциями и параметрами.Я вижу здесь много маленьких ошибок, например, как вы настраиваете одноэлементный интерфейсный прокси без цели , но мне не совсем понятно, как цель добавляется постфактум.Существует много сложностей, которых вы можете избежать, используя предоставленные инструменты.

Тем не менее, я также смотрю на сообщение об исключении.Без трассировки стека я не могу на 100% гарантировать это, но поиск по источнику Autofac показывает, что это не сообщение, полученное от Autofac - скорее всего, это сообщение из контейнера Microsoft.Extensions.DependencyInjection по умолчанию.Это указывает на то, что у вас может быть не все организовано так, как вы думаете.

Я бы немного отступил и просто начал работать с простыми вещами и убедиться, что они исходят от Autofac.Если вы решили, что не хотите использовать Autofac в игре, убедитесь, что вы полностью удалили его из уравнения.По сути, просто убедитесь, что он чистый и работает в общем смысле.

После этого добавляйте вещи медленно, по одному за раз.Я мог бы порекомендовать поместить репродукцию в модульный тест, в котором вы используете эти механизмы регистрации и работаете без усложнения всего приложения.Размотайте это оттуда.Если это слишком сложно для модульного тестирования ... возможно, это индикатор, который вы должны упростить и реорганизовать.Сделайте это тестируемым.

...