如何在NHibernate中的异构集合上执行OfType<>()。Count()?

时间:2010-12-07 19:51:34

标签: c# sql linq nhibernate fluent-nhibernate

我有一个看起来像这样的实体:

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是抽象基类,PictureVideoAudioMedia的子类型,因此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实体才能获得正确的行为?

1 个答案:

答案 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。