После небольшого исследования я смог использовать IsConcurrencyToken в столбце байта [8], названном RowVersion в Entity Framework 6.
Поскольку мы хотим использовать один и тот же тип данных в DB2 (который не имеют rowversion в самой базе данных), мы не можем использовать параметр 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 используется для идентификации сущностей, которые имеют rowversion, которые нуждаются в особой обработке.
I использующий свободный API для конфигурирования конструктора:
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(e => e.RowVersion).IsFixedLength().HasMaxLength(8).IsConcurrencyToken();
}
}
И, наконец, я добавил метод к моему производному классу DBContext для обновления поля до базы. Вызывается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 изменилось ... даже если оно не было!
Причина в том, что для бют e [] как оригинал, так и currentValue, если вы установите CurrentValue отдельно (?? странное и неожиданное поведение).
Итак, я снова установил исходное значение в исходное значение перед обновлением rowversion ... Также я копирую массив, чтобы не ссылаться на один и тот же байт-массив!
Внимание: здесь я использую инкрементный подход для изменения rowversion, вы можете использовать свою собственную стратегию для заполнения этого значения. (Случайные или временные)
Вы должны либо хранить в какой-то очереди свои открытые узлы, либо каждый раз заполнять всю карту:
bool NextFill(int (&map)[MAX_SIZE][MAX_SIZE], int n)
{
const int offx = {-1, 0, 0, 1};
const int offy = {0, -1, 1, 0}
bool found = false;
for (int x = 0; x != MAX_SIZE; ++x) {
for (int y = 0; y != MAX_SIZE; ++y) {
if (map[x][y] == n) {
for (int i = 0; i != 4) {
auto& neighbor = map[x + offx[i]][y + offy[i]];
if (neighbor == -1) { /*Nothing*/ } // wall
else if (neighbor == -2) { found = true; } // Found
else if (neighbor == 0) { neighbor = n + 1; } // unvisited
// else {/*Nothing*/} // Already visited.
}
}
}
}
return found;
}