我以作者和书籍为例,使解释变得简单。作者 - 书籍有一对多关系,即一位作者可以写很多书。映射是使用 FluentHibernate 完成的,并没有什么特别之处。
我正在尝试查询尚未撰写任何书籍的作者。但是当查看在NHibernate Profiler中生成的查询时,它并不是我所期望的。你们可以把错误搞清楚吗
我期望的结果来自以下查询
select a.AuthorName
,a.AuthorId from Authors a left outer join Books b on a.AuthorId = b.AuthorId
where b.AuthorId is null
生成的实际查询如下
SELECT AuthorId,
AuthorName,
CreatedAt
FROM Authors
WHERE AuthorId is null
为上述提供的映射
public class AuthorsMap : ClassMap<Author>
{
public AuthorsMap()
{
Table("Authors");
LazyLoad();
Id(x => x.AuthorId).GeneratedBy.Identity().Column("AuthorId");
Map(x => x.AuthorName).Column("AuthorName").Not.Nullable();
Map(x => x.CreatedAt).Column("CreateDatetime").Not.Nullable();
HasMany(x => x.Books).KeyColumn("AuthorId");
}
}
以上是为作者提供的映射,下面是书籍
public class BooksMap : ClassMap<Books>
{
public BooksMap()
{
Table("Books");
LazyLoad();
Id(x => x.BookId).GeneratedBy.Identity().Column("BookId");
References(x => x.Author).Column("AuthorId");
Map(x => x.BookName).Column("BookName").Not.Nullable();
}
}
查询调用如下
Session.Linq<Author>().Where(author => author.Books == null).ToList();
上面的另一种风格导致了对象引用错误
Session.Linq<Author>().Where(author => author.Books.Count == 0).ToList();
效果问题:
来自@ Ocelot20的答案有效,但正在生成N + 1个查询以查找记录
Session.Linq<Author>().Where(author => !author.Books.Any()0).ToList();
示例;
作者1,2,3没有书籍,然后在执行Any()时运行以下查询
SELECT AuthorId,BookId,BookName from Books Where AuthorId = 1
SELECT AuthorId,BookId,BookName from Books Where AuthorId = 2
SELECT AuthorId,BookId,BookName from Books Where AuthorId = 3
现在怎么回事!!
答案 0 :(得分:1)
这有用吗?
Session.Linq<Author>().Where(author => !author.Books.Any()).ToList()
我明白为什么author.Books == null
不起作用,因为它应该是一个空集合,而不仅仅是完全为空。我对nhibernate不太熟悉,所以我无法对配置发表评论。这就是我使用Linq编写查询的方式。
答案 1 :(得分:1)
在这种情况下,我建议使用子查询而不是 JOIN 。原因是,一旦我们需要切换查询以查找具有某些书籍的作者,JOIN将乘以结果集(具有2本书的作者将被列出两次等)。
另外,让我们使用带有Query
扩展名的NHibernate原生Linq提供程序语法(返回必需的IQueryable<>
)
实现这一目标的语法是:
子选择:
var subquery = session.Query<Book>()
.Select(b => b.Author.AuthorId)
;
没有任何书的作者集
var list = session.Query<Author>()
.Where(a => !subquery.Contains(a.AuthorId))
;
如果我们想要获得具有某些书籍的作者列表,我们可以删除NOT运算符(!)并应用分页(Take()
,Skip()
),这将返回正确但不会相乘的结果。