NHibernate:没有CastProjection的持久性

时间:2013-10-01 15:24:08

标签: nhibernate nhibernate-projections

假设我有一个包含学术论文表的数据库,并且有一个专栏指出该论文是否发表在“主要”期刊上(作为一点点)。我可能想运行一个查询来列出每个作者以及他们是否曾在其中一个作者中发表过。它可能看起来像:

select author, max( cast( major_journal as INT )) as ever_published
from AcademicPapers
group by author;

酷!现在我想用NHibernate做到这一点。删除查询的其余部分,并专注于max(cast( ... ))部分,我尝试了这个:

Projections.Max<AcademicPaper>( 
  m => Projections.Cast( NHibernateUtil.Int32, Projections.Property( "major_journal" ) ) )
  .WithAlias( () => report.EverPublished )

然而,当我运行这个时,我得到一个或多或少难以理解的异常:

No persister for: NHibernate.Criterion.CastProjection

我100%肯定地知道我已经错误地构建了这个Projection业务,但我还没有找到NHibernate的好参考资料;每次我搜索一个,我只找到StackOverflow。我很乐意接受这个特殊问题,或者链接到这里实际发生的事情。

谢天谢地!

1 个答案:

答案 0 :(得分:0)

我希望我能正确理解你的问题,所以你只想让所有至少有一篇论文的作者将这个标志设置为真,对吗?

为什么你不只是使用Linq,它更容易编写,应该适用于这种简单的场景。 我也会将你的旗帜映射到一个布尔,所以我想根本不需要进行Max操作...... 例如:

var authorsWithPublications = session.Query<Paper>()
     .Select(p => new { Author = p.Author, HasPublished = p.HasPublished })
     .Where(p => p.HasPublished == true).ToList();

我使用了这个简单的场景,请告诉我这是否与您的问题不符:

实体+制图:

public class Paper
{
    public virtual int Id { get; set; }
    public virtual string Author { get; set; }
    public virtual bool HasPublished { get; set; }
    public virtual string Description { get; set; }
    public virtual string Something { get; set; }
    public virtual string SomethingElse { get; set; }
}

public class PaperMap : ClassMap<Paper>
{
    public PaperMap()
    {
        Id<int>("Id");

        Map(p => p.Author);
        Map(p => p.HasPublished);
        Map(p => p.Description);
        Map(p => p.Something);
        Map(p => p.SomethingElse);
    }
}

创建一些测试数据和查询

using (var session = sessionFactory.OpenSession())
{
    Random r1 = new Random();

    for (int i = 0; i < 100; i++)
    {
        session.Save(new Paper()
        {
            Author = "Author" + i,
            HasPublished = r1.Next(0, 2) == 0 ? false : true,
            Description = "Description" +i,
            Something = "Something" + i,
            SomethingElse = "something else" + i
        });
    }
    session.Flush();

    var authorsWithPublications = session.Query<Paper>()
                .Select(p => new { Author = p.Author, HasPublished = p.HasPublished })
                .Where(p => p.HasPublished == true).ToList();
}

它确实让我完全回复了那些作者......你可以进一步处理它以获得一个独特的结果......

:编辑开始: 要查询具有该标志的最大值的所有作者,使用linq会变得有点棘手,以下linq查询将返回该结果:

var authorsWithPublications = session.Query<Paper>()
                .GroupBy(p => new { Author = p.Author })
                .Select(p => new { 
                    Author = p.Key, 
                    HasPublished = p.Max(c=> c.HasPublished) 
                })
                .ToList();

但是如果c.HasPublished是SqlServer中的一个位字段,它会给你一个sql异常,即在位字段上不允许max。

尝试将bool转换为int,如同linq语句一样

 ...HasPublished = p.Max(c => c.HasPublished == true ? 1 : 0)

将抛出异常Code supposed to be unreachable,因为nHibernate不支持...

我发现运行第一个Linq查询的唯一方法是在映射中指定公式:

Map(p => p.HasPublished).Formula("cast (HasPublished as int)");

现在这个公式将应用于所有select语句,语句将如下所示:

select paper0_.Author as col_0_0_, max(cast (paper0_.HasPublished as int)) as col_1_0_ 
from [Paper] paper0_ 
group by paper0_.Author

无论如何,你已经找到了解决方案,并且以下实际上是相同的而不需要公式

var criteria = session.CreateCriteria<Paper>();
criteria.SetProjection(
    Projections.Group<Paper>(p=>p.Author),
    Projections.Max(
        Projections.Cast(NHibernateUtil.Int32, Projections.Property("HasPublished")))
    );
var result = criteria.List();

但也许我们都学到了一些东西;)