2

Scenario:

I'm in the process of creating a WCF service that exposes user data. An instance of User has a property called Role. If I omit this property, the service works; if don't omit the property, the service fails. Below is a (very) simplified representation of the code.

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1, EmitConformanceClaims = true)]
[ServiceContract(Name = "IService", Namespace = "http://local/version1/")]
public interface IService : IDisposable
{
   [WebMethod]
   [SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]
   [OperationContract(Action = "http://local/version1/GetUser")]
   GetUserResponse GetUser(GetUserRequest request);
}

[MessageContract(IsWrapped = true, WrapperName = "GetUserResponse")]
[XmlRoot("GetUserResponse")]
public class GetUserResponse
{
   [XmlElement(ElementName = "User")]
   [MessageBodyMember(Order = 0)]
   public User User { get; set; }
}

public class User
{
   public int UserId { get; set; }

   public string UserName { get; set; }

   public Role Role { get; set; }
}

public class Role
{
   public string Name { get; set; }
}

The root of the problem is that the Role is nested/embedded (how do you call it...) in the User class. It seems that it can't be serialzed this way.

If searched on possible solutions, but can't seem to find the right solution. I've tried something with KnownTypeAttribute on the contract as well as the User class, but that didn't work.

Questions:

  • What is the proper way to include the Role property on the User class?
  • Which attributes should be added where to make it work?
  • Possible interfaces to be implemented to make it serializable?

Thanks in advance for any replies!

Update #1

After suggestions of @CPX, I created additional test code on the server side:

using (StringWriter stringWriter = new StringWriter())
{
   XmlSerializer serializer = new XmlSerializer(typeof(GetUserResponse));
   serializer.Serialize(stringWriter, response);
   string result = stringWriter.ToString();
}

This results in an InvalidOperationException with message 'There was an error generating the XML Document'. This exception has an InvalidOperationException (again) as InnerException, with message 'The type System.Data.Entity.DynamicProxies.User_FAC9CE2C5C9966932C0C37E6677D35437C14CDD08‌​884AD33C546805E62B7101E was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.'.

Forgot to mention in the post, that the User and Role classes are used within Entity Framework 4.1. Didn't guess should be a problem, but apparently it does.

Herman Cordes
  • 4,628
  • 9
  • 51
  • 87
  • Please give a description of how the service fails. What exception do you get? – Christian Palmstierna Sep 21 '11 at 12:43
  • Did you try to put [DataContract] and [DataMember] attribute ? – Gregory Nozik Sep 21 '11 at 12:45
  • You should add DataContract and DataMember attributes – meziantou Sep 21 '11 at 12:45
  • @CPX, On the service side I'm not getting an exception. The service simply doesn't return anything. If I attach the debugger, I can see the results are retrieved as it should. The GetUserResponse is initialized (with a Role instance) and returned. One big difference that indicates it goes wrong is that the GetUser() method is executed four times. On the client side I'm getting a CommunicationException with message: 'The underlying connection was closed: The connection was closed unexpectedly'. – Herman Cordes Sep 21 '11 at 12:47
  • @Monty: Ah, in my experience that usually means a serialization error on the server side. Try to serialize the return object yourself by creating a DataContractSerializer, and you should get a more detailed exception on the server side. – Christian Palmstierna Sep 21 '11 at 12:50
  • @GregoryNozik Even when I use MessageContracts I should use DataContracts? – Herman Cordes Sep 21 '11 at 12:51
  • @CPX Should I use any kind of event handler to catch those exceptions on the server side? – Herman Cordes Sep 21 '11 at 12:52
  • 1
    @Monty: Ahh, maybe I should have been more clear :) What I mean't was that you can add some test code on the server side that manually serializes the return object, catch any exception thrown by the serializer and inspect it in debug mode. – Christian Palmstierna Sep 21 '11 at 12:54
  • 1
    @Monty: The reason for using this approach is because to my knowledge, there is no easy way to see exceptions that occur when a response is serialized. You can enable logging on the service, but then you catch it "after the fact" and can't inspect the application or the exception. – Christian Palmstierna Sep 21 '11 at 12:57
  • @CPX Thanks for the suggestion, I've tried it. It's giving me indeed an InvalidOperationException with message 'There was an error generating the XML Document' with InnerException message 'The type System.Data.Entity.DynamicProxies.User_FAC9CE2C5C9966932C0C37E6677D35437C14CDD08884AD33C546805E62B7101E was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.'. – Herman Cordes Sep 21 '11 at 13:01

4 Answers4

2

You will need to make User and Role DataContracts like this:

[DataContract]
public class User
{
   [DataMember]
   public int UserId { get; set; }

   [DataMember]
   public string UserName { get; set; }

   [DataMember]
   public Role Role { get; set; }
}

[DataContract]
public class Role
{
   [DataMember]
   public string Name { get; set; }
}

This lets WCF know how and what to serialize. There is an example of mixing MessageContract and DataContract, as you are doing in your case, on MSDN here: http://msdn.microsoft.com/en-us/library/ff648210.aspx

CodingWithSpike
  • 42,906
  • 18
  • 101
  • 138
  • Slightly different answer than the one of @GregoryNozik. I've tried this, but also I've got the same results as without... Think I'm overlooking something, but don't know what. – Herman Cordes Sep 21 '11 at 13:00
  • The final solution can be done completly without DataContract/DataMember attributes on User/Role classes. However I like your post and the MSDN link explains it also very well. – Herman Cordes Sep 21 '11 at 13:30
2

Probably has something to do with proxy creation.

Try setting the following: context.ContextOptions.ProxyCreationEnabled = false;

Peanutbag
  • 277
  • 1
  • 4
  • 11
  • As stated in 'Update #1' on the start post, I forgot to mention I was using Entity Framework. Only when I tested the serialization process (also see 'Update #1') I discovered it had something to do with Entity Framework. This rule of code you're suggesting is exactly the solution!! It doesn't generate any additional classes, based on User/Role. Only thing is that you have to include 'Role' entity when quering. – Herman Cordes Sep 21 '11 at 13:28
1
    [DataContract]  
    public class User {   

    [DataMember]
    public int UserId { get; set; }     

    [DataMember]
    public string UserName { get; set; } 

    [DataMember]    
    public Role role { get; set; } }  

    [DataContract] 
    public class Role
    {
       [DataMember]
       public string Name { get; set; } 
    } 
Uğur Aldanmaz
  • 1,018
  • 1
  • 11
  • 16
Gregory Nozik
  • 3,296
  • 3
  • 32
  • 47
0

Add datacontract attributes to the classes and datamember attributes to each property you want to serialize

tam
  • 1,583
  • 2
  • 13
  • 25