无法通过 ASP.NET 核心 2 和 Castle.Core 使用手动拦截解析通用服务

Can not resolve a generic service using manual interception with ASP.NET Core 2 and Castle.Core

我知道有很多与此类似的问题,但实际上 none 解决了我的问题。 我创建了一个新的 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>>>();

它 returns 我 ic:

的空值

ConfigurationInfo class 只是我创建的一个简单的 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<> 并抛出如下异常:

No service for type 'System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]' has been registered.

编辑 2:

总结一下就是问题:在我当前的 Asp.Net 核心项目中,在 Asp.Net MVC 5 项目中我无法获得 Func<X, B>(这是一个整个不同的项目)我可以使用 Autofac 获得 Func<X, B>。我认为这与默认提供的 Autofac 中的参数化实例化功能有关:here

现在,我不知道 Asp.Net 核心默认 DI 容器是否有类似 'parametrized instantiation' 的功能,它允许我解析 Func<X, B> 而不是 B

我猜问题的根源在于相当复杂的拦截器手动连接。

如果您在 Autofac 中使用拦截器,最好使用 Autofac.Extras.DynamicProxy2 包和 wire up interceptors using the built-in Autofac functionality 而不是尝试将一堆解析与函数和参数链接在一起。我在这里看到很多小陷阱,比如你如何设置一个没有目标的单例接口代理 但我不完全清楚目标是如何添加的 post -事实。使用提供的工具可以避免很多复杂性。

也就是说,我也在查看异常消息。没有堆栈跟踪,我不能 100% 保证它,但是对 Autofac 源的搜索表明这不是来自 Autofac 的消息——那么它很可能是来自默认 Microsoft.Extensions.DependencyInjection 容器的消息。这表明您实际上可能没有按照您认为的方式连接所有东西。

我会后退一点,让简单的东西正常工作,并确保它们来自 Autofac。如果您决定不想使用 Autofac,请确保您已将其完全从等式中删除。基本上,只要确保它在一般意义上是干净的并且可以正常工作即可。

之后,慢慢地添加东西,一次一个。我可能会建议将复制品放入单元测试中,您可以在其中使用这些注册机制并让事情正常运行,而不会拖累整个应用程序的复杂性。从那里放松它。如果它太复杂而无法进行单元测试......也许这是一个你应该简化它并重构的指标。使其可测试。

我会为后代留下我以前的答案,但是...默认的 Microsoft IoC 提供程序非常简单,不支持 Autofac 的所有功能。您不会从中获得参数化分辨率或自动生成的工厂。

这是我必须做的: 修改后的 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> 并作为参数传递给扩展方法。