Entity Framework DbContext пытается вставить данные, которые уже существуют - PullRequest
0 голосов
/ 28 апреля 2019

У меня есть два DbContext класса.ServerContext имеет внешний ключ для UserContext.

ServerContext:

public ServerContext(DbContextOptions<ServerContext> options)
  : base(options) {
}

public DbSet<Server> Server { get; set; }

protected override void OnModelCreating(ModelBuilder builder) 
{
    base.OnModelCreating(builder);
    builder.Entity<Server>().ToTable("Server");

    builder.Entity<User.User>().HasMany(x => x.Servers).WithOne(x => x.User);
}

Сервер:

public class Server 
{
    public string ServerId { get; set; }

    [Required]
    [InverseProperty("User")]
    public string UserId { get; set; }
    public virtual User.User User { get; set; }
}

UserContext:

public class UserContext : IdentityDbContext<User> 
{
    public UserContext(DbContextOptions<UserContext> options)
      : base(options) 
    {
    }

    public DbSet<User> User { get; set; }

    protected override void OnModelCreating(ModelBuilder builder) 
    {
        base.OnModelCreating(builder);
        builder.Entity<User>().ToTable("User");
    }
}

Пользователь:

public class User : IdentityUser 
{
    public virtual ICollection<Server.Server> Servers { get; set; }
}

При запуске я извлекаю все Server данные в кеши обновляя его каждую минуту.

Ошибка происходит в модели.На модели я использую данные сервера.После этого я проверяю, содержат ли данные сервера ненулевые пользовательские данные.Это ноль, я загружаю его из базы данных (на этом этапе выдается ошибка).

public async Task OnGetAsync() 
{
    Servers = await this.ServerManager.GetServersAsync();

    foreach (var server in Servers) 
    {
        if(server == null) 
            continue;

        // This code triggers the error
        var user = await UserManager.FindByIdAsync(server.UserId);

        if(user == null) 
           continue;

        server.User = user;
    }
}

Пользователь:

  public class User : IdentityUser {

    public virtual ICollection<IdentityRole> Roles { get; set; }

    public virtual ICollection<Server.Server> Servers { get; set; }
  }

6 запросовдля данных одного пользователя: https://pastebin.com/ud9eeKKe

Через каждую минуту я сохраняю данные и получаю следующую ошибку:

DbUpdateException: при обновлении записей произошла ошибка,Смотрите внутреннее исключение для деталей.MySql.Data.MySqlClient.ПЕРВИЧНЫЙ '.

1 Ответ

1 голос
/ 28 апреля 2019

Как вы упомянули, что вы можете изменить код метода GetServersAsync, я ожидаю, что DbContext-Call выглядит примерно так:

return ServerContext.Servers;

Вы можете изменить его на:

return ServerContext.Servers.Include(u=>u.User);

Таким образом, вы можете пропустить ошибку, вызывающую код снаружи, потому что пользователь уже заполнен.

Ошибка действительно возникает, поскольку вы устанавливаете свойство пользователя для отслеживаемого объекта.

Кроме того, перед сохранением вы можете установить нулевое значение User-Property. Или вы можете установить User как EntityEntry как неизменный, прежде чем устанавливать User как User-Property (для проблем с производительностью я бы не рекомендовал это).

В качестве другого варианта вы можете интегрировать Lazy-Loading, который был представлен в EF Core 2.1, как описано здесь .

Обновление:

Вы можете загрузить свои серверы NotTracked, чтобы предотвратить проблему сохранения:

return ServerContext.Servers.AsNoTracking();

Тогда вы можете добавить пользователя, как вы уже сделали ... Позже, перед сохранением серверов, вам необходимо выполнить следующие шаги:

  1. Установите для пользователя значение null снова

  2. Добавить серверный объект как обновленный в контекст

  3. Вызов SaveChanges

...