我有一个包含以下代码的库,用于依赖项注入。这将加载所有以Handler
结尾的实现类并进行注册。
public static class HandlerRegistrationExtension
{
private static IDictionary<Type, Type> _decoratorsAttributes;
public static void AddHandlers(this IServiceCollection services, IDictionary<Type, Type> decoratorsAttributes)
{
_decoratorsAttributes = decoratorsAttributes ?? new Dictionary<Type, Type>();
List<Type> allAssembliesTypes = Assembly
.GetEntryAssembly()
.GetReferencedAssemblies()
.Select(Assembly.Load)
.SelectMany(a => a.GetTypes())
.ToList();
List<Type> handlerTypes = allAssembliesTypes
.Where(x => x.GetInterfaces().Any(y => IsHandlerInterface(y)))
.Where(x => x.Name.EndsWith("Handler", StringComparison.Ordinal))
.ToList();
foreach (Type type in handlerTypes)
{
AddHandler(services, type);
}
}
private static void AddHandler(IServiceCollection services, Type type)
{
object[] attributes = type.GetCustomAttributes(false);
List<Type> pipeline = attributes
.Select(x => ToDecorator(x))
.Concat(new[] { type })
.Reverse()
.ToList();
Type interfaceType = type.GetInterfaces().Single(y => IsHandlerInterface(y));
Func<IServiceProvider, object> factory = BuildPipeline(pipeline, interfaceType);
services.AddTransient(interfaceType, factory);
}
private static Func<IServiceProvider, object> BuildPipeline(List<Type> pipeline, Type interfaceType)
{
List<ConstructorInfo> ctors = pipeline
.Select(x =>
{
Type type = x.IsGenericType ? x.MakeGenericType(interfaceType.GenericTypeArguments) : x;
return type.GetConstructors().Single();
})
.ToList();
Func<IServiceProvider, object> func = provider =>
{
object current = null;
foreach (ConstructorInfo ctor in ctors)
{
List<ParameterInfo> parameterInfos = ctor.GetParameters().ToList();
object[] parameters = GetParameters(parameterInfos, current, provider);
current = ctor.Invoke(parameters);
}
return current;
};
return func;
}
private static object[] GetParameters(List<ParameterInfo> parameterInfos, object current, IServiceProvider provider)
{
var result = new object[parameterInfos.Count];
for (int i = 0; i < parameterInfos.Count; i++)
{
result[i] = GetParameter(parameterInfos[i], current, provider);
}
return result;
}
private static object GetParameter(ParameterInfo parameterInfo, object current, IServiceProvider provider)
{
Type parameterType = parameterInfo.ParameterType;
if (IsHandlerInterface(parameterType))
return current;
object service = provider.GetService(parameterType);
if (service != null)
return service;
throw new ArgumentException($"Type {parameterType} not found");
}
private static Type ToDecorator(object attribute)
{
Type type = attribute.GetType();
if (_decoratorsAttributes.ContainsKey(type))
{
return _decoratorsAttributes[type];
}
throw new ArgumentException(attribute.ToString());
}
private static bool IsHandlerInterface(Type type)
{
if (!type.IsGenericType)
return false;
Type typeDefinition = type.GetGenericTypeDefinition();
return typeDefinition == typeof(ICommandHandler<,>) || typeDefinition == typeof(IQueryHandler<,>);
}
}
当我在AWS Lambda函数中部署应用程序时,似乎找不到请求实现处理程序的代码。
private readonly IServiceProvider _provider;
public MessagesDispatcher(IServiceProvider provider)
{
_provider = provider;
}
public async Task<T> DispatchAsync<T>(ICommand<T> command, CancellationToken cancellationToken)
{
Type type = typeof(ICommandHandler<,>);
Type[] typeArgs = { command.GetType(), typeof(T) };
Type handlerType = type.MakeGenericType(typeArgs);
dynamic handler = _provider.GetService(handlerType);
T result = await handler.HandleAsync((dynamic)command, cancellationToken);
return result;
}
我不知道在应用与反射加载的程序集相关的lambda中部署应用程序会发生什么变化,因为使用LocalEntryPoint.cs
时代码可以正常工作。
答案 0 :(得分:1)
经过一些搜索和尝试/失败过程后,我发现以下方法可以使Assembly决定即将使用的二进制类型的程序集在AWS Lambda环境中工作。下面是更改后的方法AddHandlers
。
public static void AddHandlers(
this IServiceCollection services,
IDictionary<Type, Type> decoratorsAttributes,
params Assembly[] assemblies) // Added a parameter to pass multiple assemblies
{
_decoratorsAttributes = decoratorsAttributes ?? new Dictionary<Type, Type>();
List<Type> allAssembliesTypes = assemblies // Here we get the types from the assembly
.SelectMany(a => a.GetTypes())
.ToList();
List<Type> handlerTypes = allAssembliesTypes
.Where(x => x.GetInterfaces().Any(y => IsHandlerInterface(y)))
.Where(x => x.Name.EndsWith("Handler", StringComparison.Ordinal))
.ToList();
foreach (Type type in handlerTypes)
{
AddHandler(services, type);
}
}
在Startup.cs
中,我叫AddHandlers
,如下所示。
services.AddHandlers(new Dictionary<Type, Type>
{
{ typeof(CircuitBreakerCommandDecoratorAttribute), typeof(CircuitBreakerCommandDecorator<,>) },
{ typeof(CircuitBreakerQueryDecoratorAttribute), typeof(CircuitBreakerQueryDecorator<,>) }
},
typeof(RegisterUserCommandHandler).GetTypeInfo().Assembly); .. This way the assembly containing the types I am scanning is loaded correctly