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

特定のユーザーが UserId で各ツイートに投票したかどうかを示す追加の列を含むツイートのリストを選択する NHibernate でクエリを実行しようとしています。ユーザーがツイートに投票すると、上記の「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} と ? に注意してください。Hibernate が交換するプレースホルダー。

これは、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 (NHibernate 2.1.2.4000 を使用) によって生成された最終的な SQL は次のようになります...

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 に答える