Chen
Chen

Reputation: 621

ASP.NET Web Api System.Runtime.Serialization.SerializationException

I have a fully working MVC4 web site to which today I am trying to add a Web API though without success.

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'System.Data.Entity.Infrastructure.DbQuery`1[[LeasingWeb.Models.Car, LeasingWeb, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' with data contract name 'ArrayOfCar:http://schemas.datacontract.org/2004/07/LeasingWeb.Models' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.SerializationException
</ExceptionType>
<StackTrace>
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteCarDBToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract ) at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.<>c__DisplayClass7.<WriteToStreamAsync>b__6() at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)
</StackTrace>
</InnerException>
</Error>

My objects are these:

public class Image
{
    public int ID { get; set; }

    [Required(ErrorMessage="Please select a car")]
    [ForeignKey("Car")]
    public int CarID { get; set; }

    [DisplayName("Picture")]
    [Required]
    [FileExtensions(ErrorMessage = "Please specify a valid image file (.jpg, .jpeg, .gif or .png)", Extensions = ("jpg,png,jpeg"))]
    public string Name { get; set; }


    public virtual Car Car { get; set; }
}

public class Car
{
    public int ID { get; set; }

    [Required]
    [DisplayName("Car Model")]
    public string Name { get; set; }

    [Required]
    public string Company { get; set; }

    [Required]
    [DisplayName("Car Type")]
    [ForeignKey("CarType")]
    public int CarTypeID { get; set; }

    [Required]
    [Range(1,5)]
    [DisplayName("Number Of Doors")]
    public float NumDoors { get; set; }

    [Required]
    [Range(0, Int32.MaxValue)]
    public float Acceleration { get; set; }

    public virtual CarType CarType { get; set; }
}

public class CarType
{
    [Key]
    public int ID { get; set; }

    [Required]
    [DataType(DataType.Text)]
    public string Type { get; set; }
}

And an object that holds them both:

public class CarDB
{
    public IQueryable<Car> Cars { get; set; }
    public IEnumerable<Image> Images { get; set; }
}

The API Controller:

public CarDB Get(int ID = -1)
{
    CarDB car = new CarDB();
    if (ID == -1)
    {
        car = new CarDB { Cars = db.Cars.Include(c => c.CarType), Images = db.Images };
    }
    else
    {
        car = new CarDB { Cars = db.Cars.Where(c => c.ID == ID).Include(c => c.CarType), Images = db.Images.Where(c => c.CarID == ID) };
    }
    return car;
}

Thanks to anyone that can help.

Upvotes: 3

Views: 5760

Answers (2)

trees_are_great
trees_are_great

Reputation: 3911

I had this problem with legacy code using 30 tables with about 40 Foreign Keys with Lazy Loading. I found it easier, rather than adding more annotations to all the database classes, to just create a ViewModel with the fields that I wanted to be returned. If the data being returned is complex, then I suggest using automapper.

Upvotes: 0

Youssef Moussaoui
Youssef Moussaoui

Reputation: 12395

The issue here is the Cars member on the CarDB object. The DataContractSerializer special cases collection interfaces like IEnumerable and IList, but it doesn't special case derived interfaces like IQueryable. For these interfaces, they're treated as if the type were object and any implementations must be declared as known types.

You can try fixing this by changing the IQueryable<Car> member to IEnumerable<Car>.

Upvotes: 4

Related Questions