Search code examples
.netwcfdatacontractserializer

WCF DataContract Serialization of Bidirectional Entities or Circular dependencies


I am implementing a Sample WCF service to understand How DataContractSerializer serializes bidirectional entities. I have the following DataContracts.

[DataContract]
public class Process
{
    [DataMember]
    public string ProcessName { get; set; }
    [DataMember]
    public string Memory { get; set; }
    [DataMember]
    public User UserOfProcess { get; set; }
}

[DataContract]
public class User
{
    [DataMember]
    public string UserID { get; set; }
    [DataMember]
    public string UserName { get; set; }
    [DataMember]
    public List<Process> ProcessesOfUser { get; set; }
}

As you may notice that User have number of Process and every Process in turn has a User associated with it.

  [OperationContract]
  User GetUser();

When I run this method from WCFTestClient, I get an exception and that is understandable because whenever we serialize ProcessOfUser in User it in trun will serialize User again.

To avoid this, I remove the DataMember attribute from UserOfProcess in Process[DataContract]. It works fine.

My question is,
1. Is it the right way to solve this problem,or Is there some other way to this? I also find the [IgnoreDataMember] attribute to avoid its serialization.
2. Can I add or remove the [DataMember] attribute programmatically?


Solution

  • You can use the [IsReference] attribute to tell WCF to resolve circular dependencies.

    However, after using this myself on business objects, I ran into too many complications and ended up using explicit DTOs instead, and effectively doing what you've done and omit the circular reference. I wire up the references again at the client end when the DTO is transformed back to a business object.

    IsReference should be fine for simple object graphs though.

    EDIT: Regarding DTO's

    As I said I originally serialised domain objects across the wire but it got way too messy in the end.

    Firstly because I also serialise those same objects for persistence, so I ended up with conflicting serialisation needs, but mainly because DataContractSerializer does not call constructors so you have no guarantee that your object is valid. I ended up having large [OnDeserialized] methods to make sure members weren't null etc. Doing this on collection members caused MASSIVE headaches due to the way WCF serialises object graphs.

    Using DTO's makes it all go away and exposes a really clean object showing exactly what's going over the service. You get a DTO and then can do your mapping knowing everything is in place and not in some wierd semi-serialised state. If your domain objects are really simple you might get away with it, but I personally wouldn't recommend it after the pain is caused me.

    The DTO's are stupidly simple:

    [DataContract]
    public class UserProcessesDTO
    {
        [DataMember] public string       UserID          { get; set; }
        [DataMember] public string       UserName        { get; set; }
        [DataMember] public ProcessDTO[] Processes       { get; set; }
    }
    
    [DataContract]
    public class ProcessDTO
    {
        [DataMember] public string       ProcessName     { get; set; }
        [DataMember] public string       Memory          { get; set; }
    }
    

    All my collections now go over the wire as simple arrays. Dictionaries etc in business objects get cleanly built at the client end rather than trying to serialise them.