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

私は、この Projection ビジネスを間違って構築したことを 100% 確信していますが、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 に答える