2

I have the same issue as this question on MSDN, but I don't understand the solution because it is still not clear to me if Roman Kiss's solution will correctly replace an endpoint address while a single workflow instance being executed concurrently.

When internal Send activity is scheduled for execution by one thread with certain enpoint address, wouldn't this address be overridden by another thread that schedules same activity with different endpoint address? Correct me if I am mistaken, but I assume it would, because Send.Endpoint is a regular property as oppose to being InArgument<Endpoint> bound to whatever current workflow execution context is.

Can someone shed more light onto this?

UPDATE

I tested the solution provided by Roman Kiss, and it turns out that it is not working as expected in my scenario. I modified Execute method as follows:

protected override void Execute(NativeActivityContext context)
{
    Thread.Sleep(Address.Get(context).EndsWith("1") ? 1000 : 0);

    Body.Endpoint.Binding = GetBinding(Binding.Get(context));
    Body.Endpoint.AddressUri = new Uri(Address.Get(context));

    Thread.Sleep(Address.Get(context).EndsWith("1") ? 0 : 3000);
    var address = Address.Get(context) + " => " + Body.Endpoint.AddressUri;
    Console.WriteLine(address);
    Thread.Sleep(10000); 

    context.ScheduleActivity(Body);
}

Ran this test:

static void Main(string[] args)
{
    // Workflow1 is just a SendScope wrapped around by a Sequence with single Address input argument exposed
    var workflow = new Workflow1();
    Task.WaitAll(
        Task.Run(() => WorkflowInvoker.Invoke(workflow, new Dictionary<string, object> { { "Address", @"http://localhost/1" } })),
        Task.Run(() => WorkflowInvoker.Invoke(workflow, new Dictionary<string, object> { { "Address", @"http://localhost/2" } })));

    Console.ReadLine();
}

The result I am getting is:

http://localhost/1 => http://localhost/1

http://localhost/2 => http://localhost/1

The question remains open: how do I assign endpoint address of my Send activity dynamically at runtime?

Yuriy Magurdumov
  • 235
  • 2
  • 12

1 Answers1

1

This will work as shown because a new Send activity is created by the factory and so when using the CacheMetadata method to setup that Send activity it is setting the binding properly on that instance of the activity.

Including Content Incase Link Dies

[ContentProperty("Body")]
public class SendScope : NativeActivity
{
    [DefaultValue((string)null)]
    [RequiredArgument]
    public InArgument<string> Binding { get; set; }

    [DefaultValue((string)null)]
    [RequiredArgument]
    public InArgument<string> Address { get; set; }

    [Browsable(false)]
    public Send Body { get; set; }

    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        if (this.Body == null || this.Body.EndpointAddress != null)
        {
            metadata.AddValidationError("Error ...");
            return;
        }
        this.Body.Endpoint = new Endpoint()
        {
            AddressUri = new Uri("http://localhost/"),
            Binding = new BasicHttpBinding(),
            ServiceContractName = this.Body.ServiceContractName
        };
        metadata.AddChild(this.Body);
        base.CacheMetadata(metadata);
    }

    protected override void Execute(NativeActivityContext context)
    {
        this.Body.Endpoint.Binding = GetBinding(this.Binding.Get(context));
        this.Body.Endpoint.AddressUri = new Uri(this.Address.Get(context));
        context.ScheduleActivity(Body);
    }


    private System.ServiceModel.Channels.Binding GetBinding(string binding)
    {
        if (binding == "basicHttpBinding")
            return new BasicHttpBinding();
        //else ... others bindings
        return null;
    }
}

public class SendScopeFactory : IActivityTemplateFactory
{
    public Activity Create(DependencyObject target)
    {
        return new SendScope()
        {
            DisplayName = "SendScope",
            Body = new Send()
            {
                Action = "*",
                OperationName = "ProcessMessage",
                ServiceContractName = "IGenericContract",
            }
        };
    }
}
  1. Create a custom native activity for setting Send.Endpoint property during the runtime based on your properties such as Binding, Address, Security, etc.
  2. Create designer for this SendScope activity something simular like CorrelationScope
  3. Create SendScopeFactory - see the above code snippet.
Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
  • I am executing a single instance of Workflow like this: `Activity workflow1 = new Workflow1(); Task.WaitAll( Task.Run(() => WorkflowInvoker.Invoke(workflow1)), Task.Run(() => WorkflowInvoker.Invoke(workflow1)));` Though a breakpoint in `CacheMetadata` hits only once. – Yuriy Magurdumov Sep 12 '12 at 13:56