使用 Entity framework 我只想包含第一个子对象而不是子对象的子对象(子对象的子对象)

Use Entity framework I want to include only first children objects and not child of child(sub of sub)

使用 Entity framework 我只想包含第一级子对象而不是子对象的子对象

我有这两个 类:

public class BusinessesTBL
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string lastName { get; set; }

    public ICollection<OffersTBL> OffersTBLs { get; set; }
}

public class OffersTBL 
{
    public int ID { get; set; }
    public string Name { get; set; }

    public int CatId { get; set; }

    public string BusinessesTBLID { get; set; }
    public virtual BusinessesTBL BusinessesTBLs { get; set; }
}

当我尝试根据 CatId 字段提供所有报价时,我还需要 return BusinessesTBL,但该方法还 return 每个 BusinessesTBL obj 再次提供,我的代码是:

public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
    db.OffersTBLs.Include(s => s.BusinessesTBLs);
}

您可以在 上看到错误的结果: http://mycustom.azurewebsites.net/api/OffersApi/GetOffersTBLsCat/4

如您所见,return 每个业务对象下的所有报价,而每个报价下的业务对象,我只想 return 业务对象下的报价及其业务对象,而不是业务对象下的报价。

有人可以帮忙吗?

假设对象不是 null 并且只是空的:

 public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
 {
     db.OffersTBLs.Include(s => s.BusinessesTBLs).Where(x => !x.BusinessesTBLs.OffersTBLs.Any());
 }

编辑:在包含之前过滤:

public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
     db.OffersTBLs.Where(x => !x.BusinessesTBLs.OffersTBLs.Any())
         .Include(s => s.BusinessesTBLs);
}

您已停用 OffersTBL 上的延迟加载,使其成为非虚拟的。如果您激活延迟加载怎么办?像这样:

public class BusinessesTBL
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string lastName { get; set; }

    //put a virtual here
    public virtual ICollection<OffersTBL> OffersTBLs { get; set; }
}

然后,请确保在序列化时不要 call/includeOffersTBL。如果 OffersTBL 仍在返回,那是因为您正在代码中的某处获取它们。如果发生这种情况,请编辑您的问题并粘贴所有代码,包括序列化逻辑。

由于 OffersTBL 与 BusinessesTBL 和 BusinessesTBL 与 OffersTBL 有关联,您可以无限循环抛出 OffersTBL.BusinessesTBL.OffersTBL.BusinessesTBL 等实体。

为了控制实体的嵌套深度,我通常使用具有所需属性的辅助类。

对于企业TBL

public class BusinessesTBLHelper
{
    private BusinessesTBLHelper(BusinessesTBL o){
        ID = o.ID;
        FirstName = o.FirstName;
        lastName = o.LastName;
        OffersTBLids = new List<int>();

        foreach(OffersTBL offersTbl in o.OffersTBLs){
            OffersTBLids.Add(offersTbl.ID);
        }
    }

    public string ID { get; set; }
    public string FirstName { get; set; }
    public string lastName { get; set; }

    public IEnumerable<int> OffersTBLids { get; set; } //no references anymore
}

对于您的 OffersTBL 实体也是如此。

public class OffersTBLHelper
{
    private OffersTBLHelper(OffersTBL o){
        ID = o.ID;
        Name = o.Name;
        CatId = o.CatId;
        BusinessesTBLID = o.BusinessesTBLID;
        BusinessesTBLs = new BusinessesTBLHelper(o.BusinessesTBLs);
    }

    public string ID { get; set; }
    public string Name{ get; set; }
    public intCatId{ get; set; }

    public string BusinessesTBLID { get; set; }
    public BusinessesTBLHelper BusinessesTBLs { get; set; }
}

在查询数据库时,您可以直接从查询结果创建新的辅助对象:

public IEnumerable<OffersTBLHelper> GetOffersTBLsCat(int id)
{
    return db.OffersTBLs.where(s => s.CatId == id).Select(x=> new OffersTBLHelper(x)).ToList();
}

现在您拥有了所有带有 BusinessesTBL 的 OffersTBL。循环在这里停止,因为 BusinessesTBL 下没有 OffersTBL。但是,它在列表中只有它们的 ID,以供进一步参考和识别。

否决票使我重新注意到这个答案(谢谢)。我现在看到其中很大一部分是废话。

果然死循环的原因是关系修复。但是您不能阻止 EF 这样做。即使在使用 AsNoTracking 时,EF 在一次查询 中具体化的对象中执行关系修复 。因此,您使用 Include 的查询将导致完全填充的导航属性 OffersTBLsBusinessesTBLs.

信息很简单:如果您不希望结果中出现这些引用循环,则必须投影到视图模型或 DTO class,如 中所示。在我看来,在进行序列化时,另一种不太吸引人的方法是将序列化程序配置为忽略引用循环。另一个不太吸引人的替代方法是使用 AsNoTracking 单独获取对象,然后自己有选择地填充导航属性。


原回答:

发生这种情况是因为 Entity Framework 执行 关系修复 ,这是当上下文中存在属于那里的对象时自动填充导航属性的过程。因此,使用循环引用,即使延迟加载被禁用,您也可以无休止地向下钻取导航属性。 Json 序列化程序正是这样做的(但显然它被指示处理循环引用,因此它不会陷入无限循环)。

诀窍是防止关系修复发生。关系修复依赖于上下文的 ChangeTracker,它缓存对象以跟踪它们的更改和关联。但是,如果没有什么可跟踪的,就没有什么可修复的。您可以通过调用 AsNoTracking():

来停止跟踪
db.OffersTBLs.Include(s => s.BusinessesTBLs)
             .AsNoTracking()

如果除此之外您还禁用上下文的延迟加载(通过设置 contextConfiguration.LazyLoadingEnabled = false),您将看到只有 OffersTBL.BusinessesTBLs 填充在 Json 字符串中并且 BusinessesTBL.OffersTBLs 是空数组。

好处是 AsNoTracking() 提高了性能,因为更改跟踪器并不忙于跟踪 EF 实现的所有对象。事实上,您应该始终在断开连接的情况下使用它。