【问题标题】:Return JSON with WEB API Controller with objects with foreign keys使用带有外键的对象返回带有 WEB API 控制器的 JSON
【发布时间】:2012-10-25 13:11:41
【问题描述】:

我的代码有问题。 我有一些基本的 WEB API 控制器可以正常工作(三个字段没有任何外键),但是我有一个 API 控制器的问题,它从模型中返回一个对象列表,并且该类具有另一个类的外键模型。 这会引发错误: 这是 Api 控制器:

Public Class MaestroProvinciaController
    Inherits System.Web.Http.ApiController

    Private db As New UnificadorEntities

    ' GET api/MaestroProvincia
    Function GetMaestroProvincias() As IEnumerable(Of MaestroProvincia)
        Dim l As IEnumerable(Of MaestroProvincia)
        l = db.MaestroProvincia.AsEnumerable()
        Return l
    End Function
End Class

这是MaestroProvincia的模型

Partial Public Class MaestroProvincia
  Public Property Codigo As Integer
  Public Property Descripcion As String
  Public Overridable Property Usuario As ICollection(Of Usuario) = New HashSet(Of Usuario)

End Class

当我消费,尝试,从浏览器到这个地址: ......../api/maestroprovincia

我收到一个错误: Error del servidor El sistema encontró un error mientras extraía ......../api/maestroprovincia . Es posible que el servidor no esté disponible por mantenimiento o no esté bien configurado. A continuación se detallan algunas sugerencias: Volver a cargar esta página web después. Error HTTP 500 (Internal Server Error): Se encontró una situación inesperada mientras el servidor intentaba cumplir con la solicitud.

当我调试Controller时,在"l"变量中放了一个WATCH(检查),列表的对象类型很奇怪,而不是MaestroProvincia类型我得到system.data.entity.DynamicProxies.MaestroProvincia_D7543654378543

从没有 FK 的模型返回对象的所有其他 API 控制器都没有任何问题。

感谢您的帮助。

【问题讨论】:

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


    【解决方案1】:

    看起来您正在使用模型优先或数据库优先方法的 EF。 EF 正在返回动态代理,允许您使用外键延迟加载依赖表。您不能序列化动态代理,这是您的 API 返回结果所必需的。直接返回您的 EF 实体通常不是一个好习惯。您应该改用纯POCOSdata transfer objects (DTO)

    我所说的纯 POCOS 是指您自己定义的类,它们模仿实体但只返回客户端所需的数据。您不能使用 EF 使用 T4 模板生成的 POCOS,因为它们有时是纯 POCOS,有时它们是动态代理。将实体转换为 DTO 的方法是为实体中的属性创建一个使用相同名称的类,并且仅包括客户端使用的那些属性。我通常将 DTO 放在单独的命名空间中,以将它们与实体区分开来。然后使用Automapper 自动将实体映射到 DTO,并将 DTO 映射到返回的实体。 Automapper 从映射过程中消除了苦差事。只需一行代码即可创建地图:

    Mapper.CreateMap<Entity.MaestroProvincia, DTO.MaestroProvincia>();
    

    然后只需要另一行代码来执行实际的映射:

    DTO.MaestroProvincia dto = Mapper.Map<Entity.MaestroProvincia, DTO.MaestroProvincia>(maestroProvincia);
    

    【讨论】:

    • 我该怎么做?我认为带有 db.MaestroProvincia.AsEnumerable 的 EF 已经返回了 POCO 实体列表。
    • 非常感谢,最后一个问题,我应该什么时候执行 Mapper.createmap?就在 Mapper.Map 之前,或者我可以在代码中的另一个位置执行一次?
    • 我在 Web API 控制器的顶部定义它们,以便在构造控制器时创建它们。
    【解决方案2】:

    这些是 Entityy Framework 创建的代理,用于促进延迟加载。但它将具有您的域模型所具有的所有属性。您应该做的是从这些类中读取数据并将您的 ViewModel(POCO 类)返回到公共世界。将域模型原样返回到公共世界不是一个好主意。人们可以从中找出您的桌子的样子。

    创建 ViewModel(POCO 类)以将数据返回到外部世界。只拥有返回所必需的属性。您不需要一直在视图模型中拥有域模型的所有属性。稍后从您的数据访问中获取数据,读取项目,将其映射到您的视图模型的实例并返回它。

    List<Mastero> masteroList=new List<Mastero>();
    var domainItems=db.MaestroProvincia.AsEnumerable();
    foreach(var item in domainItems)
    {
      var vm=new Mastero();
      vm.Name=item.Name;
      vm.Description=item.Description;
      masteroList.Items.Add(vm);
    }  
     //Now return masteroList to the public
    

    假设 Mastero 是一个 POCO 类,你的视图模型。

    public class Mastero
    {
      public string Name { set;get;}
      public string Description { set;get;}
    }
    

    【讨论】:

    • 谢谢,我明白了,但是我必须用另一个 POCO 类重复整个域?
    • @user1800750 :不是整个域模型。只有您想要返回公众的内容。将域模型绑定到前端并不是一个好主意,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多