首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >乐观并发: IsConcurrencyToken和RowVersion

乐观并发: IsConcurrencyToken和RowVersion
EN

Stack Overflow用户
提问于 2015-07-09 23:25:27
回答 3查看 20.5K关注 0票数 18

我正在创建应用程序中使用的默认并发策略。

我决定采取乐观的策略。

我的所有实体都被映射为Table per Type (TPT) (使用继承)。我很快就了解到,在实体框架上使用带有继承的RowVersion类型的列时会出现问题:

代码语言:javascript
复制
Product

Id INT IDENTITY PRIMARY KEY
RowVersion ROWVERSION

Car (inherits Product records)

Color TYNIINT NOT NULL,
AnotherProperty....   

如果我更新Car表的记录,则不会更新Product表中的RowVersion列。

我计划在datetime2 (7)中使用Product类型的列,并在继承此表的表的任何记录被修改时手动更新它。

我想我在重新发明方向盘。

在实体框架中使用ROWVERSION时,还有其他方法可以使用乐观并发策略吗?

编辑

我的地图:

代码语言:javascript
复制
class Product
{
    int Id { get; set; }
    string Name { get; set; }
    byte[] RowVersion { get; set; }
}

class Car : Product
{
    int Color { get; set; }
}

CodeFirst会议。

只有RowVersion实体上的Product属性具有自定义定义:

代码语言:javascript
复制
modelBuilder.Entity<Product>() 
    .Property(t => t.RowVersion) 
    .IsConcurrencyToken();
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-07-14 21:44:47

在EF6和EF-core中,在使用Server时,必须使用以下映射:

代码语言:javascript
复制
modelBuilder.Entity<Product>() 
    .Property(t => t.RowVersion) 
    .IsRowVersion(); // Not: IsConcurrencyToken

IsConcurrencyToken确实将属性配置为并发令牌,但是(当将其用于byte[]属性时)

  • 数据类型为varbinary(max)
  • 如果不初始化它,它的值总是null
  • 它的值在更新记录时不会自动递增.

另一方面,IsRowVersion

  • 具有数据类型rowversion (在Server中,或在早期版本中的timestamp ),因此
  • 它的值从不为空,而且
  • 它的值总是在更新记录时自动递增.
  • 并且它自动将属性配置为一个乐观的并发令牌。

现在,当您更新一个Car时,您将看到两个update语句:

代码语言:javascript
复制
DECLARE @p int
UPDATE [dbo].[Product]
SET @p = 0
WHERE (([Id] = @0) AND ([Rowversion] = @1))
SELECT [Rowversion]
FROM [dbo].[Product]
WHERE @@ROWCOUNT > 0 AND [Id] = @0

UPDATE [dbo].[Car]
SET ...

第一个语句不会更新任何内容,但是它会增加行版本,如果行版本在中间被更改,它将抛出并发异常。

[System.ComponentModel.DataAnnotations.Schema.Timestamp]属性是相当于IsRowVersion()的数据注释。

代码语言:javascript
复制
[Timestamp]
public byte[] RowVersion { get; set; }

注意,正式文件是不正确的。它说,IsConcurrencyToken[Timestamp]属性的流畅等效物。然而,IsRowVersion是等价的。

票数 37
EN

Stack Overflow用户

发布于 2017-03-08 08:30:58

经过一番调查,我能够在实体框架6中名为IsConcurrencyToken的byte8列上使用RowVersion。

因为我们想在DB2中使用相同的数据类型(数据库本身没有行版本),所以不能使用IsRowVersion()选项!

我进一步研究了如何使用IsConcurrencyToken。

我做了以下工作,以获得一个似乎有效的解决方案:

我的模型:

代码语言:javascript
复制
    public interface IConcurrencyEnabled
{
    byte[] RowVersion { get; set; }
}

  public class Product : AuditableEntity<Guid>,IProduct,IConcurrencyEnabled
{
    public string Name
    {
        get; set;
    }
    public string Description
    {
        get; set;
    }
    private byte[] _rowVersion = new byte[8];
    public byte[] RowVersion
    {
        get
        {
            return _rowVersion;
        }

        set
        {
            System.Array.Copy(value, _rowVersion, 8);
        }
    }
}

IConcurrencyEnabled用于标识具有需要特殊处理的行版本的实体。

我使用fluent API来配置模型构建器:

代码语言:javascript
复制
    public class ProductConfiguration : EntityTypeConfiguration<Product>
{
    public ProductConfiguration()
    {
        Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        Property(e => e.RowVersion).IsFixedLength().HasMaxLength(8).IsConcurrencyToken();
    }
}

最后,我向派生的DBContext类添加了一个方法,以便在调用base.SaveChanges之前更新该字段:

代码语言:javascript
复制
        public void OnBeforeSaveChanges(DbContext dbContext)
    {
        foreach (var dbEntityEntry in dbContext.ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified))
        {
            IConcurrencyEnabled entity = dbEntityEntry.Entity as IConcurrencyEnabled;
            if (entity != null)
            {

                if (dbEntityEntry.State == EntityState.Added)
                {
                    var rowversion = dbEntityEntry.Property("RowVersion");
                    rowversion.CurrentValue = BitConverter.GetBytes((Int64)1);
                }
                else if (dbEntityEntry.State == EntityState.Modified)
                {
                    var valueBefore = new byte[8];
                    System.Array.Copy(dbEntityEntry.OriginalValues.GetValue<byte[]>("RowVersion"), valueBefore, 8);

                    var value = BitConverter.ToInt64(entity.RowVersion, 0);
                    if (value == Int64.MaxValue)
                        value = 1;
                    else value++;

                    var rowversion = dbEntityEntry.Property("RowVersion");
                    rowversion.CurrentValue = BitConverter.GetBytes((Int64)value);
                    rowversion.OriginalValue = valueBefore;//This is the magic line!!

                }

            }
        }
    }

大多数人遇到的问题是,在设置实体的值之后,我们总是得到一个UpdateDBConcurrencyException,因为OriginalValue已经改变了.即使它没有!

原因是,如果只设置byte[] (??),则currentValue和原始CurrentValue都会发生更改。奇怪和意外的行为)。

因此,在更新行版本之前,我再次将OriginalValue设置为原始值。此外,我复制数组,以避免引用相同的字节数组!

注意:这里我使用增量方法来更改行版本,您可以自由地使用自己的策略来填充这个值。(随机或基于时间的)

票数 6
EN

Stack Overflow用户

发布于 2018-01-04 18:01:49

问题不在于你是如何设置的。正在发生的情况是,一旦从上下文中取出新值,RowVersion条目的RowVersion就会被设置为新值。

代码语言:javascript
复制
 var carInstance = dbContext.Cars.First();
 carInstance.RowVersion = carDTO.RowVerison;
 carInstance.Color = carDTO.Color ;


 var entry = dbContext.Entry(carInstance); //Can also come from ChangeTrack in override of SaveChanges (to do it automatically)     

 entry.Property(e => e.RowVersion)
                    .OriginalValue = entry.Entity.RowVersion;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31330015

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档