December 16, 2010 WCF
December 16, 2010 WCF
Reason is that if you have cyclic dependencies XmlSerializer anyway tries to serialize whole graph till it reaches stackoverflow.
[EDITED because of complains] Actual error is something like “SerializationException: Customer contains cycles and cannot be serialized if reference tracking is disabled…“
To avoid this problem you would need to mark you data contract with [DataContract(IsReference = true)]. So I did:
[ServiceContract]
public interface IDataContractCycles
{
[OperationContract]
CustomerModel GetCustomer();
}
[DataContract(IsReference = true)]
public class CustomerModel
{
[DataMember]
public string Name { get; set; }
[DataMember]
public IList Orders { get; set; }
}
[DataContract(IsReference = true)]
public class OrderModel
{
[DataMember]
public string Name { get; set; }
[DataMember]
public CustomerModel Customer { get; set; }
} And to test I again fired up wcftestclient and it crashed!!! wtf? I had to commit some code to keep guys working so I commented Customer property of my Order. I thought that it would crash in our client the same way.
And now meaning of this story: TRUST NO ONE!
I cannot give up that easily, so I wrote ever simple wcf server and client, then added custom message inspector to see actual payload.
For this you would need something like below:
class Program
{
static void Main(string[] args)
{
var client = new DataContractCyclesClient();
client.Endpoint.Behaviors.Add(new CustomEndpointBehavior());
client.Open();
CustomerModel customerModel = client.GetCustomer();
}
}
internal class CustomEndpointBehavior : IEndpointBehavior
{
public void Validate(ServiceEndpoint endpoint){}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher){}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
}
internal class CustomMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
reply.ToString(); // THIS IS ACTUAL PAYLOAD OF THE MESSAGE
}
}I got correct object graph and payload with reference Id-s. See message below. It has Customer marked with z:Ref=”i1″ and then the same Id is used in two orders. This is cool stuff.
<s:Body u:Id=”_0″>
<GetCustomerResponse xmlns=”http://tempuri.org/”>
<GetCustomerResult z:Id=”i1″ xmlns:a=”http://schemas.datacontract.org/2004/07/WcfLearningCycles” xmlns:i=”http://www.w3.org/2001/XMLSchema-instance” xmlns:z=”http://schemas.microsoft.com/2003/10/Serialization/”>
<a:Name>Andriy Buday</a:Name>
<a:Orders>
<a:OrderModel z:Id=”i2″>
<a:Customer z:Ref=”i1″ />
<a:Name>Kindle</a:Name>
</a:OrderModel>
<a:OrderModel z:Id=”i3″>
<a:Customer z:Ref=”i1″ />
<a:Name>HTC Cable</a:Name>
</a:OrderModel>
</a:Orders>
</GetCustomerResult>
</GetCustomerResponse>
</s:Body>
Liked it? Yes? No?
| Markdown | Result |
|---|---|
| *text* | text |
| **text** | text |
| ***text*** | text |
| `code` | code |
| ~~~ more code ~~~~ |
more code |
| [Link](https://www.example.com) | Link |
| * Listitem |
|
| > Quote | Quote |
so what was the problem?
Customer was not marked as DataReference or what?
You are very unclear in this post, actually…
Also a question, have you tried this with Silverlight?
Either I'm really unclear… or you was not attentive when reading: "Reason is that if you have cyclic dependencies XmlSerializer anyway tries to serialize whole graph till it reaches stackoverflow."
What do you mean when asking if I tried this with Silverlight? Service I wrote is consumed by another service hosted in managed code and then consumer of that second service is WP7 with Silverlight on it of course.
ok, I guess I missed that, because I actually thought from the very beginning, that you are trying to solve circular dependencies :)
as for Silverlight… I have a shared between ASP.NET server and Silverlight client library with DTO classes.
And when I tried to annotate DTO classes and their members with attributes like [DataMemeber] I was stuck with the problem, that those attributes cause compilation errors on server side, because of differences in System.ComponentModel libraries between .NET 4 and Silverlight runtime…
So there is no way to annotate DTO if they are shared. So the only choice is to go with proxied classes, which I don't like for different reasons :)
Yeah… I see your problem. We faced the same and are using proxy classes as well…