C# NHibernate筛选的子集合延迟加载,即使指定了急切获取

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

我试图找出为什么即使在急切地加载集合并且生成的SQL是正确的情况下,也会返回子集合而不进行筛选

这些类的fluent映射为:

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);