We have an existing WCF service which uses several DataContracts. We want to modify the serialization based on the device, so that when accessed from mobile devices, the ser
There is a approach, but I think this will require extra DataContract to be generated but still no need for separate operation and data contracts for different types of devices. It can classic implementation to run-time polymorphism. I am just giving idea:
Say you have a generic DataContract like :
[DataContract]
[KnownType(typeof(Extra))]
[KnownType(typeof(Extra2))]
public class TokenMessage
{
string tokenValue;
string extraValue;
[DataMember]
public string Token
{
get { return tokenValue; }
set { tokenValue = value; }
}
}
Other device specific contracts can inherit TokenMessage as base class like:
[DataContract]
public class Extra:TokenMessage
{
[DataMember]
public string Extra
{
get ;set;
}
}
[DataContract]
public class Extra2:TokenMessage
{
[DataMember]
public string Extra2
{
get ;set;
}
}
Now at run-time as you say you know an argument in the operation contract, which helps us identify the device. Say based on device type, you can instantiate base class with derived class like:
TokenMessage tm= new Extra();
OR
TokenMessage tm= new Extra2();
So at run-time you will decide which device contract will be part of genric response.
Note: Adding KnownType
will generate the separate xsd within wsdl for all known types within base class, but saves serialization for data at run-time as this should depend on actual inheritance chosen.
In your model add a property 'ShouldSerializeYOUR_PROPERTY_NAME', set it to false when you do not want the property serialized. See more here: http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.shouldserializeproperty(v=vs.110).aspx
I'm not convinced that some variant of @Pranav Singh's answer isn't a better design, but that's not your question...
As you mentioned in a comments attributes in .NET are static by design. This means dynamically adding/removing [DataMember]
isn't a good option. It is possible. There are options like using Reflection.Emit to recreate the instance with the meta data changes (see all the answers to Can attributes be added dynamically in C#?) but all of those routes are complicated.
I see two reasonable options:
1) Implement an IParameterInspector for the service. In the AfterCall() method you could inspect and alter the parameters being returned to the client before they are serialized. There is some work to use reflection to dynamically determine the parameter types and set their values, but its not complicated. This is the better design that enables reuse of the behavior across many contracts or services. Carlos Figueira's blog is the best source for WCF extension examples.
2) Use the [OnSerializing] and [OnSerialized] events. In the [DataContract] you could temporarily alter what the properties are returning during serialization. The events are actually designed to enable initialization and as such this solution is a bit of a hack. This solution is also not thread safe. But it does keep the code contained to the DataContract class and solves the problem quickly (and I think you are looking for quick).
Solution #2 mights look something like:
[DataContract]
public class TokenMessage
{
string tokenValue;
string extraValue;
bool enableExtraValue = true;
[DataMember]
public string Extra
{
get {
if (enableExtraValue)
return extraValue;
return null;
}
set { extraValue = value; }
}
[OnSerializing()]
internal void OnSerializingMethod(StreamingContext context)
{
enableExtraValue = false;
}
[OnSerialized()]
internal void OnSerializedMethod(StreamingContext context)
{
enableExtraValue = true;
}
}
Solution #2 is a quick fix (which is what I think you are looking for).
Solution #1 is the better design.
Try using IgnoreDataMemberAttribute