如何让自引用C#实体用JSON.Net序列化子对象?

如何让自引用C#实体用JSON.Net序列化子对象?,c#,json,entity-framework,azure-mobile-services,C#,Json,Entity Framework,Azure Mobile Services,让POCO上的分层子集合序列化,我真是费了好大劲。这是一个Azure移动服务项目。为了让这一切顺利进行,我想出了一个简单的例子 以下是项目中唯一的DTO POCO。请注意名为Parent和Children的导航属性(标记为virtual),用于将层次结构链接在一起 public class Node : EntityData { public Node() { Children = new List<Node>(); } public

让POCO上的分层子集合序列化,我真是费了好大劲。这是一个Azure移动服务项目。为了让这一切顺利进行,我想出了一个简单的例子

以下是项目中唯一的DTO POCO。请注意名为
Parent
Children
的导航属性(标记为virtual),用于将层次结构链接在一起

public class Node : EntityData
{
    public Node()
    {
        Children = new List<Node>();
    }

    public string Text { get; set; }

    public string ParentId { get; set; }

    public virtual Node Parent { get; set; }

    public virtual ICollection<Node> Children { get; set; }
}
…即使生成的API文档示例表明应该有子项:

[
    {
        "$id": "1",
        "text": "sample string 1",
        "parentId": "sample string 2",
        "parent": {
          "$ref": "1"
        },
        "children": [
          {
            "$ref": "1"
          },
          {
            "$ref": "1"
          },
          {
            "$ref": "1"
          }
        ],
        "id": "sample string 3",
        "__version": "QEBA",
        "__createdAt": "2015-06-26T01:46:14.108Z",
        "__updatedAt": "2015-06-26T01:46:14.108Z",
        "__deleted": true
      },
      {
        "$ref": "1"
      },
      {
        "$ref": "1"
      }
    ]

我终于明白了。只需将控制器的
GetNodes()
方法的返回类型从
IQueryable
更改为
IEnmerable
。我仍然不知道确切的原因,但我很高兴它起作用了。现在我可以继续讨论重要的事情:修改我的实际服务,然后在Xamarin应用程序中使用它

修改的方法签名(为了更好地度量,引入了一些异步):


这是序列化的问题还是查询结果中不包含子项?进行标记狂欢并没有帮助缩小真正的问题范围……当我在调试过程中检查时,我分配给子节点的节点确实有子节点。我认为这是一个序列化问题。我已经对Json.NET序列化程序设置进行了大量修改,但似乎没有任何效果。当然,当我说“调试期间检查”时,我必须稍微修改控制器以调用ToList(),以便执行查询和检查。因此,与其添加EF堆栈,不如在
GetNode
中返回带有子项的静态结果时验证是否发生此问题。。。如果这是Json.net的一个问题,那么您根本不需要EF堆栈来向我们展示您的问题……这是一个很好的观点。我来试一试,然后回来报告。这是令人沮丧的,因为我以前使用LINQ的Include()时没有遇到任何问题。它似乎与相同类型层次结构的Json.NET序列化有关。
public class MyContext : DbContext
{
    private const string connectionStringName = "Name=MS_TableConnectionString";

    public MyContext() : base(connectionStringName)
    {
    } 

    public DbSet<Node> Nodes { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Node>()
            .HasMany(t => t.Children)
            .WithOptional(t => t.Parent)
            .HasForeignKey(t => t.ParentId);

        /* BEGIN boilerplate (from Azure Mobile Services project template) */
        string schema = ServiceSettingsDictionary.GetSchemaName();
        if (!string.IsNullOrEmpty(schema))
        {
            modelBuilder.HasDefaultSchema(schema);
        }

        modelBuilder.Conventions.Add(
            new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
                "ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
        /* END boilerplate (from Azure Mobile Services project template) */
    }
}
public class MyContextInitializer : ClearDatabaseSchemaAlways<MyContext>
{
    protected override void Seed(MyContext context)
    {
        var node0_1 = new Node { Id = Guid.NewGuid().ToString(), Text = "Node 0-1" };
        var node0_2 = new Node { Id = Guid.NewGuid().ToString(), Text = "Node 0-2" };

        var node1_1 = new Node { Id = Guid.NewGuid().ToString(), Text = "Item 1-1", Parent = node0_1 };
        var node1_2 = new Node { Id = Guid.NewGuid().ToString(), Text = "Item 1-2", Parent = node0_1 };

        var node2_1 = new Node { Id = Guid.NewGuid().ToString(), Text = "Item 2-1", Parent = node0_2 };
        var node2_2 = new Node { Id = Guid.NewGuid().ToString(), Text = "Item 2-2", Parent = node0_2 };

        node0_1.Children.Add(node1_1);
        node0_1.Children.Add(node1_2);

        node0_2.Children.Add(node2_1);
        node0_2.Children.Add(node2_2);

        List<Node> nodes = new List<Node>
        {
            node0_1,
            node0_2
        };

        context.Set<Node>().AddRange(nodes);

        base.Seed(context);
    }
}
[
    {
        "$id": "1",
        "id": "2c381538-b8e9-4b7c-b25d-7f6fd8cd373e",
        "parentId": null,
        "text": "Node 0-2"
    },
    {
        "$id": "2",
        "id": "695af179-aa27-45d3-9299-a96c5e719448",
        "parentId": null,
        "text": "Node 0-1"
    }
]
[
    {
        "$id": "1",
        "text": "sample string 1",
        "parentId": "sample string 2",
        "parent": {
          "$ref": "1"
        },
        "children": [
          {
            "$ref": "1"
          },
          {
            "$ref": "1"
          },
          {
            "$ref": "1"
          }
        ],
        "id": "sample string 3",
        "__version": "QEBA",
        "__createdAt": "2015-06-26T01:46:14.108Z",
        "__updatedAt": "2015-06-26T01:46:14.108Z",
        "__deleted": true
      },
      {
        "$ref": "1"
      },
      {
        "$ref": "1"
      }
    ]
public async Task<IEnumerable<Node>> GetNode(string id = null)
{
    return await Query()
        .Where(x => x.ParentId == id)
        .Include(x => x.Children)
        .ToListAsync();
}
[
  {
    "$id": "1",
    "children": [
      {
        "$id": "2",
        "children": [],
        "parent": {
          "$ref": "1"
        },
        "text": "Item 1-2",
        "parentId": "6a616abe-8328-4ca0-92e4-de0734101f2f",
        "id": "398cf2e6-dbfb-4fe1-8555-13090885292f",
        "__version": "AAAAAAABbzQ=",
        "__createdAt": "2015-06-26T21:07:31.466Z",
        "__updatedAt": "2015-06-26T21:07:31.466Z"
      },
      {
        "$id": "3",
        "children": [],
        "parent": {
          "$ref": "1"
        },
        "text": "Item 1-1",
        "parentId": "6a616abe-8328-4ca0-92e4-de0734101f2f",
        "id": "6560562b-0694-4436-bc50-08ead5af29e0",
        "__version": "AAAAAAABbzY=",
        "__createdAt": "2015-06-26T21:07:31.521Z",
        "__updatedAt": "2015-06-26T21:07:31.521Z"
      }
    ],
    "text": "Node 0-1",
    "id": "6a616abe-8328-4ca0-92e4-de0734101f2f",
    "__version": "AAAAAAABbzI=",
    "__createdAt": "2015-06-26T21:07:31.382Z",
    "__updatedAt": "2015-06-26T21:07:31.399Z"
  },
  {
    "$id": "4",
    "children": [
      {
        "$id": "5",
        "children": [],
        "parent": {
          "$ref": "4"
        },
        "text": "Item 2-1",
        "parentId": "fdf50979-e191-41c7-b6c4-c2067cd88dc9",
        "id": "9417571f-f89e-4183-8c14-ba7da3629624",
        "__version": "AAAAAAABbzo=",
        "__createdAt": "2015-06-26T21:07:31.634Z",
        "__updatedAt": "2015-06-26T21:07:31.634Z"
      },
      {
        "$id": "6",
        "children": [],
        "parent": {
          "$ref": "4"
        },
        "text": "Item 2-2",
        "parentId": "fdf50979-e191-41c7-b6c4-c2067cd88dc9",
        "id": "b077165c-1e3e-456f-b4c5-6b116941ba30",
        "__version": "AAAAAAABbzw=",
        "__createdAt": "2015-06-26T21:07:31.693Z",
        "__updatedAt": "2015-06-26T21:07:31.694Z"
      }
    ],
    "text": "Node 0-2",
    "id": "fdf50979-e191-41c7-b6c4-c2067cd88dc9",
    "__version": "AAAAAAABbzg=",
    "__createdAt": "2015-06-26T21:07:31.575Z",
    "__updatedAt": "2015-06-26T21:07:31.575Z"
  }
]