/// <inheritdoc />
        public Variable Build(MiddlewareBuilderContext context, ExecutorReturnType executorReturnType)
        {
            // We rely on the compiler infrastructure to make the correct calls, to the correct type (i.e. the actual
            // operation), and to fill in the parameters of that method as required.
            var handlerInvokeCall = new MethodCall(context.Descriptor.OperationType, this._method)
            {
                IgnoreReturnVariable = executorReturnType == ExecutorReturnType.NoReturn,
            };

            // Note that although we know the handler type at compile time, we still specify it as a
            // parameter to logging so that it is output as a structured value (as it changes between
            // invocations)
            context.AppendFrames(
                LogFrame.Debug(
                    _apiOperationExecutorLogEvent,
                    "Executing API operation {OperationType} with inline handler",
                    ReflectionUtilities.PrettyTypeName(context.Descriptor.OperationType)),
                handlerInvokeCall);

            // We have a void, or a Task (i.e. async with no return) so we will convert to a 'NoResult'
            if (handlerInvokeCall.ReturnVariable == null || handlerInvokeCall.ReturnVariable.VariableType == typeof(Task))
            {
                var emptyResultCreation = new VariableCreationFrame(
                    typeof(NoResultOperationResult),
                    $"{typeof(NoResultOperationResult).FullNameInCode()}.{nameof(NoResultOperationResult.Instance)};");

                context.AppendFrames(emptyResultCreation);

                return(emptyResultCreation.CreatedVariable);
            }

            return(handlerInvokeCall.ReturnVariable);
        }
コード例 #2
0
        /// <inheritdoc />
        public void Build(
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedProperties,
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedBySource,
            MiddlewareBuilderContext context)
        {
            // Never apply if we are not in a HTTP-supported operation
            if (context.Descriptor.TryGetFeatureData <HttpOperationFeatureData>(out var _) == false)
            {
                return;
            }

            if (this._applies?.Invoke(context) == false)
            {
                return;
            }

            var operationVariable   = context.FindVariable(context.Descriptor.OperationType);
            var httpContextVariable = context.FindVariable(typeof(HttpContext));
            var sourceVariable      = this._sourceCodeExpression(httpContextVariable);

            foreach (var prop in this._isCatchAll ? context.Descriptor.Properties : ownedBySource.Select(s => s.Property))
            {
                if (prop.CanWrite == false)
                {
                    continue;
                }

                // If this is a catch-all instance we DO NOT want to generate any code for a property
                // that is owned by a different source.
                if (this._isCatchAll && ownedProperties.Any(p => p.Property == prop))
                {
                    continue;
                }

                var partKey           = this.GetPartKey(prop);
                var operationProperty = operationVariable.GetProperty(prop.Name);

                // When this property is not in ALL routes we use TryGetValue from the RouteData dictionary and pass
                // the var output from that to `GetConversionExpression` as part of ternary. If the route data
                // does not exist then we fallback to the value already on the operation
                var outVariableName      = $"{this._variablePrefix}{prop.Name}";
                var conversionExpression = GetConversionExpression(prop, outVariableName, this._supportsMultiValues, this._variablePrefix);

                var indexProperty = sourceVariable.VariableType.GetProperties().SingleOrDefault(p => p.GetIndexParameters().Length == 1);

                if (indexProperty == null)
                {
                    throw new InvalidOperationException($"Source variable {sourceVariable} has no index property specified. Cannot use");
                }

                var isStringValues = indexProperty.PropertyType == typeof(StringValues);
                var emptyCheckRhs  = isStringValues ? $"{typeof(StringValues).FullNameInCode()}.Empty" : "null";

                var source = $"{sourceVariable}[\"{partKey}\"]";

                // We want to support the key naming scheme of key[] to indicate an array, therefore
                // we add a null-coalesce to partKey[]
                if (IsArrayLike(prop.PropertyType, out _))
                {
                    source = $"{source} == {emptyCheckRhs} ? {sourceVariable}[\"{partKey}[]\"] : {source}";
                }

                var sourceVariableGetter = new VariableCreationFrame(typeof(object), outVariableName, source);

                // [Source] == null ? [conversion of valuePropertyName] : [operationProperty];
                var tryConversionExpression =
                    $"{sourceVariableGetter.CreatedVariable} != {emptyCheckRhs} ? " +
                    $"{conversionExpression} : " +
                    $"{operationProperty}";

                context.AppendFrames(
                    sourceVariableGetter,
                    new VariableSetterFrame(
                        operationProperty,
                        tryConversionExpression),
                    new BlankLineFrame());
            }
        }