Search

Locations of visitors to this page

Categories

On this page

Setting UserName ClientCredentials in WF SendActivity
DataContract Serializer and IsReference property
ConfigurationErrorsException: This element is not currently associated with any context

Archive

Blogroll

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 50
This Year: 6
This Month: 1
This Week: 1
Comments: 69

Sign In

 Monday, June 02, 2008
Monday, June 02, 2008 10:35:06 AM (GMT Standard Time, UTC+00:00) ( WF )

I was recently asked if there is a way to set custom UserName credentials in a SendActivity. I did some investigation and couldn’t find any OOB way do achieve this. So the next step was to look into WCF’s awesome extensibility mechanism **Bahaviors**. Turns out you can achieve this functionality quite easily by writing a custom endpoint behavior.

Step 1 would be to create a custom endpoint behavior:

public class UserNameBehavior : IEndpointBehavior

{

    #region IEndpointBehavior Members

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

    {}

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)

    {

        var cc = endpoint.Behaviors.Find<ClientCredentials>();

        cc.UserName.UserName = "zahmed";

        cc.UserName.Password = "password";

    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)

    {}

    public void Validate(ServiceEndpoint endpoint)

    {}

    #endregion

}

In the ApplyClientBehavior method, find the appropriate behavior object and override its properties to your desired values. Now you have to specify this behavior in the endpoint configuration of the SendActivity.

To use your custom behavior from config you have to create a BehaviorElementExtension(Step 2)

    public class UserNameBehaviorElement : BehaviorExtensionElement

    {

        protected override object CreateBehavior()

        {

            return new UserNameBehavior();

        }

        public override Type BehaviorType

        {

            get { return typeof(UserNameBehavior); }

        }

    }

Step 3 would be to register your behavior as an extension in the config file:

<extensions>

  <behaviorExtensions>

    <add name="userName" type="WorkflowConsoleApplication1.UserNameBehaviorElement, WorkflowConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

  </behaviorExtensions>

</extensions>

At this point your custom behavior is ready to be used in the endpoint configuration used by the SendActivity. Here is the complete config file.

<configuration>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicHttpBinding ICalcultor">

          <security mode="Transport">

            <transport clientCredentialType="Basic" />

          </security>

        </binding>

      </basicHttpBinding>

    </bindings>

    <client>

      <endpoint address="https://localhost:7000/Calc" binding="basicHttpBinding"

          bindingConfiguration="BasicHttpBinding ICalcultor" contract="ServiceReference1.ICalcultor"

          name="BasicHttpBinding ICalcultor" behaviorConfiguration="userNameBehavior" />

    </client>

    <behaviors>

      <endpointBehaviors>

        <behavior name="userNameBehavior">

          <userName/>

        </behavior>

      </endpointBehaviors>

    </behaviors>

    <extensions>

      <behaviorExtensions>

        <add name="userName" type="WorkflowConsoleApplication1.UserNameBehaviorElement, WorkflowConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

      </behaviorExtensions>

    </extensions>

  </system.serviceModel>

</configuration>

Comments [0] | | # 
 Tuesday, May 20, 2008
Tuesday, May 20, 2008 1:19:16 PM (GMT Standard Time, UTC+00:00) ( )

In .net Framework 3.5 SP1, DataContractSerializer supports by-ref object graph serialization by using the standard xsd:ID/xsd:IDREF attributes.

You can set the IsReference=true on your DataContract definition and serializer will generate XML elements with IDs/IDREFs attributes and will link them together rather embedding them inside each other(default behavior).

Also if you examine the XSD generated by WCF as part of the metadata export, it will also contain the standard ID/IDREF xml schema attributes. Because of this, xml can be correctly parsed and understood by any framework in a standard way.

This change will enable serialization of object graphs having circular references (which wasn’t possible previously – at least not without writing custom code) and will also reduce the size of the serialized xml.

Let’s examine this change using the following DataContract definition:

    [DataContract]

    public class Employee

    {

        [DataMember]

        public string Name { get; set; }

        [DataMember]

        public Employee Manager { get; set; }

    }

    [DataContract]

    public class Department

    {

        [DataMember]

        public List<Employee> Staff { get; set; }

        [DataMember]

        public string DeptName { get; set; }

    }

Now if we serialize following Department object using DataContractSerializer

        var kenny = new Employee() { Name = "Kenny" };

        var bob = new Employee() { Name = "Bob", Manager = kenny };

        var alice = new Employee() { Name = "Alice", Manager = kenny };

        var ahmed = new Employee() { Name = "Ahmed", Manager = kenny };

 

        var dept = new Department() { DeptName = "RandD", Staff = new List<Employee>() { kenny, bob, alice, ahmed } };       

        DataContractSerializer dcs = new DataContractSerializer(typeof(Department));       

        var ms = new MemoryStream();

        dcs.WriteObject(ms, dept);

        ms.Seek(0, SeekOrigin.Begin);

       

        var sr = new StreamReader(ms);

        var xml = sr.ReadToEnd();

We will get this xml.

<Department xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication2" xmlns:i="http://www.w3.org/2001/XMLSchemainstance">

      <DeptName>RandD</DeptName>

      <Staff>

            <Employee>

                  <Manager i:nil="true" />

                  <Name>Kenny</Name>

            </Employee>

            <Employee>

                  <Manager>

                        <Manager i:nil="true" />

                        <Name>Kenny</Name>

                  </Manager>

                  <Name>Bob</Name>

            </Employee>

            <Employee>

                  <Manager>

                        <Manager i:nil="true" />

                        <Name>Kenny</Name>

                  </Manager>

                  <Name>Alice</Name>

            </Employee>

            <Employee>

                  <Manager>

                        <Manager i:nil="true" />

                        <Name>Kenny</Name>

                  </Manager>

                  <Name>Ahmed</Name>

            </Employee>

      </Staff>

</Department>

You can see manager Kenny is included in all Employee objects, essentially a by-value inclusion.  Now if we change the declaration of Employee class to following:

    [DataContract(IsReference = true)]

    public class Employee

    {

        [DataMember]

        public string Name { get; set; }

        [DataMember]

        public Employee Manager { get; set; }

    }

With above change, you will get following different xml.

<Department xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication2" xmlns:i="http://www.w3.org/2001/XMLSchemainstance">

      <DeptName>R&D</DeptName>

      <Staff>

            <Employee z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">

                  <Manager i:nil="true" />

                  <Name>Kenny</Name>

            </Employee>

            <Employee z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">

                  <Manager z:Ref="i1" />

                  <Name>Bob</Name>

            </Employee>

            <Employee z:Id="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">

                  <Manager z:Ref="i1" />

                  <Name>Alice</Name>

            </Employee>

            <Employee z:Id="i4" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">

                  <Manager z:Ref="i1" />

                  <Name>Ahmed</Name>

            </Employee>

      </Staff>

</Department>

In attribute-free (POCO) world:

you can use a different ctor, taking a boolean flag, to toggle by-val/by-ref serialization.

      DataContractSerializer(Type type, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate)

To enable circular references for operation or service scope, you can use custom behaviors etc. Essentially you need the ability to hook into serializer instantiation process and create the instance using above overload:

   1. Subclass DataContractSerializerOperationBehavior

   2. Ovverride CreateSerializer method

   3. Create a new DCS instance passing true to preserveObjectReferences param.

class DataContractSerializerOperationBehaviorEx : DataContractSerializerOperationBehavior

{

public DataContractSerializerOperationBehaviorEx(OperationDescription operation):base(operation)

{

}

public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)

{

return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, true, this.DataContractSurrogate);

}

public override XmlObjectSerializer CreateSerializer(Type type, System.Xml.XmlDictionaryString name, System.Xml.XmlDictionaryString ns, IList<Type> knownTypes)

{

return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, true, this.DataContractSurrogate);

}

}

Comments [0] | | # 
 Sunday, April 06, 2008
Sunday, April 06, 2008 10:49:28 AM (GMT Standard Time, UTC+00:00) ( )

Sometimes when debugging WCF code you might get this strange exception. In normal situations this exception is handled by WCF api and never reaches your code. If however if you have configured VS to report exception as they are thrown, WCF api never gets the chance to deal this exception.

A simple fix is to uncheck the setting for "break when an exception is thrown".

On Debug menu, select Exceptions... & uncheck the circled option.

 

Comments [1] | | #