方案:我有一个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的新值恢复为先前的值?
谢谢您的回答。