Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Getting 'The instance of entity type cannot be tracked because another instance with same key value for is already being tracked' with AsNoTracking()

I’m creating my Telegram bot using Entity Framework Core. I have these models:

public class User
{
    public int Id { get; set; }
    public long UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserName { get; set; }
    public ICollection<Chat> Chats { get; set;} = new List<Chat>();
    public ICollection<Team> CreatedTeams { get; set; } = new List<Team>();
    public ICollection<Team> Teams { get; set; } = new List<Team>();
}

public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ChatId { get; set; }
    public Chat Chat { get; set; }
    public int? CreatorId { get; set; }
    public User Creator { get; set; }
    public ICollection<User> Users { get; set; } = new List<User>();
}

When I handle incoming messages, I check if a User with the same UserId exists in DB. If it exists, then I just get it, if not – create new one on a base of Telegram User and return it.

public async Task<User> GetNewOrExistingUserAsync(Telegram.Bot.Types.User telegramUser)
{
    var user = this.GetUserByUserId(telegramUser.Id);

    if (user is null)
    {
        await this.userRepository.AddAsync((User)telegramUser);
        return this.GetUserByUserId(telegramUser.Id);
    }

    return user;
}

private User GetUserByUserId(long userId)
{
    return this.userRepository.GetAll()
            .Include(user => user.Chats)
            .Include(user => user.Teams)
            .Include(user => user.CreatedTeams)
            .AsNoTracking()
            .FirstOrDefault(user => user.UserId == userId);
 }

After that I get Team by name specified in message:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

public Team GetChatTeamByName(string name, long chatId)
{
    return this.teamRepository.GetAll()
         .Include(team => team.Creator)
         .Include(team => team.Users)
         .AsNoTracking()
         .FirstOrDefault(team => team.Chat.ChatId == chatId && team.Name.Equals(name));
}

And finally I add user to team (I check, if it already exists in team, but I receive this error even without this check):

team.Users.Add(user);
await this.teamRepository.UpdateAsync(team);

Here I get this error: ‘The instance of entity type ‘User’ cannot be tracked because another instance with the same key value for {‘Id’} is already being tracked‘. UpdateAsync() uses Entities.Update() method and saves changes. Error occurs after Entities.Update() call. I do not understand, where it starts tracking user – as you can see, I use AsNoTracking() in all related queries. I could assume, that it starts tracking after adding new user, but error occurs even when user is already in DB.

I’ve tried to use Entry<User>(user).State = EntityState.Detached; as people suggest, right before adding user to team, or after it, but error is the same. The context in my ASP app is Transient, but this error occurs even if it Scoped. Could you help me please with it? Or are there any other ways to add user to team without calling Update() method?

The whole method is:

public async Task<Message> AddUserToTeamAsync(ITelegramBotClient botClient, Message message)
{
    var user = await this.userService.GetNewOrExistingUserAsync(message.From);

    var teamName = message.Text.SplitToWords()[1];
    var team = this.teamService.GetChatTeamByName(teamName, message.Chat.Id);

    team.Users.Add(user);
    await this.teamRepository.UpdateAsync(team);
    .....
}

>Solution :

You don’t seem to fully comprehend what AsNoTracking does: It explicitly disconnects the entity from the database. So if you want to Update that entity, you shouldn’t disconnect it in the first place.

It’s a relational database. If you want to add an existing user to a team, you need to track the existing object. Else the database will try to create a new one with exactly the same values, and there will be a collision.

Side note: why are you using Include, instead of lazy loading? We are running several large application at my company and almost never have to use Include.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading