【问题标题】:ASP.Net Web API showing correctly in VS but giving HTTP500ASP.Net Web API 在 VS 中正确显示但给出 HTTP500
【发布时间】:2012-06-05 12:42:41
【问题描述】:

昨天经过大量帮助后,我在 asp.net4 beta 中遇到了一个已知错误 - 我升级到 VS2012 RC Express (4.5),现在我收到内部服务器错误,我看不到为什么。我正在创建一个 Web API:

型号

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; }
    }
}

然后我使用添加控制器向导,选择“模板:具有读/写动作的 API 控制器,使用实体框架”,选择 tblCustomerBooking 作为我的模型类,然后单击,即:

using System.Data.Entity;

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

Visual Studio 2012 Express 自动生成的我的控制器(BookingsController.cs)是:

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();
        }
    }
}

我在上面的“return db.....”处添加了一个断点,并检查了 VS 中的 Watch 部分 - 它清楚地显示了对象、客户以及相关的租金:

但是,如果我允许脚本继续运行,我只会收到 http500 错误(如下面的 Fiddler 所示):

是否有更多代码可以添加到控制器中,让我了解它为什么出错?或者任何人都可以看到可能出了什么问题? VS 似乎可以找回它,如第一张截图所示,但似乎无法将其发送出去。

感谢任何帮助或指点,

标记

更新

您好 - 我是否只是对 API 提出了太多要求?它不可能(开箱即用)简单地返回具有一对多关系的对象吗?真的只能产生一个对象列表吗?

谢谢,马克

【问题讨论】:

    标签: asp.net asp.net-mvc asp.net-web-api


    【解决方案1】:

    为了解决返回带有 virtual 关键字实体的 JSON 时出现的错误 500 问题,我执行了以下操作,

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

    在代理受到干扰的特定情况下(例如序列化)禁用代理创建(也禁用延迟加载)就足够了。这只会为 db 的特定上下文实例禁用代理创建

    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】:

      [更新]

      1、更改操作代码以包含导航属性数据:

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

      2、在数据上下文中关闭代理

      EF suggests to turn off proxy when serializing POCO

      如果要支持代理,不同的序列化器有不同的方式:

      JSON.net 序列化器:升级到最新版本可以支持代理。 4.5.1 版本有一个错误,不能支持忽略 NonserializedAttribute。它将阻止代理被序列化。

      DataContractSerializer (JSON/XML):使用 ProxyDataContractResolver 解析类型,这里是walkthrough

      3、在模型类中启用保留引用

      json.net 和 DataContract 序列化器都支持检测循环引用,它们让开发人员控制如何处理它。

      将模型类更改为:

      [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; }
      }
      

      请注意,如果模型使用 DataContract 进行属性化,则必须为其所有成员指定 DataMember,否则将不会对它们进行序列化。

      【讨论】:

        【解决方案3】:

        你在做什么:

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

        另外,您使用的是哪个 MediaTypeFormatter,Xml 还是 Json?错误 500 通常意味着 Formatter 阻塞。

        切换到 JSON.NET 格式化程序(在 Web API RC 中,最简单的方法是执行 GlobalConfiguration.Configuration.Formatters.RemoveAt(1) - 这会删除 XML 格式化程序)并查看它是否有帮助或至少给出更有意义的错误(或请求您使用内容类型 JSON 的方法)。

        【讨论】:

        • 如果你得到 'System.Collections.Generic.List' 到 'System.Collections.Generic.IList'。存在显式转换(您是否缺少演员表?)当您需要“动态”进行救援时 - 而不是 IList 作为返回类型
        • 嗨@Filip W - 谢谢 - 是的,我试过那个方法 - 但VS不会编译/运行,因为它不识别{ i.rental_id} - 刚刚得到红线在它下面,说我缺少指令或参考。在提琴手中,我尝试使用以下命令同时请求 XML 和 JSON:用户代理:提琴手主机:localhost:65387 接受:应用程序/json(和 /xml 用于 xml)-我还收到一个错误,建议 GlobalConfiguration 不包含'的定义格式化程序 - 再次感谢。
        • 因为 tblRentals 是一个虚拟属性,你需要显式加载它,因此上面的 Include,你是这样做的吗?
        • 嗨@Filip W - 是的,我做到了:公共动态GettblCustomerBookings() { return db.tblCustomerBookings.Include("tblRentals").Select(i => new { i.rental_id }); -rental_id 是 tblRentals 模型上的属性,但 VS 不会运行,但会给出错误:“MvcApplication6.Models.tblCustomerBooking”不包含“rental_id”的定义,并且没有扩展方法“rental_id”接受第一个参数可以找到“MvcApplication6.Models.tblCustomerBooking”类型的(您是否缺少 using 指令或程序集引用?) - 再次感谢。
        • 尝试:公共动态 GettblCustomerBookings() { db.Configuration.LazyLoadingEnabled = false; return db.tblCustomerBookings.Include("tblRentals").Select(i => new { name = i.customer_id, rents = i.tblRentals.ToArray() }).ToList(); }
        【解决方案4】:

        您可能希望为您的项目添加一个全局错误处理程序。它可以捕获和记录后台线程中发生的任何奇怪错误。这篇 S/O 文章讨论了一些可靠的方法。他们将在任何项目中为您节省大量时间:ASP.NET MVC Error Logging in Both Global.asax and Error.aspx

        【讨论】:

        • 我在网络表单中使用过 ELMAH,但到目前为止,还不能让它与 RC API 一起使用(有很多相互矛盾的建议)。
        【解决方案5】:

        Api Controller 是基于 Convention-Over-Configuration 的,因此您可以尝试通过以下任一方式解决:

        【讨论】:

        • 我也试过了(改为Get),但仍然没有通过Fiddler获得更多信息。
        【解决方案6】:

        我猜由于实体的延迟加载,您在序列化时遇到了异常。

        这个thread 可以帮助你。

        更新:

        试试这是否可行,如果可行,那么问题主要是我所说的

        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();
        }
        

        我猜如果您使用带有 web api 的 JSON.NET 库,您可以通过指定 [JsonIgnore] 属性轻松控制不需要序列化的属性,这样您就可以避免编写上面的 LINQ 查询。

        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

        【讨论】:

        • 嗨 - @Mark - 我试过那个链接:strathweb.com/2012/03/… - 但是 VS 不会编译,因为它不能识别(例如)i.rental_id(这种类型安全吗)?
        • 嗨 @Mark 感谢您的建议 - VS 报告错误:无法将类型 'System.Collections.Generic.List' 隐式转换为 'System.Collections.Generic.IList'。存在显式转换(您是否缺少演员表?)
        • 嗨@Mark - 如果我将其转换为 Dynamic - 但仍然只返回 tblCustomerBookings 信息 - 你的代码是否可以调整以将关联的 tblRentals 信息添加到 JSON?
        • 如果在“new”关键字后面指定类型名,则不需要指定“dynamic”。
        【解决方案7】:

        我也为将代理对象序列化为 poco 的问题而苦苦挣扎。您可以在上下文中构建一个切换 db.Configuration.ProxyCreationEnabled = false 的标志;

        或者您应该设置一个视图模型并获取代理对象并将参数分配给视图模型。

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

        或使用匿名类型:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-10
          • 1970-01-01
          • 2022-10-20
          • 1970-01-01
          相关资源
          最近更新 更多