NHibernate-从值类型集合(非实体)查询以解决选择N+1问题
我有一个实体表示来自Twitter的推文,如下所示:NHibernate-从值类型集合(非实体)查询以解决选择N+1问题,nhibernate,nhibernate-projections,Nhibernate,Nhibernate Projections,我有一个实体表示来自Twitter的推文,如下所示: public class Tweet { public virtual long Id { get; set; } public virtual string Username { get; set; } public virtual string Message { get; set; } // other properties (snip)... public virtual ISet<l
public class Tweet
{
public virtual long Id { get; set; }
public virtual string Username { get; set; }
public virtual string Message { get; set; }
// other properties (snip)...
public virtual ISet<long> VoterIds { get; protected set; }
}
…但它给了我一个“无法使用无表达式的集合”错误。请帮忙
更新:我开始认为根本不可能查询值类型的集合,我应该使用这样一个完整的实体:
public virtual ISet<Vote> Votes { get; protected set; }
…会是这样吗?您可以这样做,但是修改域模型以绕过NHibernate的限制对灵魂来说是痛苦的。使用HQL查询值集合是可能的,但是ICriteria对于使用逻辑构造查询非常方便。我知道如何使用ICriteria查询值集合的唯一方法是使用自定义SQL。这也很痛苦,而且会将代码绑定到数据库!,但对我来说,这是三害中较小的一害。我的理由是,ICriteria最终将允许这种查询,而痛苦可以在以后重构 诀窍是在自定义SQL中使用子查询,以便可以连接到集合表。在本例中,使用不涉及NHibernate别名的表别名也是一个好主意。注意{alias}和?NHibernate将替换掉的占位符 下面是一个基于假设的示例,您的Tweet类映射如下
<class name="Tweet" table="Tweet">
<id name="Id" unsaved-value="0">
<generator class="identity"/>
</id>
<version name="Version" unsaved-value="0"/>
<property name="UserName"/>
<property name="Message"/>
<set name="Votes" table="Tweet_Votes">
<key column="Tweet"/>
<element type="Int64" column="Vote"/>
</set>
</class>
exec sp_executesql N'SELECT this_.Id as y0_, this_.UserName as y1_, this_.Message as y2_, (case when EXISTS (SELECT 1 FROM [Tweet_Votes] custom_sql_t_v WHERE custom_sql_t_v.[Tweet] = this_.[Id] AND custom_sql_t_v.[Vote] = @p0) then @p1 else @p2 end) as y3_ FROM Tweet this_',N'@p0 bigint,@p1 char(1),@p2 char(1)',@p0=123,@p1='Y',@p2='N'
所有这一切的好处是,对字符串集合执行LIKE是可能的——我认为用HQL无法做到这一点
<class name="Tweet" table="Tweet">
<id name="Id" unsaved-value="0">
<generator class="identity"/>
</id>
<version name="Version" unsaved-value="0"/>
<property name="UserName"/>
<property name="Message"/>
<set name="Votes" table="Tweet_Votes">
<key column="Tweet"/>
<element type="Int64" column="Vote"/>
</set>
</class>
IList<TweetReport> tweets = Session.CreateCriteria<Tweet>()
.SetProjection(Projections.ProjectionList()
.Add(Projections.Id(), "Id")
.Add(Projections.Property("UserName"), "UserName")
.Add(Projections.Property("Message"), "Message")
.Add(Projections.Conditional(
Expression.Sql(
"EXISTS (SELECT 1 FROM [Tweet_Votes] custom_sql_t_v WHERE custom_sql_t_v.[Tweet] = {alias}.[Id] AND custom_sql_t_v.[Vote] = ?)",
userId,
NHibernateUtil.Int64),
Projections.Constant(true),
Projections.Constant(false)), "DidVote"))
.SetResultTransformer(Transformers.AliasToBean<TweetReport>())
.List<TweetReport>();
exec sp_executesql N'SELECT this_.Id as y0_, this_.UserName as y1_, this_.Message as y2_, (case when EXISTS (SELECT 1 FROM [Tweet_Votes] custom_sql_t_v WHERE custom_sql_t_v.[Tweet] = this_.[Id] AND custom_sql_t_v.[Vote] = @p0) then @p1 else @p2 end) as y3_ FROM Tweet this_',N'@p0 bigint,@p1 char(1),@p2 char(1)',@p0=123,@p1='Y',@p2='N'