无法通过 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>
并作为参数传递给扩展方法。
我知道有很多与此类似的问题,但实际上 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>
并作为参数传递给扩展方法。