0

我有一个代表 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<long> VoterIds { get; protected set; }
}

我正在尝试在 NHibernate 中运行一个查询,该查询选择一个带有附加列的推文列表,该列表示特定用户的 UserId 是否为每条推文投票。当我用户为一条推文投票时,它存储在上面的“VoterIds”集合中。

我为此使用了一组值类型,因为我只对 Twitter UserId 真正感兴趣,以确定用户是否已经为特定推文投票。因此为什么它是一个ISet<long>而不是ISet<Vote>

我正在尝试使用这样的预测:

long userId = 123;

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( //---- WHAT GOES HERE!!??
    .SetResultTransformer(Transformers.AliasToBean<TweetReport>())
    .List<TweetReport>();

我认为正确的方法是使用 Projections.Conditional,但我不确定如何使用它。有人可以帮我填写//---- WHAT GOES HERE!!??上面代码中的内容吗?

我尝试使用 Expressions.In:

.Add(Projections.Conditional(Expressions.In("VoterIds", new object[] { userId }),
    Projections.Constant(true), Projections.Constant(false)))

...但它给了我一个“不能使用 InExpression 的集合”错误。请帮忙!


更新:我开始认为根本不可能查询值类型的集合,我应该使用像这样的成熟实体:

public virtual ISet<Vote> Votes { get; protected set; }

……会是这样吗?

4

1 回答 1

1

你可以这样做,但是修改域模型以绕过 NHibernate 的限制对灵魂来说是痛苦的。可以使用 HQL 查询值集合,但 ICriteria 对于使用逻辑构造查询非常方便。我知道如何使用 ICriteria 查询值集合的唯一方法是使用自定义 SQL。这也很痛苦,并且将您的代码与您的数据库联系在一起(!),但对我来说,这是三害中较小的一个。我的理由是 ICriteria 最终将允许这种查询,并且可以在以后重构痛苦。

诀窍是在自定义 SQL 中使用子查询,以便可以连接到集合表。使用不会踩到 NHibernate 别名的表别名也是一个好主意(在本例中为 custom_sql_t_v)。并注意 {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>

这是使用 T-SQL(即 Microsoft SQL Server)修改后的查询...

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

NHibernate 生成的最终 SQL(我使用 NHibernate 2.1.2.4000)看起来像这样......

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 无法做到这一点。

于 2010-10-27T10:36:16.460 回答