使用 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
的查询将导致完全填充的导航属性 OffersTBLs
和 BusinessesTBLs
.
信息很简单:如果您不希望结果中出现这些引用循环,则必须投影到视图模型或 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 实现的所有对象。事实上,您应该始终在断开连接的情况下使用它。
使用 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
的查询将导致完全填充的导航属性 OffersTBLs
和 BusinessesTBLs
.
信息很简单:如果您不希望结果中出现这些引用循环,则必须投影到视图模型或 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 实现的所有对象。事实上,您应该始终在断开连接的情况下使用它。