EF核心随机意外行为更新实体的父级

时间:2020-06-12 09:37:52

标签: c# ef-core-3.1

方案:我有一个Players数据库表。每个玩家可能都有一个邀请者玩家和推荐人列表(被邀请玩家)。

简化的代码如下:

public class DbPlayer
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<DbPlayer> Referrals { get; set; }

    public int? InviterId { get; set; }
    [ForeignKey("InviterId")]
    public DbPlayer Inviter { get; set; }

    public DbPlayer()
    {
        Referrals = new List<DbPlayer>();
    }

    public static async Task<DbPlayer> GetByIdAsync(int id)
    {
        using (var db = new BetterDb())
            return await (from players in db.Players.Include(p => p.Inviter)
                          where players.Id == id
                          select players).SingleOrDefaultAsync().ConfigureAwait(false);
    }

    public async Task SaveAsync()
    {
        using (var db = new BetterDb())
        {
            db.Attach(this);
            db.Entry(this).State = Id == 0 ? EntityState.Added : EntityState.Modified;
            await db.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    public override string ToString()
    {
        return Name;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Id);
    }
}


public class BetterDb : DbContext
{
    public DbSet<DbPlayer> Players { get; set; }


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<DbPlayer>().HasOne(p => p.Inviter).WithMany(u => u.Referrals);
    }
}

当玩家的邀请者更改为另一邀请者时,会随机发生此问题。 在SaveAsync方法上,恰好在之后,调用“附加”,将Inviter和InviterId重置为先前的值。仅当先前的Inviter不为null时,这种情况才会随机发生。

测试:

public class PlayersMngr
{
    public PlayersMngr()
    {

    }

    public async Task TestAsync()
    {
        DbPlayer child = await DbPlayer.GetByIdAsync(3);
        if (child == null)
        {
            await CreateAsync();
        }
        else
        {
            await MovePlayer();
        }
    }

    private async Task CreateAsync()
    {
        DbPlayer p1 = new DbPlayer() { Name = "John" };
        DbPlayer p2 = new DbPlayer() { Name = "Mark" };
        DbPlayer p3 = new DbPlayer() { Name = "Paul", Inviter = p1 };

        await p1.SaveAsync();
        await p2.SaveAsync();
        await p3.SaveAsync();
    }

    public async Task MovePlayer()
    {
        DbPlayer p1 = await DbPlayer.GetByIdAsync(1);
        DbPlayer p2 = await DbPlayer.GetByIdAsync(2);
        DbPlayer p3 = await DbPlayer.GetByIdAsync(3);

        p3.Inviter = p2;
        p3.InviterId = p2.Id;

        await p3.SaveAsync();
        // p3.Inviter and p3.InviterId are randomly reverted back to the previous values if they were not null
    }
}



//.....

PlayersMngr mng = new PlayersMngr();
await mng.TestAsync();

//....

该代码在多线程上下文中运行,但是一次仅更改一个线程并保存一个Player。

有时问题在每次运行/调试时都会发生,有时我无法复制。

我使用的解决方法是在调用SaveAsync之前将其添加到MoveAsync方法中

if(p3.Inviter != null)
    p3.Inviter.Referrals.Remove(p3);

问题是:什么会导致Inviter和InviterId的新值恢复为先前的值?

谢谢您的回答。

0 个答案:

没有答案