100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET Framework

Data Contracts

How WCF uses [DataContract] and [DataMember] to define the exact wire shape of types exchanged between services and clients.

FoundationsIntermediate9 min readJul 10, 2026
Analogies

What Is a Data Contract?

A data contract defines the precise shape of the data exchanged between a WCF service and its clients, independent of how the type is implemented internally in .NET. A class or struct marked with [DataContract] and individual members marked with [DataMember] are explicitly opted into serialization by the DataContractSerializer; any field or property without [DataMember] is not included on the wire, even if it is public. This explicit, opt-in model contrasts with plain XML serialization, where public members are included by default, and it lets you keep internal-only properties (like cached computed values or backing fields) out of the serialized payload without extra attributes to exclude them.

🏏

Cricket analogy: A data contract is like an official scorecard template — only fields explicitly printed on it, runs, wickets, overs, are transmitted to the scoreboard, even though the scorer's private notebook might track dozens of other observations.

Serialization Rules and Versioning

By default, [DataMember] properties are serialized using the member's declared name, but you can override this with the Name property, control ordering with Order, and control whether a value is mandatory with IsRequired. The DataContractSerializer is tolerant of forward and backward compatible changes by default: adding a new [DataMember] with IsRequired = false to an existing data contract does not break older clients or services that don't know about the new field, since unknown or missing members are simply ignored or left at their default value. This round-trip tolerance is a deliberate design choice that makes DataContractSerializer more version-resilient than the older XmlSerializer for evolving service payloads over time.

🏏

Cricket analogy: Adding an optional new field like IsRequired=false to a data contract is like the ICC introducing an optional 'Impact Player' rule in some T20 leagues — older tournaments that don't recognize the rule simply continue playing without it, unaffected.

Known Types and Inheritance

When a data contract member's declared type is a base class or interface but the actual object passed at runtime is a derived type, WCF needs to know about that derived type in advance to serialize and deserialize it correctly; this is done with the [KnownType] attribute on the base class, or by registering known types via configuration or a KnownTypeAttribute callback method for dynamic scenarios. Without a known type declaration, attempting to send a derived object where only the base type is registered throws a SerializationException at runtime, typically with a message indicating the type was not expected and cannot be processed. Collections, enums, and nullable types are supported natively by DataContractSerializer without additional attributes, but custom generic types often require explicit [CollectionDataContract] handling for predictable wire shapes.

🏏

Cricket analogy: Declaring [KnownType] is like a tournament pre-registering which reserve players might substitute mid-series — if an uncalled-up player suddenly appears on the scorecard, officials reject the entry because he wasn't on the declared list.

csharp
[DataContract(Namespace = "http://mycompany.com/services/2026/07")]
[KnownType(typeof(DigitalProduct))]
public class Product
{
    [DataMember(Order = 1, IsRequired = true)]
    public int Id { get; set; }

    [DataMember(Order = 2)]
    public string Name { get; set; }

    [DataMember(Order = 3, IsRequired = false)]
    public decimal? DiscountPrice { get; set; } // added later, optional

    // Not serialized -- no [DataMember]
    public string InternalSku { get; private get; set; }
}

[DataContract]
public class DigitalProduct : Product
{
    [DataMember(Order = 4)]
    public string DownloadUrl { get; set; }
}

DataContractSerializer serializes members in the order specified by DataMember.Order (ascending), falling back to declaration order if Order is unset. Explicitly setting Order is a good habit whenever a contract may be inherited or refactored later, so wire shape doesn't shift unexpectedly.

Passing a derived object through a member typed as its base class without declaring [KnownType] on the base (or registering it another way) will fail at runtime with a SerializationException, even though the code compiles fine — this is a common production surprise when polymorphic data is introduced after the fact.

  • [DataContract] and [DataMember] explicitly opt individual types and members into WCF serialization.
  • Members without [DataMember] are excluded from the wire payload even if public.
  • DataMember supports Name, Order, and IsRequired to control the exact wire shape.
  • DataContractSerializer tolerates adding new optional members without breaking older clients or services.
  • [KnownType] must be declared for base types whose actual runtime value may be a derived type.
  • Missing a required [KnownType] declaration causes a runtime SerializationException, not a compile error.
  • Collections and enums serialize natively; complex generics may need [CollectionDataContract] for predictable shape.

Practice what you learned

Was this page helpful?

Topics covered

#NETFramework#WCFWindowsCommunicationFoundationStudyNotes#MicrosoftTechnologies#DataContracts#Data#Contracts#Contract#Serialization#StudyNotes#SkillVeris