簡體   English   中英

EF4代碼優先-多對多關系問題

[英]EF4 Code First - Many to many relationship issue

將一個關系保存為多對多關系時,我的EF Code First模型遇到了一些麻煩。 我的模特:

public class Event
{
    public int Id { get; set; }
    public string Name { get; set; }  
    public virtual ICollection<Tag> Tags { get; set; }
}


public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Event> Events { get; set; }
}

在我的控制器中,我將一個或多個TagViewModels映射為Tag類型,然后將其發送到我的服務層以實現持久性。 目前,通過檢查實體,標記同時具有ID和名稱 (在我的視圖中,ID為隱藏字段,名稱為文本框)

當我現在嘗試將標簽添加到事件中時,會出現問題。 讓我們來看看以下情形:

該事件已經存在於我的數據庫中,並且說它已經具有相關的標簽C#, ASP.NET

如果現在將以下標簽列表發送到服務層:

ID  Name
1   C#
2   ASP.NET
3   EF4

並通過首先從數據庫中獲取事件來添加它們,以便從DbContext獲得實際的事件,然后我只需

myEvent.Tags.Add

問題是在SaveChanges()我的數據庫現在包含這組標記:

ID  Name
1   C#
2   ASP.NET
3   EF4
4   C#
5   ASP.NET

即使我保存的標簽在保存時設置了它的ID(盡管我沒有從數據庫中獲取它)

我想我知道您的代碼中發生了什么。 讓我用一個簡單的例子來解釋我的觀點:

using (var context = new Context())
{
    // Just let assume these are your tags received from view model.
    var csharp = ...;
    var aspnet = ...;
    var ef4 = ...;

    // Now you load Event
    var e = context.Events.Where(e => e.Id == someId);
    // Ups first access to Tag collection triggers lazy loading which
    // is enabled by default so, all current tags are loaded
    e.Tags.Add(csharp);
    e.Tags.Add(aspnet);
    e.Tags.Add(ef4);

    // Now e.Tags.Count == 5 !!! Why?
    context.SaveChanges();
}

第一個問題:因為在Event實例頂部創建的動態代理使用HashSet作為Tags所以它檢查集合中是否已存在添加的實體。 如果不是,則添加該實體,否則跳過該實體。 為了能夠正確執行此檢查, 您必須在Tag中實現Equals等於GetHashCode 因此,因為您沒有執行此操作,所以它將添加的標簽作為帶有臨時鍵的新標簽,並使用自動生成的鍵將它們添加到Tags表。

第二個問題:即使實現EqualsGetHashCode您也只能解決C#和ASP.NET標記的重復性。 目前,上下文尚未跟蹤EF4標簽,因此該標簽仍被視為新標簽。 您必須通知上下文EF4標簽存在於數據庫中。 因此,在觸發“ Tags集合上的延遲加載之前,讓“ Attach所有標記Attach到上下文”。 默認情況下,將實體附加到上下文Unchanged其狀態設置為Unchanged

using (var context = new Context())
{
    foreach (var tag in TagsFromView)
    {
        context.Attach(tag);
    }

    // Now you load Event
    var e = context.Events.Where(e => e.Id == someId);
    foreach(var tag in TagsFromView)
    {
        // First access will trigger lazy loading but already
        // attached instances of tags are used
        e.Tags.Add(tag);
    }

    // Now you must delete all tags present in e.Tags and not
    // present in TagsFromView

    context.SaveChanges();
}

如果您未在視圖中創建新標簽,則此方法有效。 如果您也想這樣做,則一定不要在上下文中附加新標簽。 您必須在現有標簽和新標簽之間進行區分(例如,新標簽可以具有Id = 0)。

您需要從數據庫獲取標簽,否則EF會將其視為新項目並覆蓋ID。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM