问题描述
我有最简单的ASP.NET Core 3.1 Web API解决方案,其模型项目使用Entity Framework Core 3.1.7将我的模型映射到sql Server。
我有以下实体:
[Table("SessionDetails")]
public class SessionDetailsModel
{
public long Id { get; set; }
public DateTime CreateDate { get; set; }
public Guid Token { get; set; }
public bool IsValid { get; set; }
}
。
通过以下方式配置数据库上下文:
public class RunMeDbContext : DbContext
{
public RunMeDbContext(DbContextOptions<RunMeDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SessionDetailsModel>()
.HasKey(x => new {x.Id,x.CreateDate});
base.OnModelCreating(modelBuilder);
}
public DbSet<SessionDetailsModel> SessionDetails { get; set; }
}
插入成功,但是当我调用注销端点时,该端点通过令牌字段获取先前创建的SessionDetails
记录,并尝试将'IsValid'字段(布尔)从true更新为false,得到以下异常:
Microsoft.EntityFrameworkCore.dbupdateConcurrencyException:数据库操作预期会影响1行,但实际上影响0行。自加载实体以来,数据可能已被修改或删除。有关了解和处理乐观并发异常的信息,请参见http://go.microsoft.com/fwlink/?LinkId=527962。
我是唯一一个连接到数据库的人,在阅读了有关并发冲突的信息后,我可以确保对该记录没有其他更改。
我的代码:
public async Task<bool> InvalidateSession(Guid token)
{
var session = await _dbContext.SessionDetails.FirstOrDefaultAsync(sd => sd.Token.Equals(token))
.ConfigureAwait(false);
if (session == null)
return false;
session.IsValid = false;
_dbContext.Update(session);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
return true;
}
提示:在调试时,我注意到虽然我要更改的唯一字段是'IsValid = false',但出于某些奇怪的原因,令牌字段也被修改了(尽管它的值是相同的):
解决方法
我无法复制这种行为。看看是否可以修改它以显示您正在观察的行为。
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace EfCore3Test
{
[Table("SessionDetails")]
public class SessionDetailsModel
{
public long Id { get; set; }
public DateTime CreateDate { get; set; }
public Guid Token { get; set; }
public bool IsValid { get; set; }
}
public class Db : DbContext
{
public Db(): base()
{
}
private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddFilter((category,level) =>
category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information).AddConsole();
});
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var constr = "Server = localhost; database = efcore3test; integrated security = true";
optionsBuilder.UseLoggerFactory(loggerFactory)
.UseSqlServer(constr,o => o.UseRelationalNulls());
//optionsBuilder.UseLazyLoadingProxies();
base.OnConfiguring(optionsBuilder);
}
public DbSet<SessionDetailsModel> SessionDetails { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SessionDetailsModel>()
.HasKey(x => new { x.Id,x.CreateDate });
modelBuilder.Entity<SessionDetailsModel>().Property(e => e.Id).UseIdentityColumn();
modelBuilder.Entity<SessionDetailsModel>().HasIndex(e => e.Token).IsUnique(true);
base.OnModelCreating(modelBuilder);
}
}
class Program
{
static void Main(string[] args)
{
long id;
DateTime created = DateTime.Now;
Guid token = Guid.NewGuid();
using (var db = new Db())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var s = new SessionDetailsModel();
s.CreateDate = created;
s.Token = token;
s.IsValid = true;
db.SessionDetails.Add(s);
db.SaveChanges();
id = s.Id;
}
using (var db = new Db())
{
var sd = db.SessionDetails.Where(d => d.Token == token).Single();
sd.IsValid = false;
db.SaveChanges();
}
}
}
}