2

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

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。我很想得到这个特殊问题的帮助,或者一个链接到一个关于这里实际发生的事情的好文章的链接。

非常感谢你!

4

1 回答 1

0

I hope I understood your issue correctly, so you just want to get all Authors which have at least one paper which has this flag set to true, right?

Why do you not just use Linq, its way easier to write and should work for such simple scenarios. I would also map your flag to a bool, so I guess there is no need to do a Max operation at all... Example:

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

I used this simple scenario, let me know if this doesn't match your issue:

Entity + Mapping:

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);
    }
}

Creation of some test data and the query

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();
}

It actually returns me exactly those authors... You could process this further to have just a distinct result...

:edit starts: to query all authors with the maximum value of the flag, it becomes a little bit tricky with linq, the following linq query would return that result:

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

But if c.HasPublished is a bit field in SqlServer, it will give you the sql exception that max is not allowed on bit fields.

Trying to convert the bool to int wihtin the linq statement like

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

will throw an exception Code supposed to be unreachable, because it is not supported by nHibernate...

The only way I found to get the first Linq query running is by specifying a formula within the mapping:

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

Now this formula will be applied to all select statements, statement will look like this:

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

Anyways you already found the solution and the following does actually the same without the need of a formula

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();

But maybe we both learned something ;)

于 2013-10-01T16:12:37.473 回答