【问题标题】:Web API2 Serialization exceptionWeb API2 序列化异常
【发布时间】:2015-12-27 22:43:47
【问题描述】:

我有一个使用 EF 搭建的 web api2 应用程序。

我正在尝试序列化以下类(其中包含延迟加载的导航属性):

namespace Models
{
    public class Speciality
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int SpecialityId { get; set; }

        public string Name { get; set; }

        public virtual ICollection<Doctor> Doctors { get; set; }
        public virtual ICollection<MedicalFacility> MedicalFacilities { get; set; }
    }
} 

这是我的控制器:

namespace FindAMedicService.Controllers
{

    public class SpecialitiesController : ApiController
    {
        private ApplicationDbContext db = new ApplicationDbContext();

        // GET: api/Specialities
        public IQueryable<Speciality> GetSpeciality()
        {
            return db.Speciality;
        }
    }
}

当我尝试访问此特定服务以获取“专业”列表时,我收到以下错误:

<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.DynamicProxies.Speciality_DC264D6DBBAF52FB19E27F20DCC47DA1141620CEC0CCAF6E2DEF4D8907FA7C8C' with data contract name 'Speciality_DC264D6DBBAF52FB19E27F20DCC47DA1141620CEC0CCAF6E2DEF4D8907FA7C8C:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer 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 the serializer.
</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 WriteArrayOfSpecialityToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract ) at System.Runtime.Serialization.CollectionDataContract.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.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
</StackTrace>
</InnerException>
</Error>

在我的 Specialty 类(Doctors、MedicalFacilities)中没有两个导航属性之前,我没有出现此错误。

非常感谢任何帮助。

【问题讨论】:

  • 当您尝试return db.Speciality.ToList(); 时会发生什么?

标签: c# asp.net-mvc serialization asp.net-web-api2


【解决方案1】:

简单回顾一下 - 你为什么返回 IQueryable 和你的实体模型?

在我看来,您应该将您的实体模型转换为某种仅具有所需属性的 DTO + 将操作签名更改为 IEnumerable&lt;T&gt;

序列化程序不知道如何序列化 EF 为您的 virtual 属性创建的动态代理。

【讨论】:

  • 谢谢。我只是使用了 IQueryable,因为它是这样搭建的,并且以前可以工作。但是到了序列化虚拟属性的地步。那么是否可以发送包含其他类的类?我应该删除虚拟吗?或者添加一个 .include 语句来显式添加导航属性?
  • @NormanBentley 您应该提供从实体模型到结果对象的某种转换。一旦实体化,实体通常非常重,我相信您不需要它们的所有属性。急切加载是一种选择,但在 IMO 中是不可行的。
【解决方案2】:

请在 webconfig 中输入以下代码 sn-p:

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreservReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);

【讨论】:

    【解决方案3】:

    原因可能是您的导航属性是接口,因此当您访问服务时,它不知道要使用什么具体类型。将其更改为 List 看看是否有帮助?

    【讨论】:

    • 它们没有交互。它们是实际实现的类
    【解决方案4】:

    使用 Serializable() 属性。 试试这个:

    namespace Models
    {
        [Serializable]
        public class Speciality
        {
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public int SpecialityId { get; set; }
    
            public string Name { get; set; }
    
            public virtual ICollection<Doctor> Doctors { get; set; }
            public virtual ICollection<MedicalFacility> MedicalFacilities { get; set; }
        }
    } 
    

    【讨论】:

      【解决方案5】:

      最终起作用的是禁用 ProxyCreationEnabled。

      所以在我的控制器中,我现在有以下代码:

      public class SpecialitiesController : ApiController
          {
              private ApplicationDbContext db = new ApplicationDbContext();
      
              public SpecialitiesController()
              {
                  db.Configuration.ProxyCreationEnabled = false;
              }
      
              // GET: api/Specialities
              public IQueryable<Speciality> GetSpeciality()
              {
                  return db.Speciality;
              }
          }
      

      这确实意味着我必须使用 .include() 急切加载我想使用的任何导航属性。

      【讨论】:

      • 正如我所建议的,您可以通过使用 DTO 对象将实体转换为普通对象来保留延迟加载。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-18
      • 2010-12-15
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多