2012-07-05 95 views
2

我想用EF代碼先創建一個多對多關係的數據庫。 ASP MVC多對多的關係 - 從關聯表中檢索數據

public class Item 
{ 
    public int ItemId { get; set; } 
    public String Description { get; set; } 
    public ICollection<Tag> Tags { get; set; } 

    public Item() 
    { 
     Tags = new HashSet<Tag>(); 
    } 
} 

public class Tag 
{ 
    public int TagId { get; set; } 
    public String Text { get; set; } 
    public ICollection<Item> Presentations { get; set; } 

    public Tag() 
    { 
     Presentations = new HashSet<Item>(); 
    } 
} 

public class ItemsEntities : DbContext 
{ 
    public DbSet<Item> Items { get; set; } 
    public DbSet<Tag> Tags { get; set; } 
} 

在那之後,我將項目添加到數據庫

var tag = new Tag { Text = "tag1" }; 
var item = new Item 
{ 
    Description = "description1", 
    Tags = new List<Tag>() 
}; 
item.Tags.Add(tag); 
using (var db = new ItemsEntities()) 
{ 
    db.Items.Add(item); 
    db.SaveChanges(); 
} 

的問題是,我不能輸出的項目及其關聯的標記。控制器看起來是這樣的:

public ActionResult Index() 
{ 
    ItemsEntities db = new ItemsEntities(); 
    return View(db.Items.ToList()); 
} 

和視圖頁面有以下代碼:

@foreach (var item in Model) 
{ 
    <tr> 
    <td> 
     @Html.DisplayFor(model => item.Description) 
    </td> 
    <td> 
     @foreach (var tag in item.Tags) 
     { 
      @tag.Text 
     } 
    </td> 
</tr> 
} 

我期望表含有「說明1」和「標籤1」,但我得到的只有「內容描述」 。我真的不明白問題在哪裏。什麼是正確的方法來做到這一點?

回答

2

您的導航屬性需要標記爲virtual

public class Item 
{ 
    public int ItemId { get; set; } 
    public String Description { get; set; } 
    public virtual ICollection<Tag> Tags { get; set; } 

    public Item() 
    { 
     Tags = new HashSet<Tag>(); 
    } 
} 

public class Tag 
{ 
    public int TagId { get; set; } 
    public String Text { get; set; } 
    public virtual ICollection<Item> Presentations { get; set; } 

    public Tag() 
    { 
     Presentations = new HashSet<Item>(); 
    } 
} 
+0

非常感謝!這正是問題出在哪裏 –

+0

沒問題,這是一個常見的首要疑難問題。請注意,非收集導航屬性也是這種情況。 – danludwig

+0

-1用於引入SELECT N + 1問題。 – saintedlama

1

爲了使您的代碼的工作,你可以通過@danludwig陳述virtual標記您的收藏屬性。通過將集合屬性標記爲virtual當遍歷視圖中的項目時,EF Code First將惰性加載這些屬性。使用這種方法會遇到SELECT N + 1問題。讓我們來看看您的視圖代碼:

@foreach (var item in Model) 
{ 
    <tr> 
    <td> 
     @Html.DisplayFor(model => item.Description) 
    </td> 
    <td> 
     @foreach (var tag in item.Tags) 
     { 
     @tag.Text 
     } 
    </td> 
    </tr> 
} 

在這個foreach循環您遍歷在模型中使用EF數據內選定的所有項目。

db.Items.ToList() 

這是您的第一個選擇。但在上面的視圖中,每次訪問項目的Tags屬性時,都會執行另一個選擇。對於每個項目而言,重要的是。這意味着如果您在db.ItemsDbSet中有100 Items,那麼您將執行101次選擇。對大多數系統來說這是不可接受的。

更好的方法是預選每個項目的標籤。一種方法是使用Include或選擇與項目相關的標籤到專用對象中。

public class ItemWithTags 
{ 
    public Item Item { get;set; } 
    public IEnumerable<Tag> Tags { get;set; } 
} 

public ActionResult Index() 
{ 
    ItemsEntities db = new ItemsEntities(); 

    var itemsWithTags = db.Items.Select(item => new ItemWithTags() { Item = item, Tags = item.Tags}); 
    return View(itemsWithTags.ToList()); 
} 

在你看來,你可以遍歷itemsWithTags收集,獲取項目的屬性和標籤爲您訪問的ItemWithTagsTags財產。

您的代碼的另一個問題是,ItemsEntitiesDbContext在您的代碼中打開但從未關閉。您可以使用VS MVC模板生成一個控制器,該控制器可以正確處理DbContext打開和關閉!

您可以使用像MVC Mini Profiler這樣的工具來檢查對數據庫執行的命令。此Stackoverflow Question顯示如何使用EF Code First設置MVC Mini Profiler。

+0

-1用於引入無法對導航屬性使用延遲加載的實體模型。 – danludwig

+0

感謝您的回答,我會牢記這一點。你能解釋關於關閉ItemsEntities的部分嗎?我認爲'使用(var db = new ItemsEntities()){}'應該在使用ItemsEntities後處理處理 –

+0

在您的代碼中添加新項目時正確關閉ItemEntities,但在Controller.Index中查詢這些項目時沒有正確關閉ItemEntities '方法 – saintedlama

相關問題