2011-11-27 55 views
2

我在任務和標記之間建立了一個簡單的多對多關係。一項任務可能有許多標籤,一個標籤可能被分配給許多任務。我使用三個表來管理這個 - 任務,標籤和任務標籤。到現在爲止還挺好。NHibernate多對多條件任務標記示例

我試圖寫下面的示例條件查詢...

Find all tasks that are tagged with 'Apple' (id = 1718) AND 'Orange' (id = 1717) 

我發現下面的例子中,我試圖效仿。

NHibernate many-to-many criteria

// Create sample list of tag ids. 
var tagIDs = new List<long>(); 
tagIDs.Add(1718); 
tagIDs.Add(1717); 

// Create criteria and detached criteria. 
var criteria = Session.CreateCriteria<Task>(); 
var detachedCriteria = DetachedCriteria.For<Task>("t") 
    .SetProjection(Projections.GroupProperty(Projections.Id())) 
    .Add(Restrictions.Eq(Projections.Count(Projections.Id()), tagIDs.Count)) 
    .Add(Restrictions.EqProperty("t.ID", "ID")) 
    .CreateCriteria("Tags") 
    .Add(Restrictions.In("id", tagIDs.ToArray<long>())); 
criteria.Add(Subqueries.Exists(detachedCriteria)); 

這是它產生的SQL ...

SELECT this_.ID as ID4_0_, [MORE COLS HERE] 
FROM dbo.[Task] this_ 
WHERE exists (
    SELECT this_0_.ID as y0_ 
    FROM dbo.[Task] this_0_ 
    inner join dbo.TaskTag tags3_ on this_0_.ID=tags3_.TaskID 
    inner join dbo.[Tag] tag1_ on tags3_.TagID=tag1_.ID 
    WHERE this_0_.ID = this_0_.ID 
    and tag1_.ID in (2, 1718)     <------ 1 
    GROUP BY this_0_.ID 
    HAVING count(this_0_.ID) = 1717    <------ 2 
) 

出於某種原因,我不能清楚地瞭解,這些參數都得到錯誤的。

  1. 注意,它把數(2)在「範圍」列表中不正確,並
  2. 然後它把第二標籤ID其中計數應該去。

當我修改SQL將參數放在正確的位置,它的工作原理。

我必須有標準不正確,但我看不到它。

我很感激任何幫助。謝謝!

回答

2

這裏是我的解決方案

實體:

public class TaskTest : Entity<TaskTest> 
{ 
    public virtual string Name { get; set; } 

    public virtual IList<TagTest> Tags { get; set; } 

    public TaskTest() 
    { 
     Tags = new List<TagTest>(); 
    } 
} 

public class TagTest : Entity<TagTest> 
{ 
    public virtual string Name { get; set; } 
} 

不要採取在考慮實體<>。是否是通用的,因爲我們已經實現了一個IEquatable接口

下一個映射:

public class TaskTestMap : EntityMap<TaskTest> 
{ 
    public TaskTestMap() 
    { 
     Map(x => x.Name).Not.Nullable().Length(512); 
     HasManyToMany(x => x.Tags).Table("TaskTag").Cascade.All(); 
    } 
} 

public class TagTastMap : EntityMap<TagTest> 
{ 
    public TagTastMap() 
    { 
     Map(x => x.Name).Not.Nullable(); 
    } 
} 

我設置級聯只是爲了節省減排任務tasgs。

與我的標準存儲庫:這傳給我身邊

public IList<TaskTest> GetTaskByTagIds(IList<long> tagIds) 
    { 
     DetachedCriteria exists = DetachedCriteria.For<TaskTest>("t") 
      .CreateAlias("t.Tags", "tags") 
      .Add(Restrictions.EqProperty("t.Id", "task.Id")) 
      .Add(Restrictions.In("tags.Id", tagIds.ToArray())) 
      .SetProjection(Projections.GroupProperty("t.Id")) 
      .Add(Restrictions.Eq(Projections.Count("t.Id"), tagIds.Count)); 

     ICriteria criteria = GetSession().CreateCriteria<TaskTest>("task") 
      .Add(Subqueries.Exists(exists)); 

     return criteria.List<TaskTest>(); 
    } 

而且單元測試:

[Test] 
    public void TestTaskTest() 
    { 
     //Insert data; 

     var task1 = new TaskTest {Name = "task1"}; 
     var task2 = new TaskTest {Name = "task2"}; 
     var task3 = new TaskTest {Name = "task3"}; 

     var tag1 = new TagTest {Name = "tag1"}; 
     var tag2 = new TagTest {Name = "tag1"}; 
     var tag3 = new TagTest {Name = "tag1"}; 

     task1.Tags.Add(tag1); 
     task1.Tags.Add(tag2); 

     task2.Tags.Add(tag1); 
     task2.Tags.Add(tag3); 

     task3.Tags.Add(tag3); 


     _repository.AddToSession(task1); 
     _repository.AddToSession(task2); 
     _repository.AddToSession(task3); 

     FlushAndClearSession(); 

     //We will try to get all task which a taged with tag1 and tag2. The result should be task1 

     var tagsId = new List<long> {tag1.Id, tag2.Id}; 

     var result = _repository.GetTaskByTagIds(tagsId); 

     Assert.That(result, Is.Not.Null); 
     Assert.That(result, Is.Not.Empty); 
     Assert.That(result.Count, Is.EqualTo(1)); 
     Assert.That(result[0].Name, Is.EqualTo("task1")); 
    } 

和SQL語句是:

exec sp_executesql N'SELECT this_.Id as Id50_0_, this_.Version as Version50_0_, this_.Name as Name50_0_ 
    FROM [TaskTests] this_ 
    WHERE exists (
        SELECT this_0_.Id as y0_ 
        FROM [TaskTests] this_0_ 
        inner join TaskTag tags3_ on this_0_.Id=tags3_.TaskTestId 
        inner join [TagTests] tags1_ on tags3_.TagTestId=tags1_.Id 
        WHERE this_0_.Id = this_.Id and tags1_.Id in (@p0, @p1) 
        GROUP BY this_0_.Id 
        HAVING count(this_0_.Id) = @p2)', 
N'@p0 bigint,@p1 bigint,@p2 int', 
@p0=9000,@p1=9001,@p2=2 

問候, /Ion

+0

謝謝!我只是看不到問題。使用CreateAlias比CreateCriteria更清晰。 – Kevin