Rajesh Kumar
Rajesh Kumar

Reputation: 2503

Problem with Object Data Type serialization in wcf using DataContract Attribute

I have created a WCF Rest API Service using wcf rest starter kit preview 2 which automatically return JSON or XML data depending on the requested content type. everything is working fine but as my service methods return multiple data type some methods returns a object of a class or collection of class, bool, int or collection type. so I planed to return a same Response in all methods for this I Create a new class as following:

 [DataContract(Namespace = "")]
public class ServiceResponse
{
    [DataMember]
    public bool IsError
    { get; set; }

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

    [DataMember]
    public Object ResponseData
    { get; set; }       

}

and User class as following:

[DataContract(Namespace = "")]
public class User
{
    [DataMember]
    public string UserName 
    { get; set; }

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

}

In ServiceResponse Class I have declared an Object Type property ResponseData and my all method has return type of ServiceResponse. My problem is that when I set any string,bool,int type as ResponseData it is serialized but when I set collection type or an object of another class which also has DataContarct attribute this class is not serialized see below code :

public ServiceResponse GetUser(int userID)
    {

        User user = getUser(userID); get user data from database

        ServiceResponse response=new ServiceResponse();
        var response = new ServiceResponse
                           {
                               IsError = false,
                               ResponseMessage = string.Empty,
                               ResponseData = user; // Setting Result Data here
                           };
        return response;

    }

when I called above method(GetUser) I got Null response due to serialization problem, but following code works fine

public ServiceResponse TestMethod(string testMessage)
    {


        ServiceResponse response=new ServiceResponse();
        var response = new ServiceResponse
                           {
                               IsError = false,
                               ResponseMessage = string.Empty,
                               ResponseData = testMessage; // Setting Result Data here
                           };
        return response;

    }

can anyone help me?

Upvotes: 1

Views: 7893

Answers (1)

David Hoerster
David Hoerster

Reputation: 28701

The problem that you're having is due to the fact that in your DataContract, you are declaring the ResponseData as an Object, but not telling WCF what types are valid known types. You need to explicitly tell WCF what known types are valid for your service, and you can do this by using the ServiceKnownType attribute. I've taken your code and made a few modifications to demonstrate this:

DataContracts

[DataContract(Name="ServiceResponse")]
public class ServiceResponse
{
    [DataMember(Name="IsError", Order=1)]
    public bool IsError
    { get; set; }

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

    [DataMember(Name="ResponseData", Order=3)]
    public Object ResponseData
    { get; set; }

}

[DataContract(Name="User")]
public class User
{
    [DataMember(Name="UserName", Order=1, IsRequired=true)]
    public string UserName
    { get; set; }

    [DataMember(Name="UserID", Order=2, IsRequired=true)]
    public int UserID
    { get; set; }

}

[DataContract(Name = "User2")]
public class User2
{
    [DataMember(Name = "UserName", Order = 1, IsRequired = true)]
    public string UserName
    { get; set; }

    [DataMember(Name = "UserID", Order = 2, IsRequired = true)]
    public int UserID
    { get; set; }

}

ServiceCode

[ServiceContract(Namespace = "WebApplication1")]
[ServiceKnownType(typeof(User))]
[ServiceKnownType(typeof(User2))]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
    [OperationContract(Name="GetUser")]
    [WebGet(UriTemplate = "GetUser/{userID}", ResponseFormat = WebMessageFormat.Json)]
    public ServiceResponse GetUser(String userID)
    {
        User theUser = new User()
        {
            UserID=Convert.ToInt32(userID),
            UserName="bob"
        };

        ServiceResponse sr = new ServiceResponse()
        {
            IsError = false,
            ResponseMessage = "",
            ResponseData = theUser
        };

        return sr;
    }

    [OperationContract(Name = "GetUser2")]
    [WebGet(UriTemplate = "GetUser2/{userID}", ResponseFormat = WebMessageFormat.Json)]
    public ServiceResponse GetUser2(String userID)
    {
        User2 theUser = new User2()
        {
            UserID = Convert.ToInt32(userID),
            UserName = "bob"
        };

        ServiceResponse sr = new ServiceResponse()
        {
            IsError = false,
            ResponseMessage = "",
            ResponseData = theUser
        };

        return sr;
    }

}

What I did:

  1. Not really necessary, but I added some additional attributes to the DataContract and members. I usually like to do that anyway, so I'm just being OCD there. :)
  2. I created a second DataContract to demonstrate the use of ServiceKnownType.
  3. In the service, I added the ServiceKnownType attributes on my service class, stating what types can be returned from my service methods.

That's pretty much it. Using the ServiceKnownType attributes is kind of ugly - there are some solutions out there to get around it (here).

Let me know if you have additional questions. Hope this helps!

Upvotes: 2

Related Questions