【问题标题】:Entity Framework and RESTful WebAPI - possible circular reference实体框架和 RESTful WebAPI - 可能的循环引用
【发布时间】:2018-09-07 11:29:24
【问题描述】:

这是我的模型的简化版本:

public class User  {
    public int UserID { get; set; }
    public string FirstName { get; set; }
    public virtual ICollection<Recipe> Recipes { get; set; }
}

public class Recipe {
    public int RecipeID { get; set; }
    public string RecipeName { get; set; }
    public int UserID { get; set; }
    public virtual User User { get; set; }
}

我有一个控制器,我想返回一个用户以及一些关于他们的食谱的摘要信息。脚手架控制器代码如下所示:

var user = await _context.Users.SingleOrDefaultAsync(m => m.UserID == id);

它工作正常。现在我尝试添加食谱,但它会中断:

var user = await _context.Users.Include(u => u.Recipes).SingleOrDefaultAsync(m => m.UserID == id);

我的网络浏览器开始呈现 JSON,它闪烁,我在浏览器中收到一条消息,说连接已重置。

我的理论 - 我相信父(用户)呈现,它暴露了包含对父(用户)的引用的子(食谱),其中包含子(食谱)的集合) 等等导致无限循环。这就是我认为会发生这种情况的原因:

  1. Visual Studio 调试器允许我以这种方式无限地导航属性。
  2. 如果我注释掉 Recipe.User 属性,它可以正常工作。

我尝试过的 我尝试使用实体框架投影仅包含来自我需要的Recipe 数据(我试图不包含Recipe.User)。我试图只包含 Recipe.RecipeName... 但是当我尝试使用投影来创建这样的匿名类型时:

var user = await _context.Users.Include(u => u.Recipes.Select(r => new { r.RecipeName })).SingleOrDefaultAsync(m => m.UserID == id);

我收到此错误:

InvalidOperationException:属性表达式 'u => {from Recipe r in u.Recipes select new f__AnonymousType1`1(RecipeName = [r].RecipeName)}' 无效。该表达式应表示属性访问:'t => t.MyProperty'。

解决办法是什么?我可以使用不同的语法进行投影吗?我是不是搞错了?

【问题讨论】:

标签: entity-framework asp.net-web-api asp.net-web-api2 asp.net-core-webapi


【解决方案1】:

我可以为你推荐 3 个选项。

  1. U sing [JsonIgnore] on property,但它适用于 Recipe 类的每次使用,因此当您只想返回 Recipe 类时,您不会在其中包含 User。

    public class Recipe {
        public int RecipeID { get; set; }
        public string RecipeName { get; set; }
        public int UserID { get; set; }
        [JsonIgnore]
        public virtual User User { get; set; }
    }
    
  2. 您可以使用此解决方案来停止所有 json 中的引用循环 https://stackoverflow.com/a/42522643/3355459
  3. 最后一个选项是创建类 (ViewModel),它只包含您希望发送到浏览器的属性,并将结果映射到它。出于安全原因,这可能是最好的。

【讨论】:

    【解决方案2】:

    考虑使用 POCO 进行序列化而不是双重链接的实体类:

    public class UserPOCO  {
        public int UserID { get; set; }
        public string FirstName { get; set; }
        public ICollection<RecipePOCO> Recipes { get; set; }
    }
    
    public class RecipePOCO {
        public int RecipeID { get; set; }
        public string RecipeName { get; set; }
        public int UserID { get; set; }
    }
    

    将实体内容复制到对应的 POCO 中,然后将这些 POCO 对象作为 JSON 结果返回。通过使用RecipePOCO 类删除User 属性将删除循环引用。

    【讨论】:

    • Matthew - 我认为你是对的,我认为这是我要选择的选项,但是...如果我有一个 Recipe 类的实例,并且只有 UserID,我将如何通过代码导航到用户?我还没弄明白……
    • 理想情况下,您将使用实体进行导航和任何处理。将数据复制到 POCO 绝对是向客户端发送数据之前的最后一步。您最终会得到类似于类的重复,但 POCO 模型的用途与实体类不同。就客户端而言,一旦数据被序列化为 JSON 并发送,我不知道有任何简单的方法可以恢复链接。
    猜你喜欢
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多