2017-08-10 111 views
0

實體框架插入我使用實體框架6,我不斷收到以下錯誤:現有的導航屬性的實體

Violation of PRIMARY KEY constraint 'PK_AssignmentType'. Cannot insert duplicate key in object 'dbo.AssignmentType'.

正如你看到我試圖將實體AssignmentType的狀態更改爲unchanged。當我將實體Assignment指定爲AssignmentType的外鍵,然後將導航屬性AssignmentType設置爲null時,我仍然得到相同的錯誤(在foreach循環中Assignments,而不是在Comments foreach循環中)。

實體框架在哪裏跟蹤實體AssignmentType?爲什麼它仍然認爲它是一個新的實體而不是現有的實體?

實體ReportingAssignment之間存在多對多關係。

[Route("")] 
    [HttpPost] 
    public IHttpActionResult Add(ReportingDTO data) 
    {    
     Reporting reporting = new Reporting { ID = Guid.NewGuid(), Date = DateTime.Now };      

     foreach (Assignment assignment in data.Assignments) 
     { 
      _db.Entry(assignment.AssignmentType).State = EntityState.Unchanged; 

      if (assignment.Reporting.Count > 0) 
      { 
       _db.Entry(assignment).State = EntityState.Added; 
       _db.Entry(assignment).State = EntityState.Modified; 
      } 

      reporting.Assignment.Add(assignment); 
     } 

     foreach (Comment comment in data.Comments) 
     { 
      comment.ID = Guid.NewGuid(); 
      comment.AssignmentID = comment.Assignment.ID; 
      comment.Assignment = null; 
      comment.ReportingID= reporting.ID; 
     } 

     using(var transaction = _db.Database.BeginTransaction()) 
     { 
      _db.Reporting.Add(reporting); 
      _db.Comments.AddRange(data.Comments); 
      _db.SaveChanges(); 

      transaction.Commit(); 
     } 

     return Ok(reporting); 
    } 

Reporting.cs

[Table("Reporting")] 
public partial class Reporting 
{ 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 
    public Reporting() 
    { 
     Assignment= new HashSet<Assignment>(); 
    } 

    [Key] 
    public Guid ID{ get; set; } 

    [Column(TypeName = "date")] 
    public DateTime Date{ get; set; } 

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
    public virtual ICollection<Assignment> Assignment{ get; set; } 
} 

Assignment.cs

[Table("Assignment")] 
public partial class Assignment 
{ 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 
    public Assignment() 
    { 
     Reporting = new HashSet<Reporting>(); 
    } 

    [Key] 
    public Guid OID { get; set; } 

    [Required] 
    [StringLength(100)] 
    public string Name { get; set; } 

    public Guid AssignmentTypeID { get; set; } 

    [Required] 
    [StringLength(100)] 
    public string Project { get; set; } 

    public bool Completed { get; set; } 

    [ForeignKey("AssignmentTypeID")] 
    public virtual AssignmentType AssignmentType { get; set; } 

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
    public virtual ICollection<Reporting> Reporting { get; set; } 
} 

AssignmentType

[Table("AssignmentType")] 
public partial class AssignmentType 
{ 
    [Key] 
    public Guid ID { get; set; } 

    [Required] 
    [StringLength(100)] 
    public string Name { get; set; }   
} 
+0

你有一個外鍵:Assignment.AssignmentTypeId? – Atlasmaybe

+0

是的,我願意。在ReportingDTO中,這個字段等於null。 – adiii4

+0

你可以發佈你的數據模型嗎? – Atlasmaybe

回答

1

嘗試是這樣的:

public IHttpActionResult Add(ReportingDTO data) 
{    
    Reporting reporting = new Reporting { ID = Guid.NewGuid(), Date = DateTime.Now };      

    foreach (Assignment assignment in data.Assignments) 
    { 
     if (assignment.ID != null) 
     { 
      _db.Assignments.Attach(assignment); 
      _db.Entry(assignment).State = EntityState.Modified; 
     } 
     else 
     { 
      assignment.ID = Guid.NewGuid(); 
     } 

     if (assignment.AssignmentType != null) 
     { 
      assignment.AssignmentTypeID = assignment.AssignmentType.ID; 
      assignment.AssignmentType = null; 
     } 
    } 

    reporting.Assignment.AddRange(data.Assignments); 

    foreach (Comment comment in data.Comments) 
    { 
     comment.ID = Guid.NewGuid(); 
     comment.AssignmentID = comment.Assignment.ID; 
     comment.Assignment = null; 
     comment.ReportingID = reporting.ID; 
    } 

    _db.Reporting.Add(reporting); 
    _db.Comments.AddRange(data.Comments); 
    _db.SaveChanges(); 

    return Ok(reporting); 
} 

除非您正在更新實體,否則您所有的Entry()操作都是無用的。在這種情況下,您必須在更新其狀態之前更新實體Attach()

另外,爲了回答你最初的問題:EF增加了一個新的AssignmentType,因爲它已經失去了現有的一個。您的控制器每次創建請求時都會構造,所以您的DbContext也是每次構建的。創建新的DbContext時,它不知道以前的操作,例如返回Assignment或其他(如果在查詢過程中指定AsNoTracking()也是如此)。換句話說,如果實體沒有被DbContext跟蹤,那麼EF將嘗試添加它。
爲了防止這種情況發生,您可以(而且確實應該)使用外鍵代替導航屬性。或者你可以每個已知的實體(但它是沒用的,如果你有外鍵)Attach()。對於ManyToMany關係,您必須在添加/更新新實體之前查詢數據庫,否則,您將擁有懷疑情緒。

或者,你也可以使用我所謂的「鏈接表」。這只是你們兩個主要班級之間的班級。 EF已經爲你所有的ManyToMany關係做了它(檢查你的數據庫)。 Basicaly,它會是這樣的:

public class A 
{ 
    public int Id {get; set;} 
    public virtual List<AB> ABs { get; set; } 
} 

public class B 
{ 
    public int Id {get; set;} 
    public virtual List<AB> BAs {get; set; } 
} 

public class AB 
{ 
    public int AId {get;set;} 
    public virtual A {get; set;} 

    public int BId {get;set;} 
    public virtual B {get; set;} 
} 

用這種方式,你可以用_db.ABs.Add(new AB{AId = 1, BId = 2})添加一個新的AB(或刪除一個),沒有采取其他的事情。

+0

如果我只添加if語句,然後將其添加到報告中(就像您所做的那樣),但我仍然遇到與上述相同的錯誤。另外我應該提到可以有新的作業或更新的作業。 – adiii4

+0

你的意思是你想要在一個方法中添加或更新賦值?加上報告和評論? – Atlasmaybe

+0

是的。用戶通過一個嚮導並最終保存包含所有這些數據的報告。 – adiii4