- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个包含多个链接实体的 Entity Framework 项目。因为它一次被多个用户使用,所以我为可能被多个用户同时编辑的实体设置了一个 RowVersion-Field。不幸的是,我现在每次尝试保存链接到现有实体的新实体时都会收到 OptimisticConecurrencyException
。
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
现在的问题是这个错误并没有给出任何关于错误真正所在的指针。它可能是同时修改了基础模型,新模型或其他原因可能存在验证错误。
我用来添加新实体的代码如下:
using (ctx = new DbContext())
{
try
{
ctx.Samples.Add(model);
ctx.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
LogManager.HandleException(ex.InnerException);
}
}
model
是我要添加到数据库的模型
编辑:如上所示,我修改了代码以忽略基础模型的更新。此外,我已经通过以下方式验证:
ctx.Database.log = s => Debug.Write(s);
只有一个插入语句被发送到数据库,而不是一个额外的更新语句。
INSERT [dbo].[Samples]([IDSample], [ModificationDate], [IDUser])
VALUES (@0, @1, @2)
SELECT [RowVersion]
FROM [dbo].[Samples]
WHERE @@ROWCOUNT > 0 AND [IDSample] = @0 AND [ModificationDate] = @1
如果我更新一个实体并且 rowversion 列不匹配,我会理解异常,但在这种情况下它是一个全新的实体。有没有办法查看其中一个属性是否格式错误?
编辑2:
我现在使用 DateTime.Today 而不是 DateTime.Now 而不是仅仅调整毫秒数。似乎 ModificationDate 上的 datetime2(4) 存在一些问题。我已经确保 ModificationDate 被截断为 4 毫秒,因此应该没有解析错误。
编辑3:
切换回 DateTime.Now 并调整毫秒数后,它停止工作,并且不再将实体插入到数据库中。这可能是由于 sql server 在匹配基于毫秒值的实体时出现问题。我使用一些虚构的值执行了上面看到的 EF 生成的 SQL,它通过了,尽管在某些情况下查询没有返回 rowversion-value。就 Entity Framework 而言,客户端会将此解释为 0 行的返回值,因此调用并发异常。 (还应注意,ModificationDate 与 IDSample 一起是实体的主键。)
编辑4:
我现在使用 DateTime.Today 然后添加所需的精度,这对我有用。这可以标记为已解决。 (尽管我本以为 EF 可以自行处理日期时间格式转换:/)
最佳答案
我的问题是您在哪里添加日期时间?您正在创建太多步骤来解决这个问题。创建日期时间、修改日期时间等。
如果您的实体继承自具有映射属性的基类,请在 SaveChanges() 的 DbContext 覆盖中执行并发添加/更新。
这是一个例子:(没有优化语法编写)
public abstract class EntityBase
{
public int Id {get; set;}
public DateTime CreationDate {get; set;}
public DateTime? ModifyDate {get; set;}
public string VersionHash {get; set;}
}
public static class EntityBaseExtensions
{
public static void MyBaseEntityMapping<T>(this EntityTypeConfiguration<T> configuration) where T : EntityBase
{
configuration.HasKey(x => x.Id);
configuration.Property(x => x.CreationDate)
.IsRequired();
configuration.Property(x => x.ModifyDate)
.IsOptional();
configuration.Property(x => x.VersionHash).IsConcurrencyToken();
}
}
public class MyEntity : EntityBase
{
public string MyProperty {get; set;}
}
public class MyEntityMapping : EntityTypeConfiguration<MyEntity>
{
public MyEntityMapping()
{
this.MyBaseEntityMapping();
Property(x=>x.MyProperty).IsRequired();
}
}
public class MyContext : DbContext
{
....
public override int SaveChanges()
{
this.ChangeTracker.DetectChanges(); //this forces EF to compare changes to originals including references and one to many relationships, I'm in the habit of doing this.
var context = ((IObjectContextAdapter)this).ObjectContext; //grab the underlying context
var ostateEntries = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added); // grab the entity entries (add/remove, queried) in the current context
var stateEntries = ostateEntries.Where(x => x.IsRelationship == false && x.Entity is EntityBase); // don't care about relationships, but has to inherit from EntityBase
var time = DateTime.Now; //getting a date for our auditing dates
foreach (var entry in stateEntries)
{
var entity = entry.Entity as EntityBase;
if (entity != null) //redundant, but resharper still yells at you :)
{
if (entry.State == EntityState.Added) //could also look at Id field > 0, but this is safe enough
{
entity.CreationDate = time;
}
entity.ModifyDate = time;
entity.VersionHash = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 10); //this an example of a simple random configuration of letters/numbers.. since the query on sql server is primarily using the primary key index, you can use whatever you want without worrying about query execution.. just don't query on the version itself!
}
}
return base.SaveChanges();
}
....
}
关于c# - 乐观并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30255309/
我正在编写一个 Web 应用程序,两个不同的用户可以在其中更新事物列表,例如待办事项列表。我已经意识到,乐观锁定机制效果最好,因为我不希望出现高争用情况。 我一直在查看事务隔离级别,现在我有点困惑。看
这个问题在这里已经有了答案: Pessimistic versus Optimistic Concurrency (Locking versus Feedback) (3 个答案) 关闭 8 年前。
我是一名优秀的程序员,十分优秀!