2015-07-09 20 views
9

Uygulamamda kullanacağım varsayılan eşzamanlılık stratejisini oluşturuyorum.İyimser eşzamanlılık: IsConcurrencyToken ve RowVersion

İyimser bir stratejiye karar verdim.

Bütün varlıklarım Table per Type (TPT) (miras kullanarak) olarak eşlenir. Yakında İdare Framework üzerinde miras tip RowVersion sütunlarının kullanırken bir sorun olduğunu öğrendik:

Product 

Id INT IDENTITY PRIMARY KEY 
RowVersion ROWVERSION 

Car (inherits Product records) 

Color TYNIINT NOT NULL, 
AnotherProperty....    

ben Product tablodan rowversion sütun güncellenen olmayacak Car tablonun rekor güncellerseniz

.

Product'da datetime2 (7) türünde bir sütun kullanmayı ve bu tabloyu devralan tabloların herhangi bir kaydı değiştirildiyse bunu el ile güncelleştirmeyi planlıyorum.

Sanırım tekerleği yeniden icat ediyorum.

Table per Type (TPT) Entity Framework'de kullanırken iyimser eşzamanlılık stratejisini ROWVERSION ile kullanmanın başka bir yolu var mı?

Düzenleme

Benim haritalama:

class Product 
{ 
    int Id { get; set; } 
    string Name { get; set; } 
    byte[] RowVersion { get; set; } 
} 

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

CodeFirst konvansiyonlar.

Sadece Product varlık üzerinde rowversion mülkünde özel tanımları vardır:

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

: SQL Server ile çalışırken

modelBuilder.Entity<Product>() 
    .Property(t => t.RowVersion) 
    .IsConcurrencyToken(); 
+0

Herhangi biri? :/Plx. –

+0

* Ürün tablosu güncellenmeyecek *. Aslında, rowversion'ı artıran bir kukla güncelleme ile (EF 6.1.3). –

+0

Merhaba @GertArnold. Testlerimde (EF6.1.3), rowversion artırılmıyor. –

cevap

17

Hem Ef6 ve EF-çekirdek, bu eşleştirmeyi kullanmak zorunda IsConcurrencyToken, bir özelliği eşzamanlılık belirteci olarak yapılandırır, ancak (byte[] özelliği için kullanıldığında)

  • veri türü başlatmak yoksa varbinary(max)
  • değeri daima null olduğunu o
  • rekor güncellendiğinde değeri otomatik artan edilmez. Öte yandan

IsRowVersion,

  • değeri asla boş yani, (önceki sürümlerinde SQL Server veya timestamp) veri türü rowversion sahiptir ve
  • onun Bir kayıt güncellendiğinde değer her zaman otomatik olarak artırılır.
  • ve özelliği iyimser bir eşzamanlılık jetonu olması için özelliği otomatik olarak yapılandırır.

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

    ilk deyimi bir şey değil güncelleştirme ama rowversion artırır ve bir eşzamanlılık istisna eğer atacağım: Bir Car iki güncelleme ifadeleri göreceksiniz güncellemek Şimdi

Aradakiler değişti.

[Timestamp] 
public byte[] RowVersion { get; set; } 
+0

Hangi SQL Server sürümünü kullanıyorsunuz? Belgelere göre, byte [] özelliğinde kullanılan IsConcurrencyToken(), SQL Server 2005'te eklenen rowversion veri türü ile eşleşmelidir. –

+0

@ SørenBoisen Bu, SQL2012 IIRC idi. İlk önce kodu test ettim ve elime geçenleri bildirdim. Belgeler başka bir şey söylerse, yanlıştır ya da almadığım diğer adımları gerektirir. Hangi belgeleri kullanıyorsunuz? –

+0

Tamam, belki de tam olarak belgeleme değil, ama resmi sitede bir öğretici yine de :-) Burada: http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using -mvc-4/hand-net-mvc-application-conc-net-mvc-uygulama-conc-net-mvc-uygulama-out-net-mvc-uygulama –

3

I 6.

varlık Framework bir bayt [8], kolon adı rowversion IsConcurrencyToken kullanmak mümkün araştıran biraz sonra:

[System.ComponentModel.DataAnnotations.Schema.Timestamp] özelliği IsRowVersion() veri açıklamalar denk

Aynı veri türünü DB2'de kullanmak istiyoruz (ki bu da veritabanında kendi kendine dönüş yapmıyor) IsRowVersion() seçeneğini kullanamıyoruz!

IsConcurrencyToken ile nasıl çalışılacağını biraz daha araştırdım.

Ben iş gibi görünüyor çözüm bulmak amacıyla aşağıdaki yaptı:

Benim Modeli:

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 özel muamele gerektiren bir rowversion sahip Varlıkları tanımlamak için kullanılır.

Ben ModelBuilder yapılandırmak için akıcı API kullandı:

public class ProductConfiguration : EntityTypeConfiguration<Product> 
{ 
    public ProductConfiguration() 
    { 
     Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); 
     Property(e => e.RowVersion).IsFixedLength().HasMaxLength(8).IsConcurrencyToken(); 
    } 
} 

Ve sonunda base.SaveChanges çağrılmadan önce alanı güncelleştirmek için benim türetilmiş DBContext sınıfa bir yöntem eklendi:

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

       } 

      } 
     } 
    } 

Çoğu kişinin karşılaştığı problem, varlığın değerini ayarladıktan sonra, her zaman bir UpdateDBConcurrencyException alırız, çünkü OriginalValue değişmemiş olsa bile ...

Bunun nedeni, yalnızca geçerli bir Değer (yalnızca garip ve beklenmedik davranış) olarak ayarlanmış olmanız durumunda bir bayt [] için hem özgün hem de geçerliValue değişimidir.

Bu yüzden, orijinal dizilimi tekrar orijinal değerine döndürdüm. Bu satırları değiştirmeden önce ... Ayrıca, aynı bayt dizisine başvurmaktan kaçınmak için diziyi kopyalarım!

Dikkat: Burada rowversion değiştirmek için artan bir yaklaşım kullanıyorum, bu değeri doldurmak için kendi stratejinizi kullanmakta serbestsiniz. (Rasgele veya zaman tabanlı)

0

Sorun, nasıl kurulduğunu değil. Olan şey, RowVersion girişinizin OriginalValue girişinin Bağlamdan çıkar çıkmaz yeni değere ayarlanmasıdır.

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; 
İlgili konular