Asp.net mvc 使用实体框架我只想包括第一个子对象,而不是子对象的子对象(子对象的子对象)

Asp.net mvc 使用实体框架我只想包括第一个子对象,而不是子对象的子对象(子对象的子对象),asp.net-mvc,entity-framework,entity-framework-4,linq-to-entities,entity-framework-5,Asp.net Mvc,Entity Framework,Entity Framework 4,Linq To Entities,Entity Framework 5,使用实体框架,我希望只包含第一级的子对象,而不是子对象的子对象 我有两门课: 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 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; }
}
公共类业务ESTBL
{
公共字符串ID{get;set;}
公共字符串名{get;set;}
公共字符串lastName{get;set;}
公共ICollection OffersTBLs{get;set;}
}
公共类报价TBL
{
公共int ID{get;set;}
公共字符串名称{get;set;}
公共int CatId{get;set;}
公共字符串BusinessesTBLID{get;set;}
公共虚拟业务estbl BusinessesTBLs{get;set;}
}
当我尝试根据CatId字段提交所有报价时,我还需要返回BusinessesTBL,但该方法也会根据每个BusinessesTBL obj再次返回报价,我的代码是:

public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
    db.OffersTBLs.Include(s => s.BusinessesTBLs);
}
public IQueryable GetOffersTBLsCat(int-id)
{
db.OffersTBLs.Include(s=>s.BusinessesTBLs);
}
您可以在以下屏幕上看到错误的结果:

正如您所看到的,它返回每个业务对象下的所有报价,而业务对象返回每个报价,我只想返回带有其业务对象的报价,而不返回业务对象下的报价


有人能帮忙吗

假设对象不是空的,只是空的:

 public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
 {
     db.OffersTBLs.Include(s => s.BusinessesTBLs).Where(x => !x.BusinessesTBLs.OffersTBLs.Any());
 }
public IQueryable GetOffersTBLsCat(int-id)
{
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);
}
public IQueryable GetOffersTBLsCat(int-id)
{
db.OffersTBLs.Where(x=>!x.BusinessesTBLs.OffersTBLs.Any())
。包括(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; }
}
公共类业务ESTBL
{
公共字符串ID{get;set;}
公共字符串名{get;set;}
公共字符串lastName{get;set;}
//在这里放一个虚拟的
公用虚拟ICollection OffersTBLs{get;set;}
}

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

因为OffersTBL与BusinessesTBL和BusinessesTBL与OffersTBL有关联,所以可以无限循环抛出实体,如OffersTBL.BusinessesTBL.OffersTBL.BusinessesTBL等等

为了控制实体的嵌套深度,我通常使用带有所需属性的HelperClass

商务英语

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
}
在查询数据库时,您可以直接从queryresult创建新的HelperObject:

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

现在你有了所有的报价,所有的业务都在你的控制之下。循环在此停止,因为BusinessesTBLs下没有OffersTBL。然而,列表中只有他们的ID,以便进一步参考和识别。

一次否决投票使我重新注意到了这个答案(谢谢)。我现在明白了其中很大一部分是胡说八道

毫无疑问,这种无休止循环的原因是关系修复。但你不能阻止EF这么做。即使使用
AsNoTracking
,EF也会在一个查询中具体化的对象中执行关系修复。因此,使用
Include
查询将生成完全填充的导航属性
OffersTBLs
BusinessesTBLs

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


原始答案:

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

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

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


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

谢谢您的尝试,我尝试了您的线路,但它没有返回任何结果!我需要返回前两个级别,每个级别的报价和业务obj,但不返回三个级别,同时将与每个业务相关的所有报价显示为三级!如何加载数据库对象?如果您进入该方法,db.OffersTBLs中是否有任何内容?每个BusinessesTBL obj都有许多offer(相关的offer有很多),在我的代码中,我创建了OffersTBLs.Include(s=>s.BusinessesTBLs)以将父业务obj带到并返回它w
public IEnumerable<OffersTBLHelper> GetOffersTBLsCat(int id)
{
    return db.OffersTBLs.where(s => s.CatId == id).Select(x=> new OffersTBLHelper(x)).ToList();
}
db.OffersTBLs.Include(s => s.BusinessesTBLs)
             .AsNoTracking()