【问题标题】:SerializationException whenever virtual is added to a property每当将 virtual 添加到属性时,SerializationException
【发布时间】:2015-08-03 11:29:58
【问题描述】:

我的 web api 项目中有一个 Report 类:

[KnownType(typeof(Employee))]
[DataContract]
public class Report
{
    [DataMember]
    public int Id { get; set; }

    public string ManagerId { get; set; }
    public string EmployeeId { get; set; }

    [DataMember]
    public virtual Employee Manager { get; set; }

    [DataMember]
    public virtual Employee Employee { get; set; }
}

如果virtual 在方法签名中,我会收到以下异常:

类型 'System.Data.Entity.DynamicProxies.Report_1FFC700B8A805A61BF97A4B9A18D60F99AAA83EE08F4CA2E2454BADA9737B476' 与数据合同名:预计不会 'Report_1FFC700B8A805A61BF97A4B9A18D60F99AAA83EE08F4CA2E2454BADA9737B476 http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'。考虑使用 DataContractResolver 或将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给 DataContractSerializer 的已知类型列表中。

如果不是一切正常。我错过了什么?


已编辑以包含对象检索代码

ApiController

[ResponseType(typeof(IEnumerable<Report>))]
public IHttpActionResult GetLogs([FromUri] string id)
{
    return Ok(_loggingService.GetReportsByEmployeeId(id));
}

服务

public IQueryable<Report> GetReportsByEmployeeId(string employeeId)
{
    return _reports.GetAll().Where(x => x.ManagerId.Equals(employeeId) || x.EmployeeId.Equals(employeeId));
}

存储库

public IQueryable<T> GetAll()
{
    return _dbSet;
}

【问题讨论】:

    标签: c# entity-framework asp.net-web-api


    【解决方案1】:

    大概您正在使用实体框架或类似的 ORM 检索此对象。

    当您没有任何虚拟属性时,ORM 可以安全地使用您的类型的实例。但是,当您拥有虚拟属性时,ORM 必须使用自己的类型来支持延迟加载,这就是为什么您会看到序列化程序抱怨它无法序列化类型“DynamicProxies”。

    为避免这种情况,请将域模型与序列化模型分开,并在调用 Select 时返回序列化模型的实例。

    或者,在 ORM 中关闭延迟加载(不推荐)

    编辑:添加 cmets 中讨论的示例

    如前所述,理想情况下,您的领域模型(用于与数据库交互的数据的对象表示)和业务模型(您为业务逻辑操作的数据的对象表示)应该分开。此外,服务之外的任何内容都不应直接接触域模型,因为这样做可能会引发意外的延迟加载、代码库中的职责模糊或其他令人讨厌的结果。

    顺便说一句,这种分离将避免您遇到的问题。下面的示例当然可以随意更改命名空间和类型名称。

    商业模式

    namespace MyCompany.MyProject.BusinessModels
    {
        [KnownType(typeof(Employee))]
        [DataContract]
        public class Report
        {
            [DataMember]
            public int Id { get; set; }
    
            [DataMember]
            public virtual Employee Manager { get; set; }
    
            [DataMember]
            public virtual Employee Employee { get; set; }
        }
    }
    

    领域模型

    namespace MyCompany.MyProject.DomainModels
    {
        // this separation may seem like overkill, but when you come to
        // want different business models than your domain models, or to decorate
        // this model with Entity Framework specific attributes, you'll be glad
        // for the separation.
        public class Report
        {
            public int Id { get; set; }
    
            public virtual Employee Manager { get; set; }
    
            public virtual Employee Employee { get; set; }
        }
    }
    

    服务

    public IQueryable<BusinessModels.Report> GetReportsByEmployeeId(string employeeId)
    {
        return _reports // _reports is a collection of Type DomainModels.Report
                .GetAll()
                .Where(x => 
                     x.ManagerId.Equals(employeeId) 
                     || x.EmployeeId.Equals(employeeId))
                .Select(s =>
                   new BusinessModels.Report
                   {
                        Id = s.Id,
                        Employee = s.Employee,
                        Manager = s.Manager
                   })
                .ToList(); 
                // We don't want database access happening outside of the service. 
                // ToList() executes the SQL *now* rather than waiting until 
                // the first time you enumerate the result.
    }
    

    【讨论】:

    • 您的意思是像 DTO 还是可以提供/链接到示例?我不确定如何实现这种分离。
    • 如果您使用获取对象的代码更新您的问题,我会在桌面上更新我的答案
    • 已编辑。让我知道是否有任何进一步的说明有用。
    • 非常感谢。这是很棒的东西。
    猜你喜欢
    • 1970-01-01
    • 2017-03-31
    • 2015-02-09
    • 2013-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多