/// <summary> /// Ensures that the type passed is a valid activity interface. /// </summary> /// <param name="activityInterface">The type being tested.</param> /// <exception cref="ActivityTypeException">Thrown when the interface is not valid.</exception> internal static void ValidateActivityInterface(Type activityInterface) { Covenant.Requires <ArgumentNullException>(activityInterface != null, nameof(activityInterface)); if (!activityInterface.IsInterface) { throw new ActivityTypeException($"[{activityInterface.FullName}] is not an interface."); } if (!activityInterface.Implements <IActivity>()) { throw new ActivityTypeException($"[{activityInterface.FullName}] does not implement [{typeof(IActivity).FullName}]."); } if (activityInterface.IsGenericType) { throw new ActivityTypeException($"[{activityInterface.FullName}] has generic type parameters. Activity interfaces cannot be generic."); } if (!activityInterface.IsPublic && !activityInterface.IsNestedPublic) { throw new ActivityTypeException($"Activity interface [{activityInterface.FullName}] is not public."); } if (activityInterface.GetCustomAttribute <WorkflowInterfaceAttribute>() != null) { throw new WorkflowTypeException($"Workflow interface [{activityInterface.FullName}] cannot be tagged with [WorkflowInterface] because it doesn't define a workflow."); } if (activityInterface.GetCustomAttribute <ActivityAttribute>() != null) { throw new WorkflowTypeException($"Activity interface [{activityInterface.FullName}] cannot not be tagged with [Activity] because that is valid only for activity implementation classes."); } // Validate the activity methods. var activityNames = new HashSet <string>(); foreach (var method in activityInterface.GetMethods()) { var activityMethodAttribute = method.GetCustomAttribute <ActivityMethodAttribute>(); if (activityMethodAttribute == null) { continue; } if (!CadenceHelper.IsTask(method.ReturnType)) { throw new WorkflowTypeException($"Activity interface method [{activityInterface.FullName}.{method.Name}()] must return a Task."); } var name = activityMethodAttribute.Name ?? string.Empty; if (activityNames.Contains(name)) { throw new ActivityTypeException($"Multiple [{activityInterface.FullName}] activity methods are tagged by [ActivityMethod(Name = \"{name}\")]."); } activityNames.Add(name); } if (activityNames.Count == 0) { throw new ActivityTypeException($"Activity interface [{activityInterface.FullName}] does not define any methods tagged with [ActivityMethod]."); } }
/// <summary> /// Ensures that the type passed is a valid workflow interface. /// </summary> /// <param name="workflowInterface">The type being tested.</param> /// <exception cref="ActivityTypeException">Thrown when the interface is not valid.</exception> internal static void ValidateWorkflowInterface(Type workflowInterface) { Covenant.Requires <ArgumentNullException>(workflowInterface != null, nameof(workflowInterface)); if (!workflowInterface.IsInterface) { throw new WorkflowTypeException($"[{workflowInterface.FullName}] is not an interface."); } if (!workflowInterface.Implements <IWorkflow>()) { throw new WorkflowTypeException($"[{workflowInterface.FullName}] does not implement [{typeof(IWorkflow).FullName}]."); } if (workflowInterface.IsGenericType) { throw new WorkflowTypeException($"[{workflowInterface.FullName}] has generic type parameters. Workflow interfaces cannot be generic."); } if (!workflowInterface.IsPublic && !workflowInterface.IsNestedPublic) { throw new WorkflowTypeException($"Workflow interface [{workflowInterface.FullName}] is not public."); } if (workflowInterface.GetCustomAttribute <ActivityInterfaceAttribute>() != null) { throw new WorkflowTypeException($"Workflow interface [{workflowInterface.FullName}] cannot be tagged with [ActivityInterface] because it doesn't define an activity."); } if (workflowInterface.GetCustomAttribute <WorkflowAttribute>() != null) { throw new WorkflowTypeException($"Workflow interface [{workflowInterface.FullName}] cannot not be tagged with [Workflow] because that is valid only for activity implementation classes."); } // Validate the entrypoint method names and result types. var workflowNames = new HashSet <string>(); foreach (var method in workflowInterface.GetMethods()) { var workflowMethodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); if (workflowMethodAttribute == null) { continue; } if (!CadenceHelper.IsTask(method.ReturnType)) { throw new WorkflowTypeException($"Workflow interface method [{workflowInterface.FullName}.{method.Name}()] must return a Task."); } var name = workflowMethodAttribute.Name ?? string.Empty; if (workflowNames.Contains(name)) { throw new WorkflowTypeException($"Multiple workflow methods are tagged by [WorkflowMethod(Name = \"{name}\")]."); } workflowNames.Add(name); } if (workflowNames.Count == 0) { throw new ActivityTypeException($"Workflow interface [{workflowInterface.FullName}] does not define any methods tagged with [WorkflowMethod]."); } // Validate the signal method names and return types. var signalNames = new HashSet <string>(); foreach (var method in workflowInterface.GetMethods()) { var signalMethodAttribute = method.GetCustomAttribute <SignalMethodAttribute>(); if (signalMethodAttribute == null) { continue; } if (!CadenceHelper.IsTask(method.ReturnType)) { throw new WorkflowTypeException($"Workflow interface method [{workflowInterface.FullName}.{method.Name}()] must return a Task."); } var name = signalMethodAttribute.Name ?? string.Empty; if (signalNames.Contains(name)) { throw new WorkflowTypeException($"Multiple signal methods are tagged by [SignalMethod(name:\"{name}\")]."); } signalNames.Add(name); } // Validate the signal method names and return types. var queryNames = new HashSet <string>(); foreach (var method in workflowInterface.GetMethods()) { var queryMethodAttribute = method.GetCustomAttribute <QueryMethodAttribute>(); if (queryMethodAttribute == null) { continue; } if (!CadenceHelper.IsTask(method.ReturnType)) { throw new WorkflowTypeException($"Workflow interface method [{workflowInterface.FullName}.{method.Name}()] must return a Task."); } var name = queryMethodAttribute.Name ?? string.Empty; if (queryNames.Contains(name)) { throw new WorkflowTypeException($"Multiple query methods are tagged by [QueryMethod(name:\"{name}\")]."); } queryNames.Add(name); } }