Ошибка проекции NHibernate

У меня есть класс тегов, который имеет отношение многие ко многим с классом статьи. Проблема в том, что я хочу создать проекцию, представляющую класс модели представления тегов, поэтому результат столбца должен выглядеть следующим образом

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

SQL-запрос должен быть таким:

 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

но сказано

  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

это мой класс Tag:

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

это класс отображения

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

}

это класс ViewModel:

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

Вот мой код для проекции:

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

Я впервые использую проекции, и я понятия не имею, как заставить это работать. Я что-то здесь пропустил?

2 ответа

Решение

После прочтения NHIibernate API и ответа на эту ссылку, наконец, я нашел способ заставить его работать.

Вот мой окончательный код:

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

Мне нужно использовать.SelectList вместо.Select для проецирования списка, а также для того, чтобы nhibernate успешно посчитал статьи, которые мне нужны, чтобы объединить его со связанной таблицей, чтобы ускорить мой запрос также в классе отображения, инициализировать статьи тегов как свойство ExtraLazyLoad. Надеюсь, я также помогу кому-то, кто сталкивается с той же проблемой.

Полученная ошибка говорит о том, что SQL, сгенерированный NHibernate для проекций, не может быть выполнен в его текущей форме. Запрос, являющийся

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 

Это просто из-за того, что для подсчета агрегации необходима функция агрегирования.

Вы можете пометить коллекцию Articles как ленивую и лишнюю и получить значение Article.Count, не загружая всю коллекцию, если именно для этого вы проецируете каждый столбец. Есть довольно много действительных статей, которые помогут вам отметить коллекцию как очень ленивую.

Другие вопросы по тегам