2017-05-28 85 views
1

根據其他問題(herehere),可以通過捕獲拋出的異常並檢查它是否爲InnerException來捕獲Entity Framework 6中的唯一密鑰違規。處理EntityFramework核心中的重複密鑰違規

當重複組數據調用DbContext.SaveChanges(),拋出一個異常,但它是一個相當標準的InvalidOperationException,它的InnerExceptionnull

如何檢測實體框架核心中的重複鍵違規?

有更多的上下文(雙關語意)更新

具體違反我試圖趕上/檢測增加是當兩個實體(團隊和用戶),由多到加入之間的聯繫多種關係。

System.InvalidOperationException:無法跟蹤實體類型'TeamUser'的實例,因爲具有相同鍵的此類型的另一個實例已被跟蹤。添加新實體時,對於大多數鍵類型,如果未設置鍵(即,如果鍵屬性被指定爲其類型的默認值),則將創建唯一的臨時鍵值。如果您明確地爲新實體設置關鍵值,請確保它們不與現有實體衝突或爲其他新實體生成的臨時值衝突。在附加現有實體時,確保只有具有給定鍵值的一個實體實例附加到上下文。

用戶實體類:

public class User 
{ 
    [Key] 
    public string Name { get; set; } 

    public ICollection<TeamUser> TeamUsers { get; set; } 
} 

團隊實體類:

public class Team 
{ 
    [Key] 
    public string Id { get; set; } 

    [Required] 
    public string Name { get; set; } 

    public ICollection<Template> Templates { get; set; } 
    public ICollection<Checklist> Checklists { get; set; } 

    public ICollection<TeamUser> TeamUsers { get; set; } 
} 

TeamUser實體類:

public class TeamUser 
{ 
    public string TeamId { get; set; } 
    public Team Team { get; set; } 

    public string UserName { get; set; } 
    public User User { get; set; } 
} 

DbContext子類配置團隊和用戶之間的許多一對多的關係:

protected override void OnModelCreating(ModelBuilder modelBuilder) 
{ 
    var teamUserEntity = modelBuilder.Entity<TeamUser>(); 

    teamUserEntity 
     .HasKey(tu => new { tu.TeamId, tu.UserName }); 

    teamUserEntity 
     .HasOne(tu => tu.Team) 
     .WithMany(t => t.TeamUsers) 
      .HasForeignKey(tu => tu.TeamId); 

    teamUserEntity 
     .HasOne(tu => tu.User) 
     .WithMany(u => u.TeamUsers) 
     .HasForeignKey(tu => tu.UserName); 
} 

EF核心已經產生了TeamUser表如下:

CREATE TABLE "TeamUser" (
    "TeamId" TEXT NOT NULL, 
    "UserName" TEXT NOT NULL, 
    CONSTRAINT "PK_TeamUser" PRIMARY KEY ("TeamId", "UserName"), 
    CONSTRAINT "FK_TeamUser_Teams_TeamId" FOREIGN KEY ("TeamId") REFERENCES "Teams" ("Id") ON DELETE CASCADE, 
    CONSTRAINT "FK_TeamUser_Users_UserName" FOREIGN KEY ("UserName") REFERENCES "Users" ("Name") ON DELETE CASCADE 
); 
CREATE INDEX "IX_TeamUser_UserName" ON "TeamUser" ("UserName"); 
+0

你是否在你的數據庫上下文中設置了獨特的約束條件? –

+0

我的意思是這樣的:entity.HasAlternateKey(p => new {p.ProductName})。HasName(「U_ProductName」); –

+0

@ H.Herzl請檢查更新的問題,我添加了相關的實體類和'OnModelCreating'方法。 –

回答

1

的原因而無法檢測重複密鑰違規是因爲您正在使用一個dbcontext實例來保存重複數據。讓我用一個樣本控制器解釋:

MyController.cs

public class MyController : Controller 
{ 
    private readonly MyDbContext _context; 

    public MyController(MyDbContext context) 
    { 
     _context = context; 
    } 

    public IActionResult AddFirst() 
    { 
     var user = new User 
     { 
      Name = "Alice" 
     }; 
     _context.Users.Add(user); 

     var team = new Team 
     { 
      Id = "uniqueteamid", 
      Name = "A Team" 
     }; 
     _context.Teams.Add(team); 

     var teamuser1 = new TeamUser() 
     { 
      User = user, 
      Team = team 
     }; 
     _context.TeamUsers.Add(teamuser1); 

     _context.SaveChanges(); 

     return View(); 
    } 

    public IActionResult AddSecond() 
    { 
     var teamuser2 = new TeamUser() 
     { 
      UserName = "Alice", 
      TeamId = "uniqueteamid" 
     }; 
     _context.TeamUsers.Add(teamuser2); 

     _context.SaveChanges(); 

     return View(); 
    } 

    public IActionResult AddFirstAndSecond() 
    { 
     var user = new User 
     { 
      Name = "Bob" 
     }; 
     _context.Users.Add(user); 

     var team = new Team 
     { 
      Id = "anotherteamid", 
      Name = "B Team" 
     }; 
     _context.Teams.Add(team); 

     var teamuser1 = new TeamUser() 
     { 
      User = user, 
      Team = team 
     }; 
     _context.TeamUsers.Add(teamuser1); 

     var teamuser2 = new TeamUser() 
     { 
      User = user, 
      Team = team 
     }; 
     _context.TeamUsers.Add(teamuser2); 

     _context.SaveChanges(); 

     return View(); 
    } 

    public IActionResult AddFirstAndSecondAgain() 
    { 
     var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>(); 
     optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=aspnet-WebApplication1;Trusted_Connection=True;MultipleActiveResultSets=true"); 

     using (var context = new MyDbContext(optionsBuilder.Options)) 
     { 
      var user = new User 
      { 
       Name = "Cat" 
      }; 
      context.Users.Add(user); 
      context.SaveChanges(); 
     } 

     using (var context = new MyDbContext(optionsBuilder.Options)) 
     { 
      var team = new Team 
      { 
       Id = "andanotherteamid", 
       Name = "C Team" 
      }; 
      context.Teams.Add(team); 
      context.SaveChanges(); 
     } 

     using (var context = new MyDbContext(optionsBuilder.Options)) 
     { 
      var teamuser1 = new TeamUser() 
      { 
       UserName = "Cat", 
       TeamId = "andanotherteamid" 
      }; 
      context.TeamUsers.Add(teamuser1); 
      context.SaveChanges(); 
     } 

     using (var context = new MyDbContext(optionsBuilder.Options)) 
     { 
      var teamuser2 = new TeamUser() 
      { 
       UserName = "Cat", 
       TeamId = "andanotherteamid" 
      }; 
      context.TeamUsers.Add(teamuser2); 
      context.SaveChanges(); 
     } 

     return View(); 
    } 
} 

在此控制器,有4種動作方法:AddFirstAddSecondAddFirstAndSecondAddFirstAndSecondAgain

病例1(AddFirstAddSecond):

AddFirst假設首先調用。這將創建一個新的用戶,一個新的團隊和一個TeamUser。現在如果AddSecond之後被調用,這將嘗試添加重複的TeamUser並拋出重複的鍵違例異常。原因是插入重複TeamUser的第二次調用是使用不同於第一次調用插入TeamUser的dbcontext實例。

案例2(AddFirstAndSecond):

假設你叫AddFirstAndSecond。這會引發無效的操作異常。 爲什麼?因爲您正在使用dbcontext的單個實例來添加第一個TeamUser和第二個重複的TeamUser。實體框架核心已經在跟蹤第一個TeamUser,因此它不能跟蹤第二個重複的TeamUser。

案例3(AddFirstAndSecondAgain):

如果你真的需要在一個單一的操作方法添加重複TeamUser,你需要使用的DbContext的不同實例添加每個TeamUser時。看看AddFirstAndSecondAgain的操作方法。這也會引發重複的鍵違例異常,因爲您正在使用不同的dbcontext實例來添加第一個和第二個重複的TeamUser。

+0

道歉,但我沒有真正遵循你所建議的我需要做的事情。你可以看一下[示例項目](https://github.com/NxSoftware/EFCoreDuplicateRecord)我推送到Github並給出更多的指針? –

+0

我更新了我的答案。希望能幫助到你。 – kimbaudi

+0

當你使用'Add'方法向DbContext添加內容時,上下文開始跟蹤它(以監視當調用SaveChanges時是否需要保存什麼)。如果你嘗試多次添加同一個實例到上下文中,那麼EF拋出異常,它不能跟蹤新的實體。這就像在單個DbContext實例中跟蹤實體內發生的唯一約束違規。作爲一個指針,你應該尋找異常發生的位置,以及在同一個上下文實例中兩次添加的實體是什麼。這將幫助您找到添加了兩次的內容。 – Smit