ASP.NET Core 2.1 反射在 AWS lambda 中部署时不起作用
ASP.NET Core 2.1 reflection doesn't work when deploying in AWS lambda
我有一个包含以下依赖注入代码的库。这将加载所有以 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
时工作正常。
经过一些搜索和 try/fail 过程后,我发现以下获取程序集并确定二进制文件类型的方法在 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
我有一个包含以下依赖注入代码的库。这将加载所有以 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
时工作正常。
经过一些搜索和 try/fail 过程后,我发现以下获取程序集并确定二进制文件类型的方法在 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