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 IListOrders { 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…