Ошибка проекции 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, не загружая всю коллекцию, если именно для этого вы проецируете каждый столбец. Есть довольно много действительных статей, которые помогут вам отметить коллекцию как очень ленивую.