Ejemplo n.º 1
0
        private static ClassDeclarationSyntax CreateClass(
            ClassDeclarationSyntax parentClass,
            ConstructorDeclarationSyntax constructor)
        {
            ObjectCreationExpressionSyntax CreateMoq(TypeSyntax moqType) =>
            EGH.CreateObject(
                moqType,
                SF.Argument(
                    EGH.MemberAccess(
                        GH.Identifier("MockBehavior"),
                        GH.Identifier("Strict"))));

            var mi = new MethodInspector(constructor);

            var recordBuilder = new RecordBuilder($"{mi.Name}Test")
                                .AddModifiers(Modifiers.Public);

            foreach (var p in mi.Parameters)
            {
                var mockedType = GetMockedType(p.Type);
                var moqType    = GH.GenericName("Mock", mockedType);
                recordBuilder.AddField(moqType, MockName(p.Name), CreateMoq(moqType));
            }

            recordBuilder.AddMethod(GenerateCreateSut(parentClass, mi));

            return(recordBuilder.Build());
        }
Ejemplo n.º 2
0
        public MethodInspector GetMethodInspector(BasicDeliverEventArgs eventDetails)
        {
            try
            {
                ulong CalculateHashCode()
                {
                    var resultBytes = new byte[8];

                    for (int i = 0; i < eventDetails.Body.Length; i++)
                    {
                        resultBytes[i % 8] ^= eventDetails.Body[i];
                    }

                    return(BitConverter.ToUInt64(resultBytes, 0));
                }

                Task <ulong> ghc = new Task <ulong>(CalculateHashCode);
                ghc.Start();
                MethodInspector inspector = JsonConvert.DeserializeObject <MethodInspector>(Encoding.UTF8.GetString(eventDetails.Body));
                if (inspector != null)
                {
                    inspector.Hash = ghc.Result;
                }
                return(inspector);
            }
            catch (Exception)
            {
                // This message is so badly formatted we'd never be able to do anything with it. Get rid.
                _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}",
                              new LogItem("Event", "Message failed to deserialise to MethodInspector, so it is rejected"),
                              new LogItem("MessageBody", () => Encoding.UTF8.GetString(eventDetails.Body)));
                return(null);
            }
        }
Ejemplo n.º 3
0
        private static MethodDeclarationSyntax GenerateCreateSut(
            ClassDeclarationSyntax classDeclaration,
            MethodInspector constructorInspector)
        {
            var methodBuilder = new MethodBuilder(GH.IdentifierToken("CreateSut"))
                                .Modifiers(Modifiers.Private);

            var constructorParameters = constructorInspector
                                        .Parameters
                                        .Select(p => SF.Argument(
                                                    EGH.MemberAccess(
                                                        EGH.ThisMemberAccess(
                                                            GH.Identifier(MockName(p.Name))
                                                            ),
                                                        GH.Identifier("Object")
                                                        )
                                                    )
                                                );

            methodBuilder.ArrowBody(
                EGH.Arrow(
                    EGH.CreateObject(
                        SF.IdentifierName(classDeclaration.Identifier),
                        constructorParameters.ToArray())));

            return(methodBuilder.Build());
        }
Ejemplo n.º 4
0
        private static ClassDeclarationSyntax CreateClass(
            SemanticModel semanticModel,
            MethodDeclarationSyntax method)
        {
            var mi            = new MethodInspector(method);
            var semanticQuery = mi.CreateSemanticQuery(semanticModel);
            var isTaskReturn  = SymbolSemanticQuery.IsTask(
                semanticQuery.GetReturnType().Symbol);

            var parameters = mi.Parameters.Select(par => par.Type).ToList();

            var recordBuilder = new RecordBuilder($"{mi.Name}Command")
                                .AddModifiers(Modifiers.Public)
                                .AddProperties(
                mi.Parameters
                .Select(p => (p.Type, p.Name)).ToArray());

            if (isTaskReturn.IsTask())
            {
                AddTaskUtilities(recordBuilder, Types.Bool);
            }
            else if (isTaskReturn.IsTypedTask(out var typeSymbol))
            {
                AddTaskUtilities(
                    recordBuilder,
                    GH.Identifier(SymbolSemanticQuery.GetName(typeSymbol)));
            }

            return(recordBuilder.Build());
        }
