Reputation: 16806
I am sending a User entity in Json format in my http request like this:
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}}
And my controller (in EF-core) handles that like this:
[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();
}
}
}
But the line Context.AccessTokens.Remove(accessTokenEntity);
throws this exception:
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.
I tried also accessing the AccessToken navigation property directly from userEntity with the same exception.
Here is my UnitOfWork initialization:
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();
}
My 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)
{
}
}
I have the following simple data model with one to one relationship:
User ----- AccessToken
User:
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; }
}
Could someone help me to solve that? I didn't understand what is exactly happening..
Upvotes: 5
Views: 7685
Reputation: 121
try replace this line:
var accessTokenEntity = Context.AccessTokens.Find(userEntity.AccessToken.Id);
to:
var accessTokenEntity = Context.AccessTokens.AsNoTracking().Find(userEntity.AccessToken.Id);
Upvotes: 0
Reputation: 4120
It appears that EF is already tracking user
and the AccessToken
in it. So, let's try to avoid getting another instance of the same entity and reuse the already tracked one.
Try
[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();
}
}
Alternatively try:
[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();
}
}
Upvotes: 1