如何从 C# 控制器(OData)的 JSON 序列化中修复丢失的子对象(导航属性)?

Posted

技术标签:

【中文标题】如何从 C# 控制器(OData)的 JSON 序列化中修复丢失的子对象(导航属性)?【英文标题】:How to fix a missing child object (navigation property) from JSON serialization from C# controller (OData)? 【发布时间】:2014-04-25 22:57:07 【问题描述】:

堆栈:MVC5、WebAPI、OData、EF6

在 MVC 控制器中,我返回一个序列化为 JSON 对象的对象:

                .Select(s => new ProjectEditorDTO()
                
                    // projection
                    WebsiteId = s.WebsiteId,
                    WebsiteGUID = s.WebsiteGUID,
                    WebsiteName = s.WebsiteName,
                    WebsiteNotes = s.WebsiteNotes,
                    Test = "some test string",
                    DefaultContentType = new ContentTypeDTO 
                         
                            ContentTypeId = s.DefaultContentTypeID,
                            Description = s.ContentType.Description
                        
                );

            var r = results.ToList();  // contains DefaultContentType object

            return Ok(results, results.GetType());

问题是当我在 Fiddler 中查看返回的数据时,它缺少 JSON 对象内的DefaultContentType 数据。

我有另一个控制器使用相同的对象(单独),并且返回很好。

仅供参考,这里是ContentTypeDTO

public class ContentTypeDTO

    public int ContentTypeId  get; set; 

    [Required]
    [StringLength(100, MinimumLength=3)]
    [Display(Name="Content Type")]
    public string Description  get; set; 

    //public int NumberOfContentTypes  get; set; 

    //public UserDTO UserDTO  get; set; 

我正在使用 WebAPI,并将其配置如下以返回 JSON 内容:

        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;

        config.Formatters.Remove(config.Formatters.XmlFormatter);

-- 更新--

我正在使用 WebAPI 和 OData,因此生成的调用必须包含 ProjectEditorDTO 对象形式的 ProjectEditor 目标:

        modelBuilder.EntitySet<Core.UI.Models.ProjectEditorDTO>("ProjectEditor");              

从客户端返回的数据是通过 OData URL 完成的:

?$format=json&amp;$inlinecount=allpages&amp;$top=10

当我添加 $expand= 语法 ($format=json&amp;$inlinecount=allpages&amp;$top=10&amp;$expand=DefaultContentTypeID) 时,我收到以下错误:

类型 ... 上的属性“DefaultContentTypeID”不是导航属性。只能展开导航属性。

即使在数据库中这被设置为 FK。

我猜测缺少一些扩展语法并确保导航属性有效?

我在这方面一定遗漏了一些非常小的东西......建议?

【问题讨论】:

您的DefaultContentType 是否可以在声明类/命名空间之外访问?也许有什么? ProjectEditorDTO 内部:public ContentTypeDTO DefaultContentType get; set; 。指的就是这个吧? 是的。没关系,只要确保它可以访问,否则我们可能已经在这里很长时间了! :) 我认为这更多是暴露导航属性的问题。我将重新提出问题以更好地反映这一点。 【参考方案1】:

我看不到 JSON 序列化在哪里。我猜是在Ok 方法中。就我所见,我看到您传递了一个自定义对象,用于像 JSON 对象一样对待。您可以在投影集合时尝试使用通用对象吗?只是避免使用您的类来定义对象,如下所示:

        .Select(s => new 
        
            // projection
            WebsiteId = s.WebsiteId,
            WebsiteGUID = s.WebsiteGUID,
            WebsiteName = s.WebsiteName,
            WebsiteNotes = s.WebsiteNotes,
            Test = "some test string",
            DefaultContentType = new 
                 
                    ContentTypeId = s.DefaultContentTypeID,
                    Description = s.ContentType.Description
                
        );

【讨论】:

我更新了显示 JSON 序列化在何处完成的问题。由于我使用的是 WebAAPI 和 OData,因此输出对象定义为 ProjectEditorDTO 并且必须与之匹配。当我将其设置为泛型类型时,我收到一个错误,因为它必须是 ProjectEditorDTO 哦!我现在看到更新了。抱歉,我想我们是同时写的。我不知道这些东西是如何工作的,但我认为你需要在你的ProjectEditorDTO 类中将DefaultContentTypepropertie 设置为virtual。你能检查一下吗?【参考方案2】:

很抱歉迟到了,我刚看了这个问题。 我遇到了同样的问题,我修复了它,将 [Key] 属性添加到 DTO 类的 id 字段中。有了它,子对象就被正确地视为 NavigationProperty,并且可以毫无错误地调用 $expand。

【讨论】:

以上是关于如何从 C# 控制器(OData)的 JSON 序列化中修复丢失的子对象(导航属性)?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 为 OData 查询中指定的每个过滤器获取一组键/值对?

为 JSON 构建 C# 对象 [重复]

在控制器的 onInit 中读取 OData 上下文

如何在 C# 操作中读取 OData URL 参数值?

如何在 C# 中拆分 OData 多级展开查询字符串?

参数中带有 IEnumerable 的 C# OData Web API POST 端点返回错误 400,输入无效