我有一个看起来像这样的实体:
public class Album
{
public virtual string Name { get; set; }
public virtual IEnumerable<Media> { get; set; }
public virtual IEnumerable<Picture>
{
get { return Media.OfType<Picture>(); }
}
public virtual IEnumerable<Video>
{
get { return Media.OfType<Video>(); }
}
public virtual IEnumerable<Audio>
{
get { return Media.OfType<Audio>(); }
}
}
其中Media
是抽象基类,Picture
,Video
和Audio
是Media
的子类型,因此IEnumerable<Media>
集合是异质的。
我有Album
的DTO,如下所示:
public class AlbumDTO
{
public string Name { get; set; }
public int PictureCount { get; set; }
public int VideoCount { get; set; }
public int AudioCount { get; set; }
}
通过执行<collection>.Count();
填充每个计数。虽然这段代码工作正常,但我得到了每种媒体类型的计数,但生成的SQL并不理想:
SELECT * FROM Media WHERE media.Album_id = 1
SELECT * FROM Media WHERE media.Album_id = 2
SELECT * FROM Media where media.Album_id = 3
换句话说,它首先从数据库中抓取所有Media
,然后在内存中执行OfType<T>.Count()
。问题是,如果我在所有Albums
上执行此操作,它将从数据库中选择所有Media
,这可能是数千条记录。最好,我想看到这样的东西(我正在使用每层次表映射):
SELECT COUNT(*) FROM Media WHERE media.Album_id = 1 AND discriminator = 'Picture'
SELECT COUNT(*) FROM Media WHERE media.Album_id = 1 AND discriminator = 'Video'
SELECT COUNT(*) FROM Media WHERE media.Album_id = 1 AND discriminator = 'Note'
有谁知道如何配置NHibernate来做到这一点?或者我是否必须修改我的Album
实体才能获得正确的行为?
答案 0 :(得分:2)
首先,你的代码不会编译;你错过了IEnumerable<Media>
的属性名称(我假设它是媒体),还有过滤器。
其次,你必须了解一下发生了什么。从这种行为来看,我很确定你已经将你的相册与HasMany关系映射到Media。默认情况下,NH延迟加载,所以当您第一次从数据库中检索Album时,Media会被赋予对称为PersistentBag的NHibernate对象的引用。这只是一个看起来像IEnumerable的占位符,并且在实际需要时保存填充真实列表的逻辑。它所能做的就是在调用其GetEnumerator()方法时拉出HBM中映射的记录(几乎每个Linq方法都会发生这种情况)。因此,当您调用OfType时,您不再使用NHibernate IQueryable,这可以构建一个完全符合您需要的SQL语句。相反,你要求你认为已经拥有的列表中的每个元素,并且NHibernate遵守。
如果你想要的只有Count,你有一些选择。如果可能的话,最简单的方法就是回到Session并要求对Album进行全新的查询:
session.Linq<Album>().Where(a=>a.Id = 1).Select(a=>a.Media.OfType<Picture>()).Count();
这将直接构建一个语句,该语句将转到数据库并获取记录的计数。你不是懒加载任何东西,你要求Repository计算,它知道如何直接转换为SQL。