locked
Resolve endpoint bindings dynamically in a workflow RRS feed

  • Question

  • I try to create an activity which executes following activities :
    - calling a rule via RuleEngine to resolve an endpoint (binding, uri, security...)
    - configuring send activity with informations resolved in the previous rule
    - receive the service response

    All is ok when my Send activity binding is static (ie : by design I put basicHttpBinding in Send.Endpoint.Binding) and I succeed in resolving EndpointAddress dynamically (because EndpointAddress type is InArgument<Uri>). But my problem is how to configure the endpoint binding dynamically (Endpoint property isn't of type InArgument<Endpoint>) ?

    For example, if my rule returns the string "wsHttpBinding", i want to set my Send activity Endpoint.Binding property to wsHttpBinding.

    My configuration : WF 4.0 , Visual Studio 2010 RC

    thanx for your help !


    • Moved by Andrew_Zhu Monday, March 15, 2010 8:20 AM (From:Windows Workflow Foundation)
    Monday, March 8, 2010 4:49 PM

Answers

  • Tien1976,

    - try the following:

    1. Create a custom native activity for setting Send.Endpoint property during the runtime based on your properties such as Binding, Address, Security, etc. 

       [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",
                    }
                };
            }
        }

    2. Create designer for this SendScope activity something simular likeCorrelationScope

    3. Create SendScopeFactory - see the above code snippet.

     
    Thanks

    Roman

     

     


    Roman Kiss, MVP Connected System Developer
    • Edited by Roman Kiss Thursday, April 15, 2010 6:51 AM editing code
    • Marked as answer by Tien1976 Thursday, April 22, 2010 11:56 AM
    Thursday, April 15, 2010 6:49 AM

All replies

  • You can use variables to set the value of the send activity end point based upon your scenario
    Monday, March 8, 2010 7:42 PM
  • You can use variables to set the value of the send activity end point based upon your scenario

    As I said before, the Endpoint property of the Send activity isn't an InArgument<Endpoint> property but a simple Endpoint type property.

    In this case, I don't know how to set my Endpoint property dynamically because I have no context to resolve my variable containing binding informations.
    Tuesday, March 9, 2010 9:14 AM
  • Nobody tried to resolved endpoint configuration from the message or from other parameters ?

    By this way, i could create a "biztalk dynamic sendport like" activity.

    Any idea ??
    Thursday, March 11, 2010 12:15 PM
  • An alternate approach could be that rather than setting endpoint configuration dynamically you can create add multiple send activities and control it through switch activity.. This way based upon your switch criteria one of your send activity will be invoked.

    hope this helps
    Thursday, March 11, 2010 12:30 PM
  • In fact, it's my backup solution but it's not very extensible. And security configuration can't be set dynamically...

    Perhaps by using EndpointConfigurationName, I don't know if it is possible to dynamically create temp configuration or to implement my own EndpointConfiguration "provider" ?
    Thursday, March 11, 2010 1:19 PM
  • you can also create a custom send activity by encapsulating send activity or custom WCF calling code. In this you will have complete command to do whatever you want to achieve.

    Thursday, March 11, 2010 4:17 PM
  • I am working on this subject since few days (not to say weeks) and I do not have find a solution yet.

    I am using the VS2010 RC1Rel.

    I am currently trying to use the Send activity with an EndPointAdress that could be set up dynamically. Unfortunatly this is not working at all.

    I have a ActivityLibrary project with a service reference in the project.

    • When you do so VS generate a set of activities, one per web service operation available.
    • Each generated activity is a Send, ReceiveReply and Assign Sequence. 
    • I am using one of this generated activity in a Xaml activity which encapsulate the generated activity in a TryCatch activity.  

    I have then a Workflowlibrary project using the library

    • In this project I use the above xaml activity (calling the web service) in a workflow which print the result of the call.
    • The workflow is executed through the default workflow engine provided in the MS examples.

    By default the generated activity does not defined any endpoint or URI (actually when you open the xaml there is a red dot), however as long as the endpoint is properly defined in the app.config of the workflow engine it works (using basicHttpBinding).

    The first test I have done is the following :

    • in my activity library, I copy the generated sequence directly in my xaml activity
    • I then change the variable required so the Send, ReceiveReply and Assign are properly set up.
    • In the send activity, I set up an endPoint with the basicHttpBinding and a EndPointAdress as New URI("https://myuri")

    The library was then build with no error , also the workflowlibrary, but when I execute the workflow i get this nice error :

    A first chance exception of type 'System.Xaml.XamlObjectWriterException' occurred in System.Xaml.dll

    The second test, I have done is the following :

    • I directly change the generated code for the Send Activity
    • In the properties I set up EndPoint to Endpoint value.
    • I set up Binding to basicHttpBinding
    • I set up the EndPointAddres as New URI("https://myuri")

    The library could then be build with no error as previously, but when you run the workflow you get this other nice error :

    A first chance exception of type 'System.Activities.InvalidWorkflowException' occurred in System.Activities.dll

    Note that using a variable or argument instead of the direct statement in the EndPointAdress does not change the exception raised. Exception is raised by the Send activity (as I have removed all other activities for finding the problem origin).

    So if anybody has already solve this issue and have a practical example with a dynamic URI for the EndPointAdress please help. We will be grateful !!

    Tuesday, April 13, 2010 8:56 AM
  • Tien1976,

    - try the following:

    1. Create a custom native activity for setting Send.Endpoint property during the runtime based on your properties such as Binding, Address, Security, etc. 

       [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",
                    }
                };
            }
        }

    2. Create designer for this SendScope activity something simular likeCorrelationScope

    3. Create SendScopeFactory - see the above code snippet.

     
    Thanks

    Roman

     

     


    Roman Kiss, MVP Connected System Developer
    • Edited by Roman Kiss Thursday, April 15, 2010 6:51 AM editing code
    • Marked as answer by Tien1976 Thursday, April 22, 2010 11:56 AM
    Thursday, April 15, 2010 6:49 AM
  • Thanks a lot Roman. As always you give very good advices !

    As you said, the trick was to set a fake value to AddressUri and Binding in the CacheMetaData method. That's the point i didn't thought about.

    We also appreciate your work on MessageMediationActivity.

    Thursday, April 22, 2010 11:59 AM
  • I wonder, what will happen if more than one threads execute a single instance of workflow with different endpoint addresses? I suppose the Send body activity would be shared in this case? Wouldn't one thread override endpoint settings set by another thread while both are running concurrently?
    Correct me if I'm wrong.
    Monday, September 10, 2012 3:43 PM
  • Hi Roman,

    so i tried this solution, and it worked great in assigning the address once.  But let's say i have a list of addresses, and I want to do a Foreach address in my list, call this send, it sends all the messages to the first address on the queue, even though i clearly see in the Execute method the Address is being set to the new Address.  Any advice on how to get this to work in a loop, or are we stuck with the first address it is dynamically set to always?

    Thanks,

    Mike

    Wednesday, February 6, 2013 11:12 PM