Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle EF 4.3.1 setting Modified a Rowversion Row

Say we have:

public class Driver
{
    public int driverID { get; set; }
    public byte[] stamp { get; set; }
    public string name { get; set; }
    public string prename { get; set; }
}

Now in short I`m facing this kind of situation.

 ...
 var myDriver = myCustomDBContext.Drivers.AsNoTracking()
                                         .Where(d => d.driverID == driverID)
                                         .SingleOrDefault();
 ...
 myDriver.name = "John";
 myDriver.prename = "Lennon";
 ...
 myCustomDBContext.Drivers.Attach(myDriver);
 myCustomDBContext.Entry(myDriver).State = EntityState.Modified;
 myCustomDBContext.SaveChanges();
 ...

And the result is

 The column cannot be modified because it is an identity, rowversion or 
     a system column. [Column name = stamp]

Is there any method which could force an Update on a detached entity, or a workaround for this rowversion column not to be set as Modified.

like image 741
Ciobanu Ion Avatar asked Apr 27 '12 15:04

Ciobanu Ion


1 Answers

It looks like you didn't have specified the stamp property as a rowversion in your model and it is just a binary field. You can specify it with Fluent API:

modelBuilder.Entity<Driver>().Property(d => d.stamp)
    .IsRowVersion()
    .IsConcurrencyToken(false);

The code above is for the case when you don't want to have the stamp property as a concurrency token. (A rowversion is a concurrency token by default, so you have to disable it explicitly.) If you want to have it as concurrency token, then you can use with Fluent API...

modelBuilder.Entity<Driver>().Property(d => d.stamp)
    .IsRowVersion();

...or with data annotations:

[Timestamp]
public byte[] stamp { get; set; }

This should prevent EF to write an UPDATE for this property.

Edit

If you use Database-First strategy the [Timestamp] attribute does not work. This attribute is only for Code-First development.

When you use Database-First the connection string contains a metadata section refering to the EDM defined in the EDMX file:

connectionString="metadata=res://*/Model1.csdl
                          |res://*/Model1.ssdl
                          |res://*/Model1.msl;
                          ...
                          ..."

If Entity Framework finds this section in the connection string it doesn't use data annotations on model properties nor does it process any code in Fluent API (OnModelCreating isn't called at all). Instead it loads the mapping definitions from the embedded and compiled EDMX file.

That means, if you want to define the stamp property as a concurrency token you must do this in the EDMX file. In XML it would look like this:

In SSDL section:

<Property Name="stamp" Type="timestamp" Nullable="false"
          StoreGeneratedPattern="Computed" />

In CSDL section:

<Property Name="stamp" Type="Binary" Nullable="false" MaxLength="8"
          FixedLength="true"
          annotation:StoreGeneratedPattern="Computed"
          ConcurrencyMode="Fixed" />

You can also define this in the model designer in Visual Studio: Mark the stamp property in the entity in the designer surface, go to the Properties Window and set "Concurrency Mode" to "Fixed" (and also set the "StoreGeneratedPattern" to "Computed").

You can also remove the metadata section from the connection string. But this effectively means that you switch from database first to code first development. Then all attributes and Fluent API will be respected, but not any definitions in EDMX anymore.

like image 56
Slauma Avatar answered Sep 21 '22 10:09

Slauma