简体   繁体   中英

NHibernate Projections failure

I have tag class which have relation many-to-many with article class, The problem is I want to make a projection which represent in tag view model class so the column result should be like this

Id|Name|CreatedBy|CreatedDate|LastModifiedBy|LastModifiedDate|ArticlesCount

SQL query should be like this:

 SELECT  t.*, COUNT(at.intTag) as ArticlesCount 
 FROM    dbo.TAG t
         LEFT OUTER JOIN dbo.ARTICLE_TAG at ON t.intID = at.intTag
         LEFT OUTER JOIN dbo.ARTICLE a ON at.intID = a.intID
 GROUP BY t.intID, t.vcName, t.intWeight, t.vcCreatedBy, t.dtCreated, t.vcLastMod, t.dtLastMod, t.btActive

but it said

  NHibernate.Exceptions.GenericADOException was caught
  HResult=-2146232832
  Message=could not execute query [ SELECT this_.intID as y0_, this_.vcName as y1_, this_.vcCreatedBy as y2_, this_.dtCreated as y3_, this_.vcLastMod as y4_, this_.dtLastMod as y5_, count(this_.intID) as y6_ FROM TB_TAG this_ WHERE this_.btActive = @p0 ]
  Name:cp0 - Value:True

this is my Tag class:

public class Tag {
    public virtual string Name { get; set; }     
    public virtual int Weight { get; set; }
    public virtual IList<Article> Articles { get; set; }
    public virtual string CreatedBy { get; set; }
    public virtual DateTime CreatedDate { get; set; }
    public virtual string LastModifiedBy { get; set; }
    public virtual DateTime LastModifiedDate { get; set; }
    public virtual bool IsActive { get; set; }
}

this is mapping class

internal sealed class TagMap : ClassMap<Tag>
{
    public TagMap()
    {
        Table("TB_TAG");

        Id(f => f.Id).Column("intID").GeneratedBy.Native();

        Map(f => f.Name).Column("vcName").Not.Nullable();
        Map(f => f.Weight).Column("intWeight").Not.Nullable();
        Map(f => f.IsActive).Column("btActive");
        Map(f => f.CreatedBy).Column("vcCreatedBy").Not.Update();
        Map(f => f.CreatedDate).Column("dtCreated").Not.Update();
        Map(f => f.LastModifiedBy).Column("vcLastMod");

        Version(f => f.LastModifiedDate).Column("dtLastMod");

        HasManyToMany(f => f.Articles).Table("S_ARTICLE_TAG")
                                      .ParentKeyColumn("intTag").ChildKeyColumn("intID")
                                      .Inverse();
    }

}

this is ViewModel class:

    public class TagView
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int ArticlesCount { get; set; }
        public virtual string CreatedBy { get; set; }
        public virtual DateTime CreatedDate { get; set; }
        public virtual string LastModifiedBy { get; set; }
        public virtual DateTime LastModifiedDate { get; set; }
        public virtual bool IsActive { get; set; }
    }

here is my code to make a projection :

Tag t = null;
tags = _session.QueryOver(() => t)
               .Select(Projections.Id().As("Id"),
                       Projections.Property(() => t.Name).As("Name"),
                       Projections.Property(() => t.CreatedBy).As("CreatedBy"),
                       Projections.Property(() => t.CreatedDate).As("CreatedDate"),
                       Projections.Property(() => t.LastModifiedBy).As("LastModifiedBy"),
                       Projections.Property(() => t.LastModifiedDate).As("LastModifiedDate"),
                       Projections.Count(() => t.Articles).As("ArticlesCount"))
               .TransformUsing(Transformers.AliasToBean<TagView>())
               .List<TagView>();

This is my first time using projections and I don't have any clue how to make it work. Did I have miss something here?

The error you are getting tells that the SQL generated by NHibernate for the projections cannot be executed in its current form. The query being

SELECT this_.intid        AS y0_, 
       this_.vcname       AS y1_, 
       this_.vccreatedby  AS y2_, 
       this_.dtcreated    AS y3_, 
       this_.vclastmod    AS y4_, 
       this_.dtlastmod    AS y5_, 
       Count(this_.intid) AS y6_ 
FROM   tb_tag this_ 
WHERE  this_.btactive = @p0 

This is simply due to the fact that count is a aggregation needs an aggregation function.

You could mark the collection of Articles as lazy and extra and fetch the Article.Count value without loading the entire collection if that is why you are projecting each column for. There are quite a few valid articles which can guide you to mark a collection as extra lazy .

After read NHIibernate API and the answer on this link , finally i found a way to make it work.

Here is my final code:

Tag t= null;
TagView tv = null;
tags = _session.QueryOver(() => t)
               .Left.JoinQueryOver(() => t.Articles, () => a)
               .SelectList(list => list
                                       .SelectGroup(() => t.Id).WithAlias(() => tv.Id)
                                       .SelectGroup(() => t.Name).WithAlias(() => tv.Name)
                                       .SelectGroup(() => t.CreatedBy).WithAlias(() => tv.CreatedBy)
                                       .SelectGroup(() => t.CreatedDate).WithAlias(() => tv.CreatedDate)
                                       .SelectGroup(() => t.LastModifiedBy).WithAlias(() => tv.LastModifiedBy)
                                       .SelectGroup(() => t.LastModifiedDate).WithAlias(() => tv.LastModifiedDate)
                                       .SelectCount(() => t.Articles).WithAlias(() => tv.ArticlesCount))
               .TransformUsing(Transformers.AliasToBean<TagView>())
               .List<TagView>();

I need to use .SelectList instead of .Select to projecting a list, also to make nhibernate succesfully counting the articles I need to join it with its related table, to speed up my query also in mapping class initialize tag articles as ExtraLazyLoad property. Hoping I also help someone out there who is facing same problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM