2017-03-05 46 views
4

我在我的HTTP請求發送一個JSON格式的用戶實體是這樣的:EF:實體類型X的實例無法被跟蹤,因爲這種類型的使用相同的密鑰的另一個實例已被跟蹤

POST http://localhost:52054/api/Authentication/DeleteAccessToken HTTP/1.1 
Host: localhost:52054 
Content-Type: application/json 

{"id":1,"userName":"mnoureldin","accessToken":{"id":1,"token":"123ABC456EFG","userId":1}} 

我的控制器(在EF-核心)處理,像這樣:

[HttpPost] 
public IActionResult DeleteAccessToken([FromBody]User user) 
{ 
    using (var Context = new UnitOfWork().Context) 
    { 
     var userEntity = Context.Users.Find(user.Id); // Get the real entity of the user received as json 
     if (userEntity != null) 
     { 
      var accessTokenEntity = Context.AccessTokens.Find(userEntity.AccessToken.Id); // Find the entity of the accesstoken (I tried also directly by accessing the navigation property of user entity) 
      Context.AccessTokens.Remove(accessTokenEntity); 
      return Ok(); 
     } 
     else 
     { 
      return Unauthorized(); 
     } 
    } 
} 

但行Context.AccessTokens.Remove(accessTokenEntity);拋出此異常:

類型的異常「System.InvalidOperationException」發生在 Microsoft.EntityFrameworkCore.dll但在用戶代碼中沒有處理

其他信息:實體類型的實例「的accessToken」 無法跟蹤,因爲的另一個實例這個類型與 相同,已被跟蹤。當添加新實體時,對於大多數鍵 類型,如果未設置密鑰 (即,如果鍵屬性被指定了其類型的默認值),則將創建唯一的臨時密鑰值。 如果您明確地爲新實體設置關鍵值,請確保它們 不會與其他新實體的現有實體衝突或產生的臨時值 。在附加現有實體時,請確保僅將具有給定鍵值的一個實體實例附加到 上下文中。

我也嘗試直接從userEntity訪問AccessToken導航屬性,同樣的異常。

這裏是我的UnitOfWork初始化:

public UnitOfWork() 
{ 
    // Configure EF connection 
    var optionsBuilder = new DbContextOptionsBuilder<CustomDbContext>(); 
    optionsBuilder 
     .UseMySQL(@"server=192.168.1.35; port=3306; sslmode=none; 
        userid=root; 
        [email protected]; 
        database=dotnet;"); 

    Context = new CustomDbContext(optionsBuilder.Options); 

    // Configure data loading method to explicit 
    Context.AccessTokens.Load(); 
} 

我CustomBdContext:

public class CustomDbContext : DbContext 
{ 
    // Tell EF to map the entities to tables 
    public DbSet<User> Users { get; set; } 
    public DbSet<AccessToken> AccessTokens { get; set; } 

    public CustomDbContext(DbContextOptions options) : base(options) 
    { 
    } 
} 

我有一對一關係如下簡單的數據模型:

User ----- AccessToken 

用戶:

public class User 
{ 
    public int Id { get; set; } 
    public string UserName { get; set; } 

    public virtual AccessToken AccessToken { get; set; } 
} 

的accessToken:

public class AccessToken 
{ 
    public int Id { get; set; } 
    public string Token { get; set; } 

    [ForeignKey("User"), Required] 
    public int UserId { get; set; } 
    public virtual User User { get; set; } 
} 

有人能幫助我解決嗎?我不明白究竟發生了什麼......

+0

第一個問題:「Context」初始化在哪裏? –

+0

@GertArnold是通過調試'userEntity'和'accessTokenEntity'變量從數據庫中獲取。 –

+0

這不是我的問題的答案。你在哪裏初始化'Context'本身?在查看這個「已被追蹤」錯誤時,這總是信息的第一個重要部分。 –

回答

1

看來EF已經在跟蹤userAccessToken了。因此,我們儘量避免獲取同一個實體的另一個實例並重用已經跟蹤的實體。

嘗試

[HttpPost] 
public IActionResult DeleteAccessToken([FromBody]User user) 
{ 
    // Requires System.Linq 
    if (Context.Users.Any(u => u.Id == user.Id)) 
    { 
     var accessTokenEntity = Context.AccessTokens.Find(user.AccessToken.Id); // Find the entity of the accesstoken (I tried also directly by accessing the navigation property of user entity) 
     Context.AccessTokens.Remove(accessTokenEntity); 

     // NOTE: You re not saving? 
     return Ok(); 
    } 
    else 
    { 
     return Unauthorized(); 
    } 
} 

或者嘗試:

[HttpPost] 
public IActionResult DeleteAccessToken([FromBody]User user) 
{ 
    if (Context.Users.Any(u => u.Id == user.Id)) 
    { 
     Context.AccessTokens.Remove(user.AccessToken); 
     return Ok(); 
    } 
    else 
    { 
     return Unauthorized(); 
    } 
} 
+5

這看起來像一個解決方法,解釋了最初導致問題的原因。 – Evk

+0

我認爲這個問題很好的解釋了異常消息。似乎EF已經在跟蹤實體,所以我試圖避免獲取同一(追蹤)實體的另一個實例。雖然,對於我來說,EF如何追蹤來自'POST'的實體,這是一個謎。 –

+2

我同意Evk,這是關於解決這個謎。不是爲了避免它。 –

0

嘗試替換此行:

var accessTokenEntity = Context.AccessTokens.Find(userEntity.AccessToken.Id); 

到:

var accessTokenEntity = Context.AccessTokens.AsNoTracking().Find(userEntity.AccessToken.Id); 
相關問題