/// <summary> /// Constructs a query/signal method map for a workflow type. /// </summary> /// <param name="workflowType">The workflow type.</param> /// <returns>The <see cref="WorkflowMethodMap"/>.</returns> public static WorkflowMethodMap Create(Type workflowType) { Covenant.Requires <ArgumentNullException>(workflowType != null, nameof(workflowType)); Covenant.Requires <ArgumentException>(!workflowType.IsInterface, nameof(workflowType)); var map = new WorkflowMethodMap(); foreach (var @interface in workflowType.GetInterfaces()) { foreach (var method in @interface.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { // Signal methods are tagged by [SignalMethod]. var signalMethodAttribute = method.GetCustomAttribute <SignalMethodAttribute>(); if (signalMethodAttribute != null) { if (signalMethodAttribute.Synchronous) { map.HasSynchronousSignals = true; } map.nameToSignalMethod[signalMethodAttribute.Name] = method; continue; } // Query methods are tagged by [QueryMethod]. var queryMethodAttribute = method.GetCustomAttribute <QueryMethodAttribute>(); if (queryMethodAttribute != null) { map.nameToQueryMethod[queryMethodAttribute.Name] = method; continue; } } } return(map); }
/// <summary> /// Registers a workflow implementation with temporal-proxy. /// </summary> /// <param name="workflowType">The workflow implementation type.</param> /// <exception cref="RegistrationException">Thrown when there's a problem with the registration.</exception> private async Task RegisterWorkflowImplementationAsync(Type workflowType) { await SyncContext.Clear; TemporalHelper.ValidateWorkflowImplementation(workflowType); var methodMap = WorkflowMethodMap.Create(workflowType); // We need to register each workflow method that implements a workflow interface method // with the same signature that that was tagged by [WorkflowMethod]. // // First, we'll create a dictionary that maps method signatures from any inherited // interfaces that are tagged by [WorkflowMethod] to the attribute. var methodSignatureToAttribute = new Dictionary <string, WorkflowMethodAttribute>(); foreach (var interfaceType in workflowType.GetInterfaces()) { foreach (var method in interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { var workflowMethodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); if (workflowMethodAttribute == null) { continue; } var signature = method.ToString(); if (methodSignatureToAttribute.ContainsKey(signature)) { throw new NotSupportedException($"Workflow type [{workflowType.FullName}] cannot implement the [{signature}] method from two different interfaces."); } methodSignatureToAttribute.Add(signature, workflowMethodAttribute); } } // Next, we need to register the workflow methods that implement the // workflow interface. foreach (var method in workflowType.GetMethods()) { if (!methodSignatureToAttribute.TryGetValue(method.ToString(), out var workflowMethodAttribute)) { continue; } var workflowTypeName = TemporalHelper.GetWorkflowTypeName(workflowType, workflowMethodAttribute); lock (nameToWorkflowRegistration) { if (nameToWorkflowRegistration.TryGetValue(workflowTypeName, out var existingRegistration)) { if (!object.ReferenceEquals(existingRegistration.WorkflowType, workflowType)) { throw new InvalidOperationException($"Conflicting workflow interface registration: Workflow interface [{workflowType.FullName}] is already registered for workflow type name [{workflowTypeName}]."); } } else { nameToWorkflowRegistration[workflowTypeName] = new WorkflowRegistration() { WorkflowType = workflowType, WorkflowMethod = method, WorkflowMethodParameterTypes = method.GetParameterTypes(), MethodMap = methodMap }; } } var reply = (WorkflowRegisterReply)await Client.CallProxyAsync( new WorkflowRegisterRequest() { WorkerId = WorkerId, Name = workflowTypeName, }); reply.ThrowOnError(); } }