在AWS Lambda中进行部署时,ASP.NET Core 2.1反射不起作用

时间:2019-05-13 20:44:39

标签: aws-lambda asp.net-core-2.1 system.reflection

我有一个包含以下代码的库,用于依赖项注入。这将加载所有以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时代码可以正常工作。

1 个答案:

答案 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