【问题标题】:Can not resolve a generic service using manual interception with ASP.NET Core 2 and Castle.Core无法使用 ASP.NET Core 2 和 Castle.Core 手动拦截来解析通用服务
【发布时间】:2018-06-26 09:19:25
【问题描述】:

我知道有很多类似的问题,但实际上没有一个能解决我的问题。 我创建了一个新的 Asp.Net Core 2 应用程序。 现在我正在尝试对特定服务使用拦截器来获取一些数据到该服务中(我正在使用Castle.Corenuget 包)。 我有一个通用的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 类只是我为存储一些额外数据而创建的一个简单类。

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&lt;&gt; 更改为方法 GetRequiredService&lt;&gt; 并抛出如下异常:

“System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]”类型的服务未注册.

编辑 2:

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

现在,我不知道在 Asp.Net Core 默认 DI 容器中是否有类似“参数化实例化”的功能,它允许我解析 Func&lt;X, B&gt; 而不是 B

【问题讨论】:

  • .Net Core 要求你手动注册你想通过 di 抓取的 Func

标签: c# dependency-injection autofac asp.net-core-2.0 castle


【解决方案1】:

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

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

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

我会稍微备份一下,让简单的事情正常工作,并确保它们来自 Autofac。如果您决定不希望使用 Autofac,请确保已将其完全从等式中删除。基本上,只要确保它是干净的并且在一般意义上可以正常工作。

之后,慢慢添加东西,一次一个。我可能会建议在单元测试中进行复制,您可以在其中使用这些注册机制并在不影响整个应用程序复杂性的情况下让事情正常工作。从那里解开它。如果单元测试太复杂......也许这是一个指标,你应该简化它并重构。使其可测试。

【讨论】:

  • 嗨@Travis,感谢您的回复。这很有帮助,但让我们了解一些更具体的细节。在当前项目中,我不使用 Autofac(仅 Asp.Net Core 默认 DI 容器),但在旧项目中他们使用 Autofac。在旧项目中它可以工作,而在仅使用默认 DI 容器的新项目中则不能。我认为我的问题是因为在 Autofac 中你有参数化的实例化,而在 asp.net 核心中我不知道是否有等效的东西。 autofaccn.readthedocs.io/en/latest/resolve/…
  • 我可以稍后在使它起作用时对其进行重构,因为这甚至不是我的代码,我需要一些时间来重新考虑创建这些代理类的更好解决方案。但目前的想法是,拦截器将在somefunc(someconfig, invocation.Arguments) 使用一些反射来将实现的服务类属性与来自另一个应用程序的 json 内容映射
  • 我很乐意提供帮助,但有两个挑战:1)您上面提出的问题不是关于从一个容器转换到另一个容器,而是关于如何解决特定类型的您已经特别展示了 Autofac 的使用并标记了问题autofac。我回答了问题的那一部分。转换是一个单独的问题。 2)这些“这不是我的代码,帮我转换”之类的东西......他们可以用链式问题持续很长时间。我很乐意回答上述问题,但如果您还有其他问题,请单独提出。
  • 如果您正在努力弄清楚如何将问题构建成一个小的、一口大小的块,StackOverflow 的观众可以通过一些重复来回答,那么有人可以修补和帮助......也许您需要顾问而不是问答网站。
  • 添加了更多信息。希望它有助于了解问题。我的问题与 autofac 没有直接关系。我在那里添加 autofac 标签的原因是因为我认为有人会更好地理解这个问题,并给我另一种解决问题的方法,而不依赖于 autofac 本身
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    这是我必须做的: 修改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&lt;X, B&gt; 并作为参数传递给扩展方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-20
      • 1970-01-01
      相关资源
      最近更新 更多