我正在创建应用程序中使用的默认并发策略。
我决定采取乐观的策略。
我的所有实体都被映射为Table per Type (TPT) (使用继承)。我很快就了解到,在实体框架上使用带有继承的RowVersion类型的列时会出现问题:
Product
Id INT IDENTITY PRIMARY KEY
RowVersion ROWVERSION
Car (inherits Product records)
Color TYNIINT NOT NULL,
AnotherProperty.... 如果我更新Car表的记录,则不会更新Product表中的RowVersion列。
我计划在datetime2 (7)中使用Product类型的列,并在继承此表的表的任何记录被修改时手动更新它。
我想我在重新发明方向盘。
在实体框架中使用ROWVERSION时,还有其他方法可以使用乐观并发策略吗?
编辑
我的地图:
class Product
{
int Id { get; set; }
string Name { get; set; }
byte[] RowVersion { get; set; }
}
class Car : Product
{
int Color { get; set; }
}CodeFirst会议。
只有RowVersion实体上的Product属性具有自定义定义:
modelBuilder.Entity<Product>()
.Property(t => t.RowVersion)
.IsConcurrencyToken();发布于 2015-07-14 21:44:47
在EF6和EF-core中,在使用Server时,必须使用以下映射:
modelBuilder.Entity<Product>()
.Property(t => t.RowVersion)
.IsRowVersion(); // Not: IsConcurrencyTokenIsConcurrencyToken确实将属性配置为并发令牌,但是(当将其用于byte[]属性时)
varbinary(max)null。另一方面,IsRowVersion,
rowversion (在Server中,或在早期版本中的timestamp ),因此现在,当您更新一个Car时,您将看到两个update语句:
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()的数据注释。
[Timestamp]
public byte[] RowVersion { get; set; }注意,正式文件是不正确的。它说,IsConcurrencyToken是[Timestamp]属性的流畅等效物。然而,IsRowVersion是等价的。
发布于 2017-03-08 08:30:58
经过一番调查,我能够在实体框架6中名为IsConcurrencyToken的byte8列上使用RowVersion。
因为我们想在DB2中使用相同的数据类型(数据库本身没有行版本),所以不能使用IsRowVersion()选项!
我进一步研究了如何使用IsConcurrencyToken。
我做了以下工作,以获得一个似乎有效的解决方案:
我的模型:
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来配置模型构建器:
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之前更新该字段:
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设置为原始值。此外,我复制数组,以避免引用相同的字节数组!
注意:这里我使用增量方法来更改行版本,您可以自由地使用自己的策略来填充这个值。(随机或基于时间的)
发布于 2018-01-04 18:01:49
问题不在于你是如何设置的。正在发生的情况是,一旦从上下文中取出新值,RowVersion条目的RowVersion就会被设置为新值。
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;https://stackoverflow.com/questions/31330015
复制相似问题