使用NHibernate 3.0.0时战斗笛卡尔积(x-join)

我的数学很差,但我很清楚笛卡尔的产品是什么。 这是我的情况(简化):
public class Project{
 public IList<Partner> Partners{get;set;}
}
public class Partner{
 public IList<PartnerCosts> Costs{get;set;}
 public IList<Address> Addresses{get;set;}
}
public class PartnerCosts{
 public Money Total{get;set;}
}
public class Money{
 public decimal Amount{get;set;}
 public int CurrencyCode{get;set;}
}
public class Address{
 public string Street{get;set;}
}
我的目标是有效地加载整个项目。 问题当然是: 如果我试图加载合作伙伴及其成本,查询返回大量行 如果我懒加载Partner.Costs,db会收到垃圾邮件请求(比第一种方法快一点) 正如我所读到的,常见的解决方法是使用MultiQueries,但我只是不明白。 所以我希望通过这个确切的例子来学习。 如何有效加载整个项目? 附:我正在使用NHibernate 3.0.0。 请不要用hql或字符串形式的标准api方法发布答案。     
已邀请:
好的,我为自己写了一个反映你的结构的例子,这应该有效:
int projectId = 1; // replace that with the id you want
// required for the joins in QueryOver
Project pAlias = null;
Partner paAlias = null;
PartnerCosts pcAlias = null;
Address aAlias = null;
Money mAlias = null;

// Query to load the desired project and nothing else    
var projects = repo.Session.QueryOver<Project>(() => pAlias)
    .Where(p => p.Id == projectId)
    .Future<Project>();

// Query to load the Partners with the Costs (and the Money)
var partners = repo.Session.QueryOver<Partner>(() => paAlias)
    .JoinAlias(p => p.Project, () => pAlias)
    .Left.JoinAlias(() => paAlias.Costs, () => pcAlias)
    .JoinAlias(() => pcAlias.Money, () => mAlias)
    .Where(() => pAlias.Id == projectId)
    .Future<Partner>();

// Query to load the Partners with the Addresses
var partners2 = repo.Session.QueryOver<Partner>(() => paAlias)
    .JoinAlias(o => o.Project, () => pAlias)
    .Left.JoinAlias(() => paAlias.Addresses, () => aAlias)
    .Where(() => pAlias.Id == projectId)
    .Future<Partner>();

// when this is executed, the three queries are executed in one roundtrip
var list = projects.ToList();
Project project = list.FirstOrDefault();
我的班级有不同的名字,但反映了完全相同的结构。我取代了名字,我希望没有错别字。 说明: 连接需要别名。我定义了三个查询来加载你想要的
Project
Partners
和他们的
Costs
以及
Partners
和他们的
Addresses
。通过使用
.Futures()
我基本上告诉NHibernate在我实际想要结果的那一刻,在一次往返中执行它们,使用
projects.ToList()
。 这将导致三个SQL语句确实在一次往返中执行。这三个语句将返回以下结果: 1)与您的项目1行 2)x行与合作伙伴及其成本(和金钱),其中x是项目合作伙伴的总成本数 3)包含合作伙伴及其地址的y行,其中y是项目合作伙伴的地址总数 你的db应该返回1 + x + y行,而不是x * y行,这将是一个笛卡尔积。我希望您的数据库实际上支持该功能。     
如果您在NHibernate上使用Linq,可以使用以下方法简化笛卡尔预防:
int projectId = 1;
var p1 = sess.Query<Project>().Where(x => x.ProjectId == projectId);


p1.FetchMany(x => x.Partners).ToFuture();

sess.Query<Partner>()
.Where(x => x.Project.ProjectId == projectId)
.FetchMany(x => x.Costs)
    .ThenFetch(x => x.Total)
.ToFuture();

sess.Query<Partner>()
.Where(x => x.Project.ProjectId == projectId)
.FetchMany(x => x.Addresses)
.ToFuture();


Project p = p1.ToFuture().Single();
详细解释如下:http://www.ienablemuch.com/2012/08/solving-nhibernate-thenfetchmany.html     
而不是渴望获取多个集合并得到一个讨厌的笛卡尔积:
Person expectedPerson = session.Query<Person>()
    .FetchMany(p => p.Phones)
        .ThenFetch(p => p.PhoneType)
    .FetchMany(p => p.Addresses)
    .Where(x => x.Id == person.Id)
    .ToList().First();
您应该在一个数据库调用中批量处理子对象:
// create the first query
var query = session.Query<Person>()
      .Where(x => x.Id == person.Id);
// batch the collections
query
   .FetchMany(x => x.Addresses)
   .ToFuture();
query
   .FetchMany(x => x.Phones)
   .ThenFetch(p => p.PhoneType)
   .ToFuture();
// execute the queries in one roundtrip
Person expectedPerson = query.ToFuture().ToList().First();
我刚刚写了一篇关于它的博客文章,解释了如何使用Linq,QueryOver或HQL来避免这种情况 http://blog.raffaeu.com/archive/2014/07/04/nhibernate-fetch-strategies.aspx     
我只是想为Florian提供真正有用的答案。我找到了困难的方法 所有这一切的关键是别名。别名决定了sql的内容 并被NHibernate用作“标识符”。成功加载a的最小Queryover 三级对象图是这样的:
Project pAlias = null;
Partner paAlias = null;

IEnumerable<Project> x = session.QueryOver<Project>(() => pAlias)
 .Where(p => p.Id == projectId)
 .Left.JoinAlias(() => pAlias.Partners, () => paAlias)
 .Future<Project>();


session.QueryOver(() => paAlias).Fetch(partner => partner.Costs).
 .Where(partner => partner.Project.Id == projectId)
 .Future<Partner>();
第一个查询加载项目及其子伙伴。重要的是合作伙伴的别名。 合作伙伴别名用于命名第二个查询。第二个查询加载合作伙伴和成本。 当它作为“Multiquery”执行时,Nhibernate将“知道”第一个和第二个查询是否已连接 由paAlias(或者更确切地说,生成的sqls将具有“相同”的列别名)。 因此,第二个查询将继续加载已在第一个查询中启动的合作伙伴。     

要回复问题请先登录注册