首页 文章

EF:无法跟踪实体类型X的实例,因为已经跟踪了具有相同密钥的此类型的另一个实例

提问于
浏览
4

我在我的http请求中以Json格式发送一个User实体,如下所示:

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-core 中)处理如下:

[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); 行引发了这个异常:

Microsoft.EntityFrameworkCore.dll中出现类型'System.InvalidOperationException'的异常但未在用户代码中处理附加信息:无法跟踪实体类型'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;
                    pwd=P@ssword;
                    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)
    {
    }
}

我有以下与 one to one 关系的简单数据模型:

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; }
}

有人可以帮我解决这个问题吗?我不明白到底发生了什么......

2 回答

  • 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();
        }
    }
    
  • 0

    尝试替换此行:

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

    至:

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

相关问题