Ejemplo n.º 5
0
        private void LoadActions()
        {
            try{
                //drop Z3 files in VS folder. C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE
                Z3Link z3 = new Z3Link();
                z3.ProcessSyntaxTree(MethodInspector.SyntaxTree);
                IDictionary <string, List <object> > varValues = z3.Solve();
                //get local variables
                //create action for all singles
                //create action for all cross join variables

                foreach (Variable v in MethodInspector.LoadVariables())
                {
                    foreach (int assignmentLocation in v.VariableAssignmentLocations)
                    {
                        Action a = new Action()
                        {
                            Variable           = v,
                            BreakPointLocation = assignmentLocation,
                            ValueToAssign      = varValues[v.VariableName].FirstOrDefault(),
                        };
                        actions.Enqueue(a);
                    }
                }
            }
            catch (Exception e) {
            }
        }
        private static IEnumerable <MethodDeclarationSyntax> GenerateApiControllerMethods(IEnumerable <MethodDeclarationSyntax> grainInterfaceMethods, string grainName)
        {
            var methodsDeclarations = new List <MethodDeclarationSyntax>();

            foreach (var methodNode in grainInterfaceMethods)
            {
                // insert the id parameter at the end of the list of parameters
                var idParam = RoslynUtils.CreateParameter("string", "id");
                MethodDeclarationSyntax methodDclr = RoslynUtils.AppendParameterToMethod(methodNode, idParam);

                methodDclr = methodDclr.AddModifiers(SF.Token(SyntaxKind.PublicKeyword)).WithSemicolonToken(SF.Token(SyntaxKind.None));

                StatementSyntax getGrainStmt = SF.ParseStatement(string.Format(
                                                                     "var grain = _grainFactory.GetGrain<{0}>(id);\n", grainName));
                MethodInspector methodInspector = new MethodInspector(methodNode);

                string callGrainStmt = string.Format("grain.{0}({1});",
                                                     methodInspector.MethodName, string.Join(", ", methodInspector.MethodParams.Keys));

                if (methodInspector.ReturnType != "Task")
                {
                    callGrainStmt = callGrainStmt.Insert(0, "return ");
                }
                else
                {
                    callGrainStmt = callGrainStmt.Insert(0, "await ");
                    methodDclr    = methodDclr.AddModifiers(SF.Token(SyntaxKind.AsyncKeyword));
                }
                StatementSyntax returnStmt = SF.ParseStatement(callGrainStmt);

                methodsDeclarations.Add(methodDclr.WithBody(SF.Block(getGrainStmt, returnStmt)));
            }
            return(methodsDeclarations);
        }
