3

これまでのところ、私にとって2週間の戦いでしたが、運がありませんでした。:(

まず私の目的を述べさせてください。「foo」と「bar」のタグが付いたエンティティを検索できるようにします。それは大変なことだと思いませんか?

これは HQL で簡単に実行できることはわかっていますが、これはオプションではない動的に構築された検索クエリであるためです。最初のいくつかのコード:

public class Foo 
{ 
     public virtual int Id { get;set; } 
     public virtual IList<Tag> Tags { get;set; } 
} 

public class Tag 
{ 
     public virtual int Id { get;set; } 
     public virtual string Text { get;set; } 
} 

Tag クラスはさまざまな型で使用されるため、多対多としてマップされます。したがって、双方向参照はありません。

そこで、抽象フィルター クラスを使用して分離基準を構築します。簡単にするために、タグ "Apples"(TagId1) && "Oranges"(TagId3) を持つ Foos を検索していると仮定しましょう。これは次のようになります。

SQL:

SELECT ft.FooId
FROM Foo_Tags ft
WHERE ft.TagId IN (1, 3)
GROUP BY ft.FooId
HAVING COUNT(DISTINCT ft.TagId) = 2; /*Number of items we are looking for*/

基準

var idsIn = new List<int>() {1, 3};
var dc = DetachedCriteria.For(typeof(Foo), "f"). 
           .CreateCriteria("Tags", "t") 
           .Add(Restrictions.InG("t.Id", idsIn)) 
           .SetProjection( Projections.ProjectionList() 
              .Add(Projections.Property("f.Id")) 
              .Add(Projections.RowCount(), "RowCount") 
              .Add(Projections.GroupProperty("f.Id"))) 
          .ProjectionCriteria.Add(Restrictions.Eq("RowCount", idsIn.Count)); 
} 
var c = Session.CreateCriteria(typeof(Foo)).Add(Subqueries.PropertyIn("Id", dc)) 

基本的に、これは指定されたすべてのタグを持つ Foo Id のリストを投影する DC を作成しています。

これは NH 2.0.1 でコンパイルされましたが、クラス Foo でプロパティ "RowCount" が見つからないと不平を言ったため、機能しませんでした。

この投稿を読んだ後、これが 2.1.0 で修正されることを期待したので、アップグレードしました。非常に残念なことに、ProjectionCriteria が DetachedCriteria から削除されており、DetachedCriteria なしで動的クエリの構築を機能させる方法がわかりません。

そこで、悪名高い Having 句を使用せずに同じクエリを作成する方法を考えてみました。タグテーブルで複数の結合を使用して実行できます。やったー、それはとても簡単だと思いました。ということで、こんな感じに書き直しました。

var idsIn = new List<int>() {1, 3};
var dc = DetachedCriteria.For(typeof(Foo), "f"). 
           .CreateCriteria("Tags", "t1").Add(Restrictions.Eq("t1.Id", idsIn[0]))
           .CreateCriteria("Tags", "t2").Add(Restrictions.Eq("t2.Id", idsIn[1]))

仕事をする以下のSQLを作成しようとする無駄な試みで(私はそれが完全に正しくないことに気づきました)。

SELECT f.Id
FROM Foo f
JOIN Foo_Tags ft1
ON ft1.FooId = f.Id
   AND ft1.TagId = 1
JOIN Foo_Tags ft2
ON ft2.FooId = f.Id
   AND ft2.TagId = 3

残念ながら、この試みで最初のハードルに落ち、「重複した関連付けパス」という例外を受け取りました。この辺りを読むと、古くて今でも非常に現実的なバグ/制限のようです。

私は何が欠けていますか?

私は、NHibernates の名前を呪い始めています。これは、あなたが非常に単純で一般的なクエリだと思うものを、非常に難しいものにしています。以前にこれを行ったことがある人を助けてください。NHibernates の制限をどのように回避しましたか。

評判と報奨金を忘れてください。誰かが私にこれをしっかりしてくれたら、あなたの問題のために6パックを送ります.

4

2 に答える 2

1

私はそれを次のように機能させることができました:

var dc = DetachedCriteria.For<Foo>( "f")
            .CreateCriteria("Tags", "t")
            .Add(Restrictions.InG("t.Id", idsIn))
            .SetProjection(Projections.SqlGroupProjection("{alias}.FooId", "{alias}.FooId having count(distinct t1_.TagId) = " + idsIn.Count,
                         new[] { "Id" }, 
                           new IType[] { NHibernateUtil.Int32 }));

ここでの唯一の問題は count( t1_ .TagId) ですが、この DetachedCriteria では毎回同じようにエイリアスを生成する必要があると思います。

于 2009-08-15T18:50:20.597 に答える
0

イアン、

使用しているdbバックエンドがわからないので、生成されたSQLクエリに対して何らかのトレースを実行し、SQLを調べて、何が問題だったのかを把握できますか?

Linq-2-SQLとLinq-2-Entitiesがどのように機能しているかを理解し、データアクセスを改善するために特定のケースを微調整し、何かがうまくいかなかった理由を理解するために、過去にこれを行ったことがあることを知っています。当初の期待どおりに機能します。

于 2009-08-15T11:52:09.153 に答える