实体框架父子表仅返回直接子项

时间:2016-03-11 19:32:04

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

我有一个标准的父/子EF模型,如下所示

public class DataDictionary 
{
    public int Id { get; set; }
    public String Name { get; set; }

    public int? ParentId { get; set; }

    [JsonIgnore]
    public virtual DataDictionary Parent { get; set; }

    public virtual ICollection<DataDictionary> Children { get; set; }
}

我通过WebApi将其公开为REST api,并且当我获取如下节点时,它将返回完整的父子层次结构。

{
"Id": 1,
"Name": "root",
"SegmentKey": null,
"ParentId": null,
"Children": [{
    "Id": 2,
    "Name": "Demographics",
    "SegmentKey": null,
    "ParentId": 1,
    "Children": [{
        "Id": 3,
        "Name": "Gender",
        "ParentId": 2,
        "Children": []
    }, {
        "Id": 4,
        "Name": "Age",
        "ParentId": 2,
        "Children": []
    }, {
        "Id": 5,
        "Name": "Income",
        "ParentId": 2,
        "Children": []
    }]
}, {
    "Id": 6,
    "Name": "Activity",
    "SegmentKey": null,
    "ParentId": 1,
    "Children": [{
        "Id": 7,
        "Name": "Navigation",
        "SegmentKey": null,
        "ParentId": 6,
        "Children": []
    }, {
        "Id": 8,
        "Name": "Behaviour",
        "SegmentKey": null,
        "ParentId": 6,
        "Children": []
    }]
}]
}

但是我需要只返回请求的对象和直接子节点,以便我的消费者可以在用户浏览数据时构建可视化表示。

更新:感谢评论家伙们,所有看起来都很好的取消虚拟然而我正在努力与.Include因为我在异步方法中,find返回对象并且我丢失了上下文。即。

    [ResponseType(typeof(DataDictionary))]
    public async Task<IHttpActionResult> GetDataDictionary(int id)
    {
        DataDictionary dataDictionary = await db.DataDictionaries.FindAsync(id);
        if (dataDictionary == null)
        {
            return NotFound();
        }
        return Ok(dataDictionary);
    }

感谢任何帮助

2 个答案:

答案 0 :(得分:3)

对于您来说,这是因为您的实体中使用了“虚拟”关键字。此关键字为您的集合启用延迟加载,因此当序列化程序来序列化您的子集合时,它会尝试枚举此集合,从而使其从数据库加载。之后,此集合中的每个元素都以递归方式序列化,从而导致从数据库加载每个子集合加载(带有N + 1选择问题)。

做你想做的事:

首先,从您的Children属性中删除virtual关键字:

public class DataDictionary 
{
    public int Id { get; set; }
    public String Name { get; set; }

    public int? ParentId { get; set; }

    [JsonIgnore]
    public virtual DataDictionary Parent { get; set; }

    public ICollection<DataDictionary> Children { get; set; }
}

其次,您需要在控制器中急切地加载此集合。此代码将仅为您的dataDictionary类实例加载1个级别:

[ResponseType(typeof(DataDictionary))]
public async Task<IHttpActionResult> GetDataDictionary(int id)
{
    DataDictionary dataDictionary = await db.DataDictionaries
        .Include(x=>x.Children)
        .FirstOrDefaultAsync(x=>x.Id == id);
    if (dataDictionary == null)
    {
        return NotFound();
    }
    return Ok(dataDictionary);
 }

不要忘记在文件开头添加using System.Data.Entity以访问.Include()功能。

另外,请考虑不要在api中使用Entity Framework实体 - 更好地创建DTO,这将允许您通过API减少对数据库结构的依赖性 - API将只有EF实体的字段子集。您还可以在此处限制树深度,从而创建一个没有子集合的子类。

希望这有帮助!

答案 1 :(得分:0)

如果您不希望检索漏洞子列表,请删除虚拟关键字。

并独立编程一个函数来加载第一个Child。