Ejemplo n.º 7
0
        private bool MessageIsDuplicate(MethodInspector inspector)
        {
            bool duplicate = _methodInspectorManager.DuplicateCheck(inspector);

            if (duplicate)
            {
                _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(MessageIsDuplicate)}", new LogItem("Event", "Duplicate message found"), new LogItem("MessageId", inspector.Uuid.ToString));
            }

            return(duplicate);
        }
        private static MethodDeclarationSyntax GenerateMethodDeclaration(MethodInspector methodInspector)
        {
            MethodDeclarationSyntax methodDclr = SF.MethodDeclaration(SF.ParseTypeName(methodInspector.ReturnType), SF.Identifier(methodInspector.MethodName));

            foreach (KeyValuePair <string, string> keyValuePair in methodInspector.MethodParams)
            {
                string paramType = keyValuePair.Value;
                string paramName = keyValuePair.Key;
                methodDclr = RoslynUtils.AppendParameterToMethod(methodDclr, RoslynUtils.CreateParameter(paramType, paramName));
            }
            return(methodDclr);
        }
        private static ClassDeclarationSyntax GenerateWriteGrain(ClassDeclarationSyntax grainClass, ITypeSymbol swmrInterface, int readReplicaCount)
        {
            string grainName                   = grainClass.Identifier.Text;
            string writerGrainName             = SwmrUtils.GetWriteInterfaceName(grainName);
            string writerInterfaceName         = SwmrUtils.GetWriteInterfaceName(swmrInterface.Name);
            ClassDeclarationSyntax writerGrain = GenerateClassSqueleton(writerGrainName).WithBaseList(RoslynUtils.BaseList(new[] { "Grain", writerInterfaceName }));

            writerGrain = RoslynUtils.AddField(writerGrain, "ITopology<string>", "_topology");
            writerGrain = writerGrain.AddMembers(GenerateOnActivateAsyncMethod(readReplicaCount));

            string readReplicaInterfaceName = SwmrUtils.GetReadReplicaInterfaceName(swmrInterface.Name);

            foreach (ISymbol member in swmrInterface.GetMembers())
            {
                IMethodSymbol methodSymbol = member as IMethodSymbol;
                if (methodSymbol == null || IsReadOnlyMethod(methodSymbol) || new MethodInspector(methodSymbol).MethodName == "GetState")
                {
                    continue;
                }

                MethodInspector         methodInspector = new MethodInspector(methodSymbol);
                MethodDeclarationSyntax methodImpl      = GenerateMethodDeclaration(methodInspector);
                methodImpl = SwmrUtils.AddSessionIdParameter(methodImpl).AddModifiers(SF.Token(SyntaxKind.PublicKeyword), SF.Token(SyntaxKind.AsyncKeyword)).WithSemicolonToken(SF.Token(SyntaxKind.None));

                BlockSyntax statmentBlock = SF.Block();
                statmentBlock = AddStatement(statmentBlock, "string grainId = this.GetPrimaryKeyString();");
                statmentBlock = AddStatement(statmentBlock, string.Format("{0} grain = GrainFactory.GetGrain<{0}>(grainId);", swmrInterface.Name));
                statmentBlock = AddStatement(statmentBlock, String.Format("{0} await grain.{1}({2});", methodInspector.ReturnType != "Task"? "var result =" : "", methodInspector.MethodName, string.Join(", ", methodInspector.MethodParams.Keys)));
                statmentBlock = AddStatement(statmentBlock, "GrainState state = await grain.GetState();");
                statmentBlock = AddStatement(statmentBlock, "string sessionNode = _topology.GetNode(sessionId);");
                statmentBlock = AddStatement(statmentBlock, "IEnumerable<string> otherNodes = _topology.Nodes.Where(node => node != sessionNode);");

                ForEachStatementSyntax forEachStatement = SF.ForEachStatement(
                    SF.PredefinedType(SF.Token(SyntaxKind.StringKeyword)),
                    SF.Identifier("node"),
                    SF.IdentifierName("otherNodes"),
                    SF.Block(SF.ParseStatement(GenerateSetStateStmt(readReplicaInterfaceName, @"node")))
                    );

                statmentBlock = statmentBlock.AddStatements(forEachStatement);
                statmentBlock =
                    AddStatement(statmentBlock, (string.Format("{0} {1}", "await",
                                                               GenerateSetStateStmt(readReplicaInterfaceName, @"sessionNode"))));
                if (methodInspector.ReturnType != "Task")
                {
                    statmentBlock = AddStatement(statmentBlock, "return result;");
                }
                methodImpl  = methodImpl.WithBody(statmentBlock);
                writerGrain = writerGrain.AddMembers(methodImpl);
            }

            return(writerGrain);
        }
Ejemplo n.º 10
0
        private Type GetMessageHandlers(MethodInspector inspector)
        {
            lock (MethodHandlerDictionary)
            {
                if (!MethodHandlerDictionary.ContainsKey(inspector.Method))
                {
                    var bestHandler = GetBestHandlerFor(inspector);
                    if (bestHandler != null)
                    {
                        MethodHandlerDictionary[inspector.Method] = bestHandler;
                    }
                }

                return(MethodHandlerDictionary[inspector.Method]);
            }
        }
Ejemplo n.º 11
0
        private Type GetBestHandlerFor(MethodInspector inspector)
        {
            var messageHandlerTypes = _messageHandlerTypes ?? (_messageHandlerTypes = _container.GetAttributedTypes <HandlerAttribute>(typeof(IMessageHandler2 <,>)));

            _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(GetMessageHandlers)}",
                          new LogItem("Event", $"Found the following types that might be able to handle this message: {string.Join(",", messageHandlerTypes.Select(t => t.Key.Name))}"),
                          new LogItem("MessageId", inspector.Uuid.ToString));

            Type handlersMatchingMethod = messageHandlerTypes.FirstOrDefault(mht => mht.Value.Any(ha => ha.Method == inspector.Method)).Key;

            if (handlersMatchingMethod == null)
            {
                var blanketHandlers =
                    messageHandlerTypes.Where(mht => mht.Key.GetGenericArguments().All(arg => arg.IsGenericParameter) && mht.Value.Any(ha => ha.Method == "*"));
                return(blanketHandlers.First().Key);
            }

            return(handlersMatchingMethod);
        }
        private static ClassDeclarationSyntax GenerateReadGrain(ClassDeclarationSyntax grainClass, ITypeSymbol swmrInterface, int readReplicaCount)
        {
            string readGrainName             = SwmrUtils.GetReadInterfaceName(grainClass.Identifier.Text);
            string readerInterfaceName       = SwmrUtils.GetReadInterfaceName(swmrInterface.Name);
            ClassDeclarationSyntax readGrain = GenerateClassSqueleton(readGrainName).WithAttributeLists(AttributeUtils.AttributeListList(AttributeUtils.Attribute(StatelessWorkerAttributeName))).WithBaseList(RoslynUtils.BaseList(new[] { "Grain", readerInterfaceName }));

            readGrain = RoslynUtils.AddField(readGrain, "ITopology<string>", "_topology");
            readGrain = readGrain.AddMembers(GenerateOnActivateAsyncMethod(readReplicaCount));

            string readReplicaInterfaceName = SwmrUtils.GetReadReplicaInterfaceName(swmrInterface.Name);

            foreach (ISymbol member in swmrInterface.GetMembers())
            {
                IMethodSymbol methodSymbol = member as IMethodSymbol;
                if (methodSymbol == null || !IsReadOnlyMethod(methodSymbol))
                {
                    continue;
                }

                MethodInspector methodInspector = new MethodInspector(methodSymbol);
                string          parameters      = "string sessionId";
                if (methodInspector.MethodParams.Any())
                {
                    parameters = string.Join(", ", methodInspector.MethodParams.Select(param => string.Format("{0} {1}", param.Value, param.Key))) + " ," + parameters;
                }

                var method = RoslynUtils.ParseMethod(string.Format(
                                                         @"
        public {0} {1}({2})
        {{
            string sessionNode = _topology.GetNode(sessionId);
            var readReplica = GrainFactory.GetGrain<{3}>(sessionNode);
            return readReplica.{1}({4});
        }}", methodInspector.ReturnType, methodInspector.MethodName, parameters, readReplicaInterfaceName, string.Join(", ", methodInspector.MethodParams.Keys)));

                readGrain =
                    readGrain.AddMembers(
                        method.WithLeadingTrivia(SF.EndOfLine("")));
            }
            return(readGrain);
        }
Ejemplo n.º 13
0
 public MethodSemanticQuery(SemanticModel model, MethodInspector inspector)
 {
     this.model     = model;
     this.inspector = inspector;
 }
 private static MethodDeclarationSyntax GenerateMethodDeclaration(MethodInspector methodInspector)
 {
     MethodDeclarationSyntax methodDclr = SF.MethodDeclaration(SF.ParseTypeName(methodInspector.ReturnType), SF.Identifier(methodInspector.MethodName));
     foreach (KeyValuePair<string, string> keyValuePair in methodInspector.MethodParams)
     {
         string paramType = keyValuePair.Value;
         string paramName = keyValuePair.Key;
         methodDclr = RoslynUtils.AppendParameterToMethod(methodDclr, RoslynUtils.CreateParameter(paramType, paramName));
     }
     return methodDclr;
 }
        private static ClassDeclarationSyntax GenerateWriteGrain(ClassDeclarationSyntax grainClass, ITypeSymbol swmrInterface, int readReplicaCount)
        {
            string grainName = grainClass.Identifier.Text;
            string writerGrainName = SwmrUtils.GetWriteInterfaceName(grainName);
            string writerInterfaceName = SwmrUtils.GetWriteInterfaceName(swmrInterface.Name);
            ClassDeclarationSyntax writerGrain = GenerateClassSqueleton(writerGrainName).WithBaseList(RoslynUtils.BaseList(new[] { "Grain", writerInterfaceName }));

            writerGrain = RoslynUtils.AddField(writerGrain, "ITopology<string>", "_topology");
            writerGrain = writerGrain.AddMembers(GenerateOnActivateAsyncMethod(readReplicaCount));

            string readReplicaInterfaceName = SwmrUtils.GetReadReplicaInterfaceName(swmrInterface.Name);
            foreach (ISymbol member in swmrInterface.GetMembers())
            {
                IMethodSymbol methodSymbol = member as IMethodSymbol;
                if (methodSymbol == null || IsReadOnlyMethod(methodSymbol) || new MethodInspector(methodSymbol).MethodName == "GetState")
                {
                    continue;
                }

                MethodInspector methodInspector = new MethodInspector(methodSymbol);
                MethodDeclarationSyntax methodImpl = GenerateMethodDeclaration(methodInspector);
                methodImpl = SwmrUtils.AddSessionIdParameter(methodImpl).AddModifiers(SF.Token(SyntaxKind.PublicKeyword), SF.Token(SyntaxKind.AsyncKeyword)).WithSemicolonToken(SF.Token(SyntaxKind.None));

                BlockSyntax statmentBlock = SF.Block();
                statmentBlock = AddStatement(statmentBlock, "string grainId = this.GetPrimaryKeyString();");
                statmentBlock = AddStatement(statmentBlock, string.Format("{0} grain = GrainFactory.GetGrain<{0}>(grainId);", swmrInterface.Name));
                statmentBlock = AddStatement(statmentBlock, String.Format("{0} await grain.{1}({2});", methodInspector.ReturnType != "Task"? "var result =" : "", methodInspector.MethodName, string.Join(", ", methodInspector.MethodParams.Keys)));
                statmentBlock = AddStatement(statmentBlock, "GrainState state = await grain.GetState();");
                statmentBlock = AddStatement(statmentBlock, "string sessionNode = _topology.GetNode(sessionId);");
                statmentBlock = AddStatement(statmentBlock, "IEnumerable<string> otherNodes = _topology.Nodes.Where(node => node != sessionNode);");

                ForEachStatementSyntax forEachStatement = SF.ForEachStatement(
                    SF.PredefinedType(SF.Token(SyntaxKind.StringKeyword)),
                    SF.Identifier("node"),
                    SF.IdentifierName("otherNodes"),
                    SF.Block(SF.ParseStatement(GenerateSetStateStmt(readReplicaInterfaceName, @"node")))
                );

                statmentBlock = statmentBlock.AddStatements(forEachStatement);
                statmentBlock =
                    AddStatement(statmentBlock, (string.Format("{0} {1}", "await",
                            GenerateSetStateStmt(readReplicaInterfaceName, @"sessionNode"))));
                if (methodInspector.ReturnType != "Task")
                {
                    statmentBlock = AddStatement(statmentBlock, "return result;");
                }
                methodImpl = methodImpl.WithBody(statmentBlock);
                writerGrain = writerGrain.AddMembers(methodImpl);
            }

            return writerGrain;
        }
        private static ClassDeclarationSyntax GenerateReadGrain(ClassDeclarationSyntax grainClass, ITypeSymbol swmrInterface, int readReplicaCount)
        {
            string readGrainName = SwmrUtils.GetReadInterfaceName(grainClass.Identifier.Text);
            string readerInterfaceName = SwmrUtils.GetReadInterfaceName(swmrInterface.Name);
            ClassDeclarationSyntax readGrain = GenerateClassSqueleton(readGrainName).WithAttributeLists(AttributeUtils.AttributeListList(AttributeUtils.Attribute(StatelessWorkerAttributeName))).WithBaseList(RoslynUtils.BaseList(new[] { "Grain", readerInterfaceName }));
            readGrain = RoslynUtils.AddField(readGrain, "ITopology<string>", "_topology");
            readGrain = readGrain.AddMembers(GenerateOnActivateAsyncMethod(readReplicaCount));

            string readReplicaInterfaceName = SwmrUtils.GetReadReplicaInterfaceName(swmrInterface.Name);

            foreach (ISymbol member in swmrInterface.GetMembers())
            {
                IMethodSymbol methodSymbol = member as IMethodSymbol;
                if (methodSymbol == null || !IsReadOnlyMethod(methodSymbol))
                {
                    continue;
                }

                MethodInspector methodInspector = new MethodInspector(methodSymbol);
                string parameters = "string sessionId";
                if (methodInspector.MethodParams.Any())
                {
                    parameters = string.Join(", ", methodInspector.MethodParams.Select(param => string.Format("{0} {1}", param.Value, param.Key))) + " ," + parameters;
                }

                var method = RoslynUtils.ParseMethod(string.Format(
    @"
        public {0} {1}({2})
        {{
            string sessionNode = _topology.GetNode(sessionId);
            var readReplica = GrainFactory.GetGrain<{3}>(sessionNode);
            return readReplica.{1}({4});
        }}", methodInspector.ReturnType, methodInspector.MethodName, parameters, readReplicaInterfaceName, string.Join(", ", methodInspector.MethodParams.Keys)));

                readGrain =
                    readGrain.AddMembers(
                        method.WithLeadingTrivia(SF.EndOfLine("")));
            }
            return readGrain;
        }
        private static IEnumerable<MethodDeclarationSyntax> GenerateApiControllerMethods(IEnumerable<MethodDeclarationSyntax> grainInterfaceMethods, string grainName)
        {
            var methodsDeclarations = new List<MethodDeclarationSyntax>();
            foreach (var methodNode in grainInterfaceMethods)
            {
                // insert the id parameter at the end of the list of parameters
                var idParam = RoslynUtils.CreateParameter("string", "id");
                MethodDeclarationSyntax methodDclr = RoslynUtils.AppendParameterToMethod(methodNode, idParam);

                methodDclr = methodDclr.AddModifiers(SF.Token(SyntaxKind.PublicKeyword)).WithSemicolonToken(SF.Token(SyntaxKind.None));

                StatementSyntax getGrainStmt = SF.ParseStatement(string.Format(
                        "var grain = _grainFactory.GetGrain<{0}>(id);\n", grainName));
                MethodInspector methodInspector = new MethodInspector(methodNode);

                string callGrainStmt = string.Format("grain.{0}({1});",
                    methodInspector.MethodName, string.Join(", ", methodInspector.MethodParams.Keys));

                if (methodInspector.ReturnType != "Task")
                {
                    callGrainStmt = callGrainStmt.Insert(0, "return ");
                }
                else
                {
                    callGrainStmt = callGrainStmt.Insert(0, "await ");
                    methodDclr = methodDclr.AddModifiers(SF.Token(SyntaxKind.AsyncKeyword));
                }
                StatementSyntax returnStmt = SF.ParseStatement(callGrainStmt);

                methodsDeclarations.Add(methodDclr.WithBody(SF.Block(getGrainStmt, returnStmt)));
            }
            return methodsDeclarations;
        }
Ejemplo n.º 18
0
        public Task ConsumeMessage(object sender, BasicDeliverEventArgs eventDetails)
        {
            try
            {
                inProgressMessages++;

                if (eventDetails?.Body == null || eventDetails.Body.Length < 1)
                {
                    return(RejectMessageWith(DiscardedMessageReason.MissingMessageBody, eventDetails));
                }

                // Deserialise into a class containing only Method and UUID - this also generates a hash
                MethodInspector inspector = GetMethodInspector(eventDetails);
                if (inspector == null)
                {
                    return(RejectMessageWith(DiscardedMessageReason.MethodDeserialisationError, eventDetails));
                }

                _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}",
                              new LogItem("Event", "Message routed"),
                              new LogItem("Calculated hashcode", inspector.Hash),
                              new LogItem("MessageId", inspector.Uuid.ToString),
                              new LogItem("MessageMethod", inspector.Method));

                if (MessageIsDuplicate(inspector))
                {
                    return(RejectMessageWith(DiscardedMessageReason.Duplicate, eventDetails));
                }

                Type handlerType = GetMessageHandlers(inspector);

                if (handlerType == null)
                {
                    _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}", new LogItem("Event", "No suitable handler found for Method " + inspector.Method));
                    // We can't process this message, so we'll Ack it.
                    _methodInspectorManager.Register(inspector); // we don't want to see it again

                    return(RejectMessageWith(DiscardedMessageReason.NoHandler, eventDetails));
                }

                Type[] genericArguments = GetGenericArguments(handlerType, inspector.Uuid);

                if (handlerType.GetTypeInfo().ContainsGenericParameters)
                {
                    handlerType = handlerType.MakeGenericType(genericArguments);
                }

                string bodyAsString = Encoding.UTF8.GetString(eventDetails.Body);
                if (!ConstructMessage(out object message, genericArguments, bodyAsString, inspector.Uuid))
                {
                    _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}",
                                  new LogItem("Event", "The Message could not be constructed, so it's been discarded."));

                    _methodInspectorManager.Register(inspector); // we don't want to see it again

                    // This is a Permanent Failure; if we couldn't construct the Message object now, processing it again won't help.
                    return(RejectMessageWith(DiscardedMessageReason.MessageDeserialisationError, eventDetails));
                }

                Type         messageType = message.GetType();
                PropertyInfo need;
                lock (NeedPropertyCache)
                {
                    if (!NeedPropertyCache.ContainsKey(messageType))
                    {
                        NeedPropertyCache.Add(messageType,
                                              messageType.GetProperty(nameof(Message <object, object> .Need)));
                    }
                    need = NeedPropertyCache[messageType];
                }

                if (need.GetValue(message) == null)
                {
                    _logger.Warn($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}",
                                 new LogItem("Event", "Message has no Need"), new LogItem("MessageId", inspector.Uuid.ToString));

                    _methodInspectorManager.Register(inspector); // we don't want to see it again

                    // This is a Permanent Failure; if there's no Need in the message now, processing it again won't change anything.
                    return(RejectMessageWith(DiscardedMessageReason.MissingNeed, eventDetails));
                }

                HandlerAttribute handlerAttribute = handlerType.GetTypeInfo().GetCustomAttributes <HandlerAttribute>().FirstOrDefault(a => a.Method == inspector.Method);

                if (!ProcessAcceptanceBehaviours(handlerAttribute, messageType, message))
                {
                    _methodInspectorManager.Register(inspector); // we don't want to see it again
                    return(RejectMessageWith(DiscardedMessageReason.AcceptanceBehaviourPrecondition, eventDetails));
                }

                _logger.Trace($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}",
                              new LogItem("Event", "Constructing handler"), new LogItem("MessageId", inspector.Uuid.ToString), new LogItem("HandlerName", handlerType.Name));

                var stopWatch = Stopwatch.StartNew();
                CompletionAction invocationResult = InvokeHandleMethod(inspector.Uuid, handlerType, message);
                stopWatch.Stop();

                _metrics.RecordProcessedMessage(handlerType.Name, stopWatch.Elapsed.TotalMilliseconds);

                if (invocationResult == CompletionAction.Republish)
                {
                    publishMethod = publishMethod ?? _queueWrapper.GetType().GetMethod(nameof(_queueWrapper.PublishMessage));
                    publishMethod.MakeGenericMethod(genericArguments).Invoke(_queueWrapper, new object[] { message, inspector.Uuid });
                }

                // Any exceptions thrown up to this point *are probably* Temporary Failures:
                // Permanent Failures will have been dealt with by local Exception Handling,
                // and the code will continue to this point.

                _methodInspectorManager.Register(inspector);

                _queueWrapper.AcknowledgeMessage(eventDetails);
            }
            catch (Exception e)
            {
                _logger.Exception($"{nameof(MessageSubscriber)}.{nameof(ConsumeMessage)}",
                                  "Failure during consuming of message",
                                  e);

                throw;
            }
            finally
            {
                inProgressMessages--;
            }

            return(Task.CompletedTask);
        }