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

我在我的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核心中)处理如下:

[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);抛出此异常:

An exception of type ‘System.InvalidOperationException’ occurred in
Microsoft.EntityFrameworkCore.dll but was not handled in user code

Additional information: The instance of entity type ‘AccessToken’
cannot be tracked because another instance of this type with the same
key is already being tracked. When adding new entities, for most key
types a unique temporary key value will be created if no key is set
(i.e. if the key property is assigned the default value for its type).
If you are explicitly setting key values for new entities, ensure they
do not collide with existing entities or temporary values generated
for other new entities. When attaching existing entities, ensure that
only one entity instance with a given key value is attached to the
context.

我还尝试直接从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)
    {
    }
}

我有以下简单的数据模型与一对一的关系:

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

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

最佳答案 看来EF已经在跟踪用户和其中的AccessToken.因此,让我们尝试避免获取同一实体的另一个实例并重用已经跟踪过的实例.

尝试

[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();
    }
}
点赞