繁体   English   中英

乐观并发

[英]Optimistic Concurrency

我有一个包含多个链接实体的实体框架项目。 由于它被多个用户同时使用,我已经为可能被多个用户同时编辑的实体设置了RowVersion-Field。 不幸的是,每次我尝试保存一个新实体时,我现在都会得到一个OptimisticConecurrencyException ,该实体链接到一个已经存在的实体。

存储更新,插入或删除语句会影响意外的行数(0)。 自实体加载后,实体可能已被修改或删除。 有关理解和处理乐观并发异常的信息,请参阅http://go.microsoft.com/fwlink/?LinkId=472540

现在的问题是,这个错误并没有真正给出关于错误所在位置的任何指示。 它可能是在此期间被修改的基础模型,新模型或其他东西可能存在验证错误。

我用来添加新实体的代码如下:

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语句被发送到数据库而不是另一个更新语句。

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列不匹配,我会理解异常,但在这种情况下,它是一个全新的实体。 有没有办法看看其中一个属性是否格式错误?

EDIT2:

我现在使用DateTime.Today代替DateTime.Now而不是仅仅修剪毫秒。 看似在ModificationDate上的datetime2(4)存在一些问题。 我已经确保将ModificationDate截断为4毫秒,因此不应该有解析错误。

EDIT3:

切换回DateTime.Now并修剪它停止工作的毫秒后,实体不再插入数据库。 这可能是由于sql server在基于毫秒值匹配实体时出现问题。 我执行了EF生成的SQL,如上所示,带有一些虚构的值,虽然在某些情况下查询没有返回rowversion值,但它仍然通过了。 就实体框架而言,客户端会将此解释为0行的返回值,因此调用并发异常。 (还应注意,ModificationDate和IDSample一起是实体的主键。)

Edit4:

我现在正在使用DateTime.Today,然后添加所需的精度,这对我有用。 这可以标记为已解决。 (Altough我原本预计EF可以自己处理日期时间格式转换:/)

我的问题是你在哪里添加DateTime? 您正在创建太多步骤来解决此问题。 创建日期时间,修改日期时间等。

如果您的实体是从具有映射属性的基类继承,请在DbContext覆盖SaveChanges()中进行并发添加/更新。

这是一个例子:(没有优化语法编写)

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();
    }
    ....
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM