简体   繁体   English

如何获得自引用 C# 实体以使用 JSON.Net 序列化子级?

[英]How to get self-referencing C# Entities to serialize children with JSON.Net?

I'm having a hell of a time getting a hierarchical child collection on a POCO to serialize.我在 POCO 上获取分层子集合进行序列化时遇到了麻烦。 This is an Azure Mobile Services project.这是一个 Azure 移动服务项目。 I've come up with a simplified example in order to try to get this to work.我想出了一个简化的例子来尝试让它工作。

The following is the sole DTO POCO in the project.以下是项目中唯一的DTO POCO。 Note the navigation properties (marked virtual) named Parent and Children , used to link the hierarchy together.请注意名为ParentChildren的导航属性(标记为虚拟),用于将层次结构链接在一起。

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; }
}

The controller:控制器:

public class NodeController : TableController<Node>
{
    /* BEGIN boilerplate (from Azure Mobile Services project template) */
    protected override void Initialize(HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);
        MyContext context = new MyContext();
        DomainManager = new EntityDomainManager<Node>(context, Request, Services);
    }
    /* END boilerplate (from Azure Mobile Services project template) */

    // GET tables/Node
    /// <summary>
    /// Gets all child nodes of a particular node, and their children.
    /// To retrieve the top-level node, pass no id.
    /// </summary>
    /// <param name="id">The id of the node you wish to retrieve.</param>
    /// <returns>The child nodes of a given id.</returns>
    public IQueryable<Node> GetNodes(string id = null)
    {
        return Query().Where(x => x.ParentId == id).Include(x => x.Children);
    }
}

The context class:上下文类:

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) */
    }
}

The context initializer:上下文初始值设定项:

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);
    }
}

Why do the Children never show up in the results?为什么孩子永远不会出现在结果中?

[
    {
        "$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"
    }
]

...even though the API documentation sample that gets generated suggests that there SHOULD be Children: ...即使生成的 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"
      }
    ]

Finally figured it out.终于想通了。 Just had to change the return type of the controller's GetNodes() method from IQueryable<Node> to IEnmerable<Node> .只需将控制器的GetNodes()方法的返回类型从IQueryable<Node>更改为IEnmerable<Node> I still don't know exactly why , but I'm glad it's working.我仍然不知道确切的原因,但我很高兴它起作用了。 Now I can move on to the important stuff: modifying my actual service, and then consuming it in the Xamarin app.现在我可以继续处理重要的事情:修改我的实际服务,然后在 Xamarin 应用程序中使用它。

Revised method signature (threw in some async for good measure):修改后的方法签名(为了更好的衡量,抛出了一些异步):

public async Task<IEnumerable<Node>> GetNode(string id = null)
{
    return await Query()
        .Where(x => x.ParentId == id)
        .Include(x => x.Children)
        .ToListAsync();
}

Results:结果:

[
  {
    "$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"
  }
]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM