/// <summary> /// Creates a local activity stub instance suitable for executing a non-local activity. /// </summary> /// <param name="client">The associated <see cref="TemporalClient"/>.</param> /// <param name="workflow">The parent workflow.</param> /// <param name="activityType">The activity implementation type.</param> /// <param name="options">Specifies the <see cref="LocalActivityOptions"/> or <c>null</c>.</param> /// <returns>The activity stub as an <see cref="object"/>.</returns> public object CreateLocal(TemporalClient client, Workflow workflow, Type activityType, LocalActivityOptions options) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); Covenant.Requires <ArgumentNullException>(workflow != null, nameof(workflow)); Covenant.Requires <ArgumentNullException>(activityType != null, nameof(activityType)); options = options ?? new LocalActivityOptions(); return(localConstructor.Invoke(new object[] { client, client.DataConverter, workflow, activityType, options, TemporalHelper.GetActivityInterface(activityType) })); }
/// <summary> /// Registers an activity implementation with the temporal-proxy. /// </summary> /// <param name="activityType">The activity implementation type.</param> /// <exception cref="RegistrationException">Thrown when there's a problem with the registration.</exception> private async Task RegisterActivityImplementationAsync(Type activityType) { await SyncContext.Clear; TemporalHelper.ValidateActivityImplementation(activityType); var interfaceType = TemporalHelper.GetActivityInterface(activityType); TemporalHelper.ValidateActivityInterface(interfaceType); // We need to register each activity method that implements an activity interface method // with the same signature that that was tagged by [ActivityMethod]. // // First, we'll create a dictionary that maps method signatures from any inherited // interfaces that are tagged by [ActivityMethod] to the attribute. var methodSignatureToAttribute = new Dictionary <string, ActivityMethodAttribute>(); foreach (var method in interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { var activityMethodAttribute = method.GetCustomAttribute <ActivityMethodAttribute>(); if (activityMethodAttribute == null) { continue; } var signature = method.ToString(); if (methodSignatureToAttribute.ContainsKey(signature)) { throw new NotSupportedException($"Activity type [{activityType.FullName}] cannot implement the [{signature}] method from two different interfaces."); } methodSignatureToAttribute.Add(signature, activityMethodAttribute); } // Next, we need to register the activity methods that implement the // activity interface. foreach (var method in activityType.GetMethods()) { if (!methodSignatureToAttribute.TryGetValue(method.ToString(), out var activityMethodAttribute)) { continue; } var activityTarget = TemporalHelper.GetActivityTarget(interfaceType, activityMethodAttribute.Name); lock (nameToActivityRegistration) { if (nameToActivityRegistration.TryGetValue(activityTarget.ActivityTypeName, out var registration)) { if (!object.ReferenceEquals(registration.ActivityType, registration.ActivityType)) { throw new InvalidOperationException($"Conflicting activity type registration: Activity type [{activityType.FullName}] is already registered for activity type name [{activityTarget.ActivityTypeName}]."); } } else { nameToActivityRegistration[activityTarget.ActivityTypeName] = new ActivityRegistration() { ActivityType = activityType, ActivityMethod = method, ActivityMethodParameterTypes = method.GetParameterTypes() }; } } var reply = (ActivityRegisterReply)await Client.CallProxyAsync( new ActivityRegisterRequest() { WorkerId = WorkerId, Name = activityTarget.ActivityTypeName, }); reply.ThrowOnError(); } }