EntityFramework - [Timestamp] vs IsConcurrencyToken [дубликат]

Разница между расширением потока и реализацией Runnable:

9
задан Marcos Dimitrio 9 August 2016 в 17:36
поделиться

3 ответа

Как в EF6, так и в EF-core, при работе с Sql Server вы должны использовать это сопоставление:

modelBuilder.Entity<Product>() 
.Property(t => t.RowVersion) 
.IsRowVersion(); // Not: IsConcurrencyToken

IsConcurrencyToken настраивает свойство как токен параллелизма, но (при использовании его для byte[])

  • тип данных varbinary(max)
  • его значение всегда null, если вы его не инициализируете
  • его значение не увеличивается автоматически при обновлении записи.

IsRowVersion , с другой стороны,

  • имеет тип данных rowversion (в Sql Server или timestamp в более ранних версиях), поэтому
  • его значение никогда не равно null, а
  • его значение всегда автоматически увеличивается при обновлении записи .
  • , и он автоматически настраивает свойство как оптимистичный токен параллелизма.

Теперь, когда вы обновляете Car, вы увидите два оператора обновления:

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 ...

Первый оператор ничего не обновляет, но увеличивает приращение rowversion и генерирует исключение параллелизма, если переменная rowversion была изменена между ними.

[System.ComponentModel.DataAnnotations.Schema.Timestamp] ttribute - это аннотации данных, эквивалентные IsRowVersion():

[Timestamp]
public byte[] RowVersion { get; set; }
18
ответ дан Gert Arnold 18 August 2018 в 10:09
поделиться
  • 1
    Какую версию SQL Server вы используете? Согласно документации, IsConcurrencyToken (), используемый для свойства byte [], должен сопоставляться типу данных rowversion, который был добавлен в SQL Server 2005. – Søren Boisen 9 February 2016 в 12:34
  • 2
    @ SørenBoisen Это был SQL2012 IIRC. Я только что протестировал его первым кодом и сообщил, что получил. Если в документации указано что-то еще, это либо неправильно, либо предполагает другие шаги, которые я не предпринимал. В какой документации вы ссылаетесь? – Gert Arnold 9 February 2016 в 13:06
  • 3
    Хорошо, возможно, не совсем документация, но учебник на официальном сайте тем не менее :-) Здесь: asp.net/mvc/overview/older-versions/… – Søren Boisen 10 February 2016 в 16:27
  • 4
    Я могу подтвердить ваши выводы кстати - нужно использовать IsRowVersion, чтобы получить правильное поведение. – Søren Boisen 10 February 2016 в 17:42
  • 5
    Документация немного не работает. Он демонстрирует использование атрибута [Timestamp], а затем указывает, что IsConcurrencyToken является эквивалентным беглому методу. Но это неправда. IsRowVersion эквивалентен. – Gert Arnold 10 February 2016 в 22:41

Проблема заключается не в настройке. Случается, что OriginalValue вашей записи 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;
0
ответ дан Chris Lamothe 18 August 2018 в 10:09
поделиться

После небольшого исследования я смог использовать 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, вы можете использовать свою собственную стратегию для заполнения этого значения. (Случайные или временные)

3
ответ дан Schwarzie2478 18 August 2018 в 10:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: