Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Conditional WCF DataContract Serialization (Using DataContractSurrogate)

5.00/5 (1 vote)
11 Jul 2012CPOL1 min read 49.6K   553  
How to add conditional DataContract Serialization in a declarative manner.

Introduction

During the development of Web Services for our system, we wanted to design a common service layer, and common data contracts which would cater to requirements of different role based users. User role distinction was important, because there were some sensitive data points which could be shared only with privileged users. One way to design this was to make the data translators role specific. The translators could ascertain the user role, and serialize the data accordingly. The approach was simple but not elegant, and would have introduced lot of additional role checks in various translators.  

An alternative was to keep the service layer and translators role agnostic, and use a more declarative approach by using Data Contract Surrogates. Data Contract Surrogate is an advanced feature which can be used to introduce special behavior during Data Contracts Serialization\Deserialization.

Implementation

Let's start by defining an attribute ConditionalDataMemberAttribute. This attribute will be used to decorate DataMembers which need to be conditionally serialized. Usage of this attribute is discussed later. 

C#
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class ConditionalDataMemberAttribute : Attribute

Next step is to create a custom Service Behavior called ConditionalDataBehavior.

C#
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConditionalDataBehavior : Attribute, IServiceBehavior

In service behavior ConditionalDataBehavior,we override the DataContractSurrogate for each operation with our custom implementation of Data Contract Surrogate   ConditionalDataContractSurrogate

C#
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
  foreach (ServiceEndpoint ep in serviceHostBase.Description.Endpoints) {
    foreach (OperationDescription od in ep.Contract.Operations) {
      ApplyDataContractSurrogate(od);
    }
  }
}

private static void ApplyDataContractSurrogate(OperationDescription description)
{
  var dcsOperationBehavior = description.Behaviors.Find<datacontractserializeroperationbehavior>();
  if (dcsOperationBehavior != null) {
    dcsOperationBehavior.DataContractSurrogate = new ConditionalDataContractSurrogate(dcsOperationBehavior.DataContractSurrogate);
  }
}

The class ConditionalDataContractSurrogate implements the Interface IDataContractSurrogate. We add our custom implementation to the method GetObjectToSerialize. During serialization this method is called on every object that needs to be serialized. This gives an opportunity to inspect each object before serialization. If the calling context is not authorized (checked inside IsAuthorized) to receive the object, then  we prevent the actual value from being serialized.

C#
public object GetObjectToSerialize(object obj, Type targetType)
{
  if (obj == null) return null;

  var type = obj.GetType();
  type.GetProperties().ToList()
    .ForEach(prop => {
      try {
        var attr = prop.GetCustomAttributes(typeof(ConditionalDataMemberAttribute), false);
        if (attr.Any()) {
          var role = ((ConditionalDataMemberAttribute)attr[0]).Role;
          //Is the user authorized
          if (!IsAuthorized(role)) {
            var proptype = prop.PropertyType;
            //Set the property value to its default value
            prop.GetSetMethod().Invoke(obj,
                                       new[] { proptype.IsValueType ? Activator.CreateInstance(proptype) : null });
          }
        }
      } catch { }
    });

  return _baseSerializer != null ? _baseSerializer.GetObjectToSerialize(obj, targetType) : obj;
}

Using the code  

Step 1:  Decorate your webservice with ConditionalDataBehavior attribute.   

C#
[ServiceBehavior(Namespace = "http://mywebservice.com/v1")]
[ConditionalDataBehavior]
public class MyWebService

Step 2:  Decorate the Data Contracts with ConditionalDataMember attribute.

C#
[DataContract]
public class UserInformation
{
    [DataMember]
    public string FirstName { get; set; }
    [DataMember]
    public string LastName { get; set; }
    [DataMember]
    [ConditionalDataMember(Role = "Level2")]
    public string Email { get; set; }
    [DataMember]
    [ConditionalDataMember(Role = "Level2")]
    public string Phone { get; set; }
}

Step 3: Add your own implementation of IsAuthroized function.

C#
private bool IsAuthorized(string role)
{
  //Implement your own Authorization check
  // currentUser.Roles.HasDesiredRole
  return true;
}

Example

C#
//Object Returned by Translator
new UserInformation {
    FirstName = "Joe", 
    LastName = "Doe", 
    Email = "joe@doe.com", 
    PhoneNumber = "408000000"
  };
XML
<!-- Data values received by Level 2 user -->
<userinfo> 
    <firstname>Joe</firstname>
    <lastname>Doe</lastname>
    <email>joe@doe.com</email>
    <phone>408000000</phone>
</userinfo> 
<!-- Data values received by Level 3 user -->
<userinfo>
    <firstname>Joe</firstname>
    <lastname>Doe</lastname>
    <email></email>
    <phone></phone>
</userinfo>

References

Data Contract Surrogate - http://msdn.microsoft.com/en-us/library/ms733064.aspx.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)