Nach ein bisschen Untersuchung ich in der Lage war IsConcurrencyToken auf einem Byte [8] Spalte namens RowVersion in Entity Framework verwenden 6.
Da wir den gleichen Datentyp in DB2 verwenden möchten (der in der Datenbank selbst keine Zeilenversion hat), können wir die Option IsRowVersion() nicht verwenden!
Ich habe ein wenig weiter untersucht, wie man mit IsConcurrencyToken arbeitet.
Ich habe die folgende, eine Lösung zu erreichen, die zu funktionieren scheint:
My Model:
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 verwendet Entitäten zu identifizieren, die eine rowversion haben, die eine besondere Behandlung benötigt.
habe ich fließend API Modelbuilder zu konfigurieren:
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(e => e.RowVersion).IsFixedLength().HasMaxLength(8).IsConcurrencyToken();
}
}
Und schließlich Ich habe eine Methode, um meine abgeleitet DbContext Klasse das Feld zu aktualisieren, bevor die base.SaveChanges aufgerufen:
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!!
}
}
}
}
Das Problem, auf das die meisten Leute stoßen, ist, dass wir nach dem Setzen des Wertes der Entity immer eine UpdateDBConcurrencyException erhalten, weil sich der OriginalValue geändert hat ... auch wenn es nicht passiert ist!
Der Grund ist, dass für ein Byte [] sowohl Original als auch CurrentValue sich ändern, wenn Sie den CurrentValue allein einstellen (seltsames und unerwartetes Verhalten).
Also setze ich den OriginalValue wieder auf den ursprünglichen Wert, bevor ich die Zeilenversion aktualisiere ... Auch ich kopiere das Array, um die Referenzierung der gleichen Byte-Array zu vermeiden!
Achtung: Hier verwende ich einen inkrementellen Ansatz, um die Zeilenversion zu ändern, Sie können Ihre eigene Strategie verwenden, um diesen Wert einzugeben. (Zufällig oder zeitbasiert)
Jeder? :/Plx. –
* Produkttabelle wird nicht aktualisiert *. Es ist tatsächlich, mit einem Dummy-Update, das die Zeilenversion erhöht (EF 6.1.3). –
Hallo @GertArnold. Bei meinen Tests (EF6.1.3) wird die Zeilenversion nicht erhöht. –