ASP.Net Web API rendering correctly in VS but providing HTTP500

after a lot of help yesterday, I encountered a known bug in asp.net4 beta - I upgraded to VS2012 RC Express (4.5), and now I get an internal server error, and I canโ€™t see why. I am creating a web API:

Model

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; using System.Data.Entity.ModelConfiguration.Conventions; using System.Data.Entity; using System.ComponentModel.DataAnnotations.Schema; namespace MvcApplication6.Models { public class tblCustomerBooking { [Key()] public int customer_id { get; set; } public string customer_name { get; set; } public string customer_email { get; set; } public virtual ICollection<tblRental> tblRentals { get; set; } } public class tblRental { [Key()] public int rental_id { get; set; } public int room_id { get; set; } public DateTime check_in { get; set; } public DateTime check_out { get; set; } public decimal room_cost { get; set; } public int customer_id { get; set; } [ForeignKey("customer_id")] public virtual tblCustomerBooking tblCustomerBooking { get; set; } } } 

Then I used the Add Controller wizard, selecting "Template: API controller with read / write instructions using the Entity Framework", selected tblCustomerBooking as my model class and clicked that:

 using System.Data.Entity; namespace MvcApplication6.Models { public class BookingsContext : DbContext { public BookingsContext() : base("name=BookingsContext") { } public DbSet<tblCustomerBooking> tblCustomerBookings { get; set; } } } 

My controller (BookingsController.cs), automatically created by Visual Studio 2012 Express, is:

 using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Infrastructure; using System.Linq; using System.Net; using System.Net.Http; using System.Web; using System.Web.Http; using MvcApplication6.Models; namespace MvcApplication6.Controllers { public class BookingsController : ApiController { private BookingsContext db = new BookingsContext(); // GET api/Bookings public IEnumerable<tblCustomerBooking> GettblCustomerBookings() { return db.tblCustomerBookings.AsEnumerable(); } } } 

I added a breakpoint in "return db ....." above and checked the Watch element in VS - it clearly shows the object, with the client and the associated rents:

Snapshot of customer and rental objects

However, if I allow the script to continue, I just get the http500 error (as shown below in Fiddler): Fiddler screenshot showing HTTP500

Is there more code that I can add to the controller so that I can understand why this is an error? Or can anyone understand what might be wrong? It seems that VS is returning it in order, as shown in the first screenshot, but it seems it cannot send it.

Thanks for any help or pointers,

Mark

Update

Hi, I'm just asking too much about the API? Is it not possible (out of the box) to simply return objects with one or more relationships? Can he really create only one list of objects?

Thanks Mark

+4
asp.net-mvc asp.net-web-api
source share
7 answers

you do:

 db.tblCustomerBookings.Include("tblRentals").Select(i => new { i.something //etc }); 

Also, which MediaTypeFormatter are you using, Xml or Json? Error 500 often means Formatter is suffocating.

Switch to the JSON.NET formatter (in Web API RC, it is easiest to do GlobalConfiguration.Configuration.Formatters.RemoveAt(1) - this removes the XML formatter) and see if it helps or at least gives a more meaningful error (or asks for your method with type JSON content).

+1
source

[Update]

1, change the action code to include data on navigation properties:

  // GET api/Bookings public IEnumerable<tblCustomerBooking> GettblCustomerBookings() { return db.tblCustomerBookings.Include("tblRentals").AsEnumerable(); } 

2, disable the proxy in the data context

EF suggests disabling proxies when serializing POCO .

If you want to support a proxy server, there are different ways for different serializers:

JSON.net serializer: it can support proxy server, updating it to the latest version. Version 4.5.1 has an error that cannot support ignoring NonserializedAttribute. It will block the proxy server for serialization.

DataContractSerializer (JSON / XML): use ProxyDataContractResolver for type resolution, here is a walkthrough .

3, enable link saving in model class

Support for json.net and DataContract serializer, which detects a circular reference, and gives the developer the ability to control how to handle it.

Change the model class as follows:

 [JsonObject(IsReference = true)] [DataContract(IsReference = true)] public class tblCustomerBooking { [Key()] public int customer_id { get; set; } [DataMember] public string customer_name { get; set; } [DataMember] public string customer_email { get; set; } [DataMember] public virtual ICollection<tblRental> tblRentals { get; set; } } public class tblRental { [Key()] public int rental_id { get; set; } public int room_id { get; set; } public DateTime check_in { get; set; } public DateTime check_out { get; set; } public decimal room_cost { get; set; } public int customer_id { get; set; } [ForeignKey("customer_id")] public virtual tblCustomerBooking tblCustomerBooking { get; set; } } 

Note that if the model is attributed to a DataContract, you need to specify a DataMember for all its members, otherwise none of them will be serialized.

+2
source

To fix the 500 error when returning JSON with objects with a virtual keyword, I did the following:

  public class BookingsController : ApiController { private BookingsContext db = new BookingsContext(); // GET api/Bookings public IEnumerable<tblCustomerBooking> GettblCustomerBookings() { db.Configuration.ProxyCreationEnabled = false; return db.tblCustomerBookings.AsEnumerable(); } } 

This is enough to disable the creation of a proxy server (which also disables lazy loading) for specific situations where proxy servers are concerned, for example, serialization. This disables proxy creation only for a specific instance of the db context

 db.Configuration.ProxyCreationEnabled = false; 

http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx

http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-feature-ctp5-part-1-introduction-and-model.aspx

+2
source

You might want to add a global error handler to your project. It can capture and record any odd errors that occur in the background thread. This S / O article talks about some solid approaches. They will save you a lot of time in any project: Recording ASP.NET MVC errors in both Global.asax and Error.aspx files

+1
source

I assume that you get an exception during serialization due to lazy loading of objects.

This one can help you.

UPDATE:

Try it if it works or not, and if so, then the problem is basically what I said

 public IList<tblCustomerBooking> GettblCustomerBookings() { var custBookings = db.tblCustomerBookings.Include("tblRentals").AsEnumerable(); return custBookings .Select(c => new tblCustomerBooking { customer_id = c.customer_id, customer_name = c.customer_name, customer_email = c.customer_email, tblRentals = c.tblRentals .Select(r => new tblRentals { rental_id = r.rental_id, // other props exclude the // tblCustomerBooking }) } ).ToList(); } 

I assume that if you use the JSON.NET library with a web api, you can easily manage properties that do not need to be serialized by specifying the [JsonIgnore] attribute, and there you can avoid writing the above LINQ request.

http://code.msdn.microsoft.com/Using-JSONNET-with-ASPNET-b2423706

http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx

0
source

Api Controller is based on end-to-end configuration, so you can try to solve one of the following methods:

0
source

I struggled with this problem of serializing a proxy object and with poco. You can create a flag in your context that switches db.Configuration.ProxyCreationEnabled = false;

Or you must configure the view model and take the proxy object and set the parameters in view mode.

 public IEnumerable<tblCustomerBooking> GettblCustomerBookings() { return db.tblCustomerBookings.Select(cb=> new CustomerBookingsViewModel{id=cb.Id, et.....); } 

or use an anonymous type:

 .Select(cb=>new{id=cb.id....} 
0
source

All Articles