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

Arn*_*psa 32 nhibernate cartesian-product eager-loading

我的数学很差,但我很清楚笛卡尔的产品是什么.
这是我的情况(简化):

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;}
}
Run Code Online (Sandbox Code Playgroud)

我的目标是有效地加载整个项目.

问题当然是:

  • 如果我试图加载合作伙伴及其成本,查询返回大量行
  • 如果我懒加载Partner.Costs,db会收到垃圾邮件请求(比第一种方法快一点)

正如我所读到的,常见的解决方法是使用MultiQueries,但我只是不明白.
所以我希望通过这个确切的例子来学习.

如何有效加载整个项目?

Ps我正在使用NHibernate 3.0.0.
请不要用hql或字符串形式的标准api方法发布答案.

Flo*_*Lim 46

好的,我为自己写了一个反映你的结构的例子,这应该有效:

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();
Run Code Online (Sandbox Code Playgroud)

我的班级有不同的名字,但反映了完全相同的结构.我取代了名字,我希望没有错别字.

说明:

连接需要别名.我定义了三个查询来加载Project你想要的,Partners与他们CostsPartners他们的Addresses.通过使用.Futures()我基本上告诉NHibernate在我实际想要结果的那一刻,在一次往返中执行它们projects.ToList().

这将导致三个SQL语句确实在一次往返中执行.这三个语句将返回以下结果:1)1行与您的项目2)x行与合作伙伴及其成本(和金钱),其中x是项目合作伙伴的成本总数3)y行与合作伙伴及其地址,其中y是项目合作伙伴的地址总数

你的db应该返回1 + x + y行,而不是x*y行,这将是一个笛卡尔积.我希望您的数据库实际上支持该功能.


Mic*_*uen 5

如果您在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();
Run Code Online (Sandbox Code Playgroud)

详细解释如下:http://www.ienablemuch.com/2012/08/solving-nhibernate-thenfetchmany.html