与IQueryOver连接时的Nhibernate条件

Kos*_*zak 0 nhibernate criteria subquery icriteria queryover

我在谷歌和stackoverflow上搜索类似的问题将近2个小时,但没有找到任何解决方案.

我有2个表,关系1对多.

1) [Accounts]
PK Account_Id
int User_ID


2) [Temporary_Accounts]
Fk Account_Id
char IsAccepted   {'1','0',null}
varchar name
Run Code Online (Sandbox Code Playgroud)

和2个映射类

1) Acc
int Id;
User user;
TempAcc Temp; //cause each Account can have 0 or one TempAcc (with IsAccepted == null)

2)TempAcc
int Id;
bool IsAccepted;
string name;
Run Code Online (Sandbox Code Playgroud)

我想显示给定user_id的所有帐户以及在[Temporary_Accounts]和IsAccepted == null中有记录的Accounts的附加信息(fe name).

所以SQL应该是这样的:

select acc.Account_Id, acc.User_Id, tempacc.Name 

from Account acc left join Temporary_Account tempacc 
on (acc.Account_ID = tempacc.Account_Id and tempacc.IsAccepted is null)
where (acc.User_Id = 65);
Run Code Online (Sandbox Code Playgroud)

但我的IQueryOverquery:

IQueryOver<Acc> query = (...)
query.JoinAlias(f => f.Temp,
                () => Temp,
                JoinType.LeftOuterJoin)
     .Where(f => f.Temp.IsAccepted == null)
     .And(f => f.user.id == userid);
Run Code Online (Sandbox Code Playgroud)

生成这样的sql:

select acc.Account_Id, acc.User_Id, tempacc.Name 

from Accounts acc left join Temporary_Accounts tempacc 
on (acc.Account_ID = tempacc.Account_Id)
where (acc.User_Id = 65 and tempacc.IsAccepted is null);
Run Code Online (Sandbox Code Playgroud)

所以我得到的结果比第一次正确的查询要少.

你有什么想法我应该改变什么,或者我可以做些什么来获得第一次查询的结果?我的想法是使用子查询leftjoin Accounts表,它从Temporary_Accounts表中选择所有IsAccepted = null帐户,但我不知道如何在Iqueryover或Icriteria中执行此操作.

我会很感激任何建议

Mar*_*rry 5

既然你有1-Many之间AccTemp你的样本sql将产生一个笛卡尔积.

您将需要的查询使用子查询,如下所示:

Acc accountAlias = null;
var subQuery = QueryOver.Of<Temp>()
               .Where(x=>x.IsAccepted==null)
               .And(x=>x.Account.Id==accountAlias.Id);

var results = session.QueryOver<Acc>(()=>accountAlias)
              .Where(x=>x.User.Id==65)
              .WithSubquery.WhereExists(subQuery);
Run Code Online (Sandbox Code Playgroud)

像这样生成SQL:

select *
from Accounts a
where a.User_Id=65
and exists (
    select t.Account_Id
    from Temporary_Accounts t
    where t.IsAccepted is null and t.Account_Id=a.Account_Id
)
Run Code Online (Sandbox Code Playgroud)

关于nhibernate.info的这篇文章对于使用QueryOver计算复杂查询非常有帮助.

更新:

如果你还需要查找Accounts哪些没有任何相应的行,Temporary_Accounts那么你需要两个子查询和一个Disjunction.

Acc accountAlias = null;
var hasTempAccount = QueryOver.Of<Temp>()
               .Where(x=>x.IsAccepted==null)
               .And(x=>x.Account.Id==accountAlias.Id);

var doesNotHaveTempAccount = QueryOver.Of<Temp>()
               .And(x=>x.Account.Id==accountAlias.Id);

var results = session.QueryOver<Acc>(()=>accountAlias)
  .Where(x=>x.User.Id==65)
  .Where(Restrictions.Disjunction()
    .Add(Subqueries.WhereExists(hasTempAccount))
    .Add(Subqueries.WhereNotExists(doesNotHaveTempAccount))     
  );
Run Code Online (Sandbox Code Playgroud)

更新2:

从NH 3.2开始,您可以为JOIN添加额外条件.有关更多详细信息,请参阅此答案:使用NHibernate ICriteria/QueryOver查询向外部联接添加条件

Temp tempAlias = null;
Account accountAlias = null;
dto dto = null;
var results = Session.QueryOver<Account>(()=>accountAlias)
  .JoinAlias(x=>x.TempAccounts,()=>tempAlias,JoinType.LeftOuterJoin,
    Restrictions.IsNull(Projections.Property(()=>tempAlias.IsAccepted))
  )
  .Where(x=>x.Account.Id==65)
  .SelectList(list=>list
      .Select(()=>accountAlias.Id).WithAlias(()=>dto.AccountId)
      .Select(()=>accountAlias.User.Id).WithAlias(()=>dto.UserId)
      .Select(()=>tempAlias.Name).WithAlias(()=>dto.TempAccName)
  )
  .SetResultTransformer(Transformers.AliasToBean<dto>())
  .List<dto>();
Run Code Online (Sandbox Code Playgroud)