C# NHibernate筛选的子集合延迟加载,即使指定了急切获取
我试图找出为什么即使在急切地加载集合并且生成的SQL是正确的情况下,也会返回子集合而不进行筛选 这些类的fluent映射为:C# NHibernate筛选的子集合延迟加载,即使指定了急切获取,c#,nhibernate,nhibernate-criteria,C#,Nhibernate,Nhibernate Criteria,我试图找出为什么即使在急切地加载集合并且生成的SQL是正确的情况下,也会返回子集合而不进行筛选 这些类的fluent映射为: public class OptionIdentifierMap : ClassMap<OptionIdentifier> { public OptionIdentifierMap() : base("OptionIdentifier") { //Id Mapping Removed HasMan
public class OptionIdentifierMap : ClassMap<OptionIdentifier>
{
public OptionIdentifierMap()
: base("OptionIdentifier")
{
//Id Mapping Removed
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None();
}
}
public class OptionPriceMap : ClassMap<OptionPrice>
{
public OptionPriceMap()
: base("OptionPrice")
{
//Id Mapping removed
References(x => x.Option)
.Column("OptionIdentifier_id")
.Cascade.None()
.ForeignKey("FK_OptionPrice_OptionIdentifier_id_OptionIdentifier_Id")
.Not.Nullable();
References(x => x.Increment)
.Column("PricingIncrement_id")
.Cascade.None()
.ForeignKey("FK_OptionPrice_PricingIncrement_id_PricingIncrement_Id")
.Not.Nullable();
Map(x => x.Price).Not.Nullable();
}
}
我用于生成此查询的条件是:
ICriteria pagedCriteria = this.Session.CreateCriteria<OptionIdentifier>()
.CreateAlias("OptionPrices", "op", JoinType.InnerJoin)
.CreateAlias("op.Increment", "i", JoinType.InnerJoin)
.SetFetchMode("op", FetchMode.Eager)
.SetFetchMode("i", FetchMode.Eager)
.Add(Restrictions.Eq("i.IncrementYear", 2015))
.Add(Expression.In("Id", idList.ToList<int>()))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity);
ICriteria pagedCriteria=this.Session.CreateCriteria()
.CreateAlias(“OptionPrices”、“op”、JoinType.InnerJoin)
.CreateAlias(“op.Increment”、“i”、JoinType.InnerJoin)
.SetFetchMode(“op”,FetchMode.Eager)
.SetFetchMode(“i”,FetchMode.Eager)
.Add(限制条件等(“i.增加年”,2015))
.Add(表达式.In(“Id”,idList.ToList()))
.SetResultTransformer(标准规范距离);
当查看SQL Profiler时,查询将执行,并且结果是正确的,我从与OptionIdentifier匹配的可用4行中为OptionPrice表中与条件匹配的每个子项获取一行,在我的示例中为一行(PricinCrement中有4行,OptionPrice中有4行,OptionIdentifier_id 7的每个PricinCrement对应一行)
但是,当我尝试迭代集合以获取一些值时,出于某种原因,nhibernate正在加载子集合,就像指定了延迟加载一样,并加载完整的4个子行。阅读文档FetchMode应该可以解决这一问题,防止nhibernate延迟加载子集合。类似于N+1常见问题
我检查了SQL探查器以查看发生了什么,当我尝试访问子集合时,nhibernate正在生成没有原始筛选器的查询以填充子集合。如果我不访问该集合,则不会生成查询
在做一些测试时,我尝试了不同的连接类型和获取模式,到目前为止,在不让hibernate加载所有元素的情况下迭代集合的唯一方法是在连接类型LeftOuterJoin中指定,但这意味着不同的东西
我试着寻找类似的问题,但他们都说急切加载应该有效,或者说我应该使用过滤器。到目前为止,我还没有找到任何答案
非常感谢您的帮助。我想分享我的方法,也许不是答案 I.避免获取一对多(集合) 当创建任何类型的复杂查询(ICriteria、QueryOver)时,我们应该只在开始模式上使用(左)连接。例如,在
多对一
(fluent中的References()
),这会从分页的角度导致预期的行数(每个根实体始终只有一行)
为了避免集合的1+N问题(但即使事实上是多对一),我们有NHiberante强大的功能:
NHibernate可以有效地利用批取,也就是说,如果访问一个代理(或集合),NHibernate可以加载多个未初始化的代理。批取是对惰性选择获取策略的优化
请在此处阅读更多信息:
public PricingIncrementMap()
: base("PricingIncrement")
{
Map(x => x.IncrementYear);
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse() // I would use .Inverse() as well
// batch fetching
.BatchSize(100);
}
我们需要再次扩展映射:
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse()
// batch fetching
.BatchSize(100)
// this filter could be turned on later
.ApplyFilter<CollFilter>();
HasMany(x=>x.OptionPrices)
.KeyColumn(“OptionIdentifier\u id”)
.Cascade.None()
.Inverse()
//批量取数
.批量大小(100)
//此过滤器可以稍后打开
.ApplyFilter();
现在,在执行我们的查询之前,我们只需启用该过滤器并提供2015年的正确ID:
// the ID of the PricingIncrement with year 2015
var pricingIncrementId thes.Session
.QueryOver<PricingIncrement>()
.Where(x => x.IncrementYear == 2015)
.Take(1)
.Select(x => x.ID)
.SingleOrDefault<int?>();
this.Session
.EnableFilter("CollFilter")
.SetParameter("pricingIncrementId", pricingIncrementId);
// ... the star schema query could be executed here
//2015年的保单ID
var pricingIncrementId thes.Session
.QueryOver()
其中(x=>x.IncrementYear==2015)
.采取(1)
.选择(x=>x.ID)
.SingleOrDefault();
本次会议
.EnableFilter(“CollFilter”)
.SetParameter(“pricingIncrementId”,pricingIncrementId);
//…可以在此处执行星型架构查询
三、 用于筛选根实体的子查询
最后,我们可以使用子查询来限制查询返回的根实体的数量
请在此处阅读更多信息:
// Subquery
var subquery = DetachedCriteria.For<OptionPrice >()
.CreateAlias("Increment", "i", JoinType.InnerJoin)
.Add(Restrictions.Eq("i.IncrementYear", 2015))
.SetProjection(Projections.Property("Option.ID"));
// root query, ready for paging, and still filtered as wanted
ICriteria pagedCriteria = this.Session.CreateCriteria<OptionIdentifier>()
.Add(Subqueries.PropertyIn("ID", subquery))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity);
//子查询
var subquery=DetachedCriteria.For()
.CreateAlias(“增量”、“i”、JoinType.InnerJoin)
.Add(限制条件等(“i.增加年”,2015))
.SetProjection(Projections.Property(“Option.ID”);
//根查询,已准备好分页,但仍按需要进行筛选
ICriteria pagedCriteria=this.Session.CreateCriteria()
.Add(子查询.PropertyIn(“ID”,子查询))
.SetResultTransformer(标准规范距离);
小结:我们可以使用NHibernate附带的许多功能。它们的存在是有原因的。通过它们的结合,我们可以实现稳定可靠的代码,为进一步扩展做好准备(首先是分页)
注意:可能我有一些输入错误……但总体思路应该很清楚这个思路很清楚,我会尝试看看它会把我带到哪里,并公布我的测试结果。我会认为,在一次数据库旅行中急切地加载一个子集合是一个更好的解决方案,而不是让一个单独的查询加载一个开始模式,一个加载一个开始模式我想说的是,NHibernate需要更深入的理解,才能使它成为一个真正强大的仆人。祝你好运,先生;)
public class CollFilter : FilterDefinition
{
public CollFilter()
{
WithName("CollFilter")
.WithCondition("PricingIncrement_id = :pricingIncrementId")
.AddParameter("pricingIncrementId",NHibernate.Int32);
}
}
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse()
// batch fetching
.BatchSize(100)
// this filter could be turned on later
.ApplyFilter<CollFilter>();
// the ID of the PricingIncrement with year 2015
var pricingIncrementId thes.Session
.QueryOver<PricingIncrement>()
.Where(x => x.IncrementYear == 2015)
.Take(1)
.Select(x => x.ID)
.SingleOrDefault<int?>();
this.Session
.EnableFilter("CollFilter")
.SetParameter("pricingIncrementId", pricingIncrementId);
// ... the star schema query could be executed here
// Subquery
var subquery = DetachedCriteria.For<OptionPrice >()
.CreateAlias("Increment", "i", JoinType.InnerJoin)
.Add(Restrictions.Eq("i.IncrementYear", 2015))
.SetProjection(Projections.Property("Option.ID"));
// root query, ready for paging, and still filtered as wanted
ICriteria pagedCriteria = this.Session.CreateCriteria<OptionIdentifier>()
.Add(Subqueries.PropertyIn("ID", subquery))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity);