Page 1 of 1

EF Code First (Oracle) Optimistic Concurrency

Posted: Wed 04 May 2011 18:12
by Eelyen
By using the Devart CodeFirst article and the ASP.NET MVC Entity Framework tutorials (Namely the one on concurrency) I was able to quickly figure out how to get CodeFirst working with dotConnect.

First off, kudos on the awesome support. It works practically exactly like an out of the box sql server. Only needed a few obvious changes to point it at oracle.

However, I was interested in the Concurrency example that is used on the ASP.NET MVC entity framework tutorials. In those examples, they use a [Timestamp] attribute on byte[] property

Code: Select all

[Timestamp]
public byte[] Timestamp { get; set; }
Now, the timestamp attribute attempts to use a rowversion column when using Sql Server, however under Oracle you receive the following error.
System.Data.MetadataException: Schema specified is not valid. Errors: (21, 6) : error 0040: The Type rowversion is not qualified with a namespace or alias. Only PrimitiveTypes can be used without qualification.
I looked around the forums and found a few posts on Concurrency, but mainly dealing with Model-First or Database First and recommending that a auto incrementing trigger is added to the concurrency tracking column. Which seems all well and good, but that doesn't work as well with Code First. So, I'm wondering if there is another way to do this via Code First with dotConnect?

Thanks in advance

Posted: Fri 06 May 2011 06:44
by Shalex
We will investigate the Concurrency issue with Code First and notify you about the results as soon as possible.

Posted: Fri 06 May 2011 14:41
by StanislavK
I will send you a sample project with a possible implementation of this scenario. Please check that the letter is not blocked by your mail filter.

Suppose we have the following entity class (we do not set the Timestamp attribute on the version property):

Code: Select all

public class Concur {
  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public long Id { get; set; }

  [MaxLength(32)]
  public string Title { get; set; }

  public Byte[] Timestamp { get; set; }
}
Declare an interim DbContext descendant:

Code: Select all

public class MyDbContext : DbContext {
  protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    //modelBuilder.Conventions.Remove<System.Data.Entity.Infrastructure.IncludeMetadataConvention>();
  }

  public DbSet Concurs { get; set; }
}
We will then inherit two context classes, one for each DBMS, from the MyDbContext class:

Code: Select all

public class MySqlContext : MyDbContext {

  protected override void OnModelCreating(DbModelBuilder modelBuilder) {

    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Concur>().Property(p => p.Timestamp)
      .IsRowVersion();
  }
}

public class MyOracleContext : MyDbContext {

  public MyOracleContext(): base() {

    ((IObjectContextAdapter)this).ObjectContext.SavingChanges += new EventHandler(ObjectContext_SavingChanges);
  }

  protected override void OnModelCreating(DbModelBuilder modelBuilder) {

    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Concur>().Property(p => p.Timestamp)
      .HasMaxLength(16)
      .IsConcurrencyToken();

    modelBuilder.Conventions.Remove();
  }

  static void ObjectContext_SavingChanges(object sender, EventArgs e) {

    ObjectContext context = (ObjectContext)sender;

    var addedOrModified = context.ObjectStateManager
      .GetObjectStateEntries(EntityState.Added | EntityState.Modified)
      .Where(entry => entry.Entity is Concur)
      .Select(entry => (Concur)entry.Entity)
      .ToList();

    foreach (Concur item in addedOrModified) {
      item.Timestamp = Guid.NewGuid().ToByteArray();
    }

    context.DetectChanges();
  }
}
Notice that the MySqlContext class relies on the server functionality, whereas MyOracleContext needs to create new GUIDs on its own at the client side.

The following code shows the possible scenario where a concurrency exception is thrown:

Code: Select all

Concur newCon = new Concur { Title = "Original" };
context.Concurs.Add(newCon);
context.SaveChanges(); // Inserted successfully
      
var originalItem = anotherContext.Concurs
  .Where(c => c.Title == "Original")
  .Single();
originalItem.Title = "First modification";
anotherContext.SaveChanges(); // Updated successfully

try {
  newCon.Title = "Second modification";
  context.SaveChanges(); // DbUpdateConcurrencyException thrown
}
catch (DbUpdateConcurrencyException ex) {
  Console.WriteLine(ex.Message);
}
Please tell us if this helps.

Re: EF Code First (Oracle) Optimistic Concurrency

Posted: Mon 03 Sep 2012 19:03
by JoeRuspante
Hi.

I have the same problem on the new version of DotConnect for Oracle.
Is it a problem of Oracle Database?

Thank you in advance

Re: EF Code First (Oracle) Optimistic Concurrency

Posted: Thu 06 Sep 2012 14:30
by Shalex
Oracle doesn't have the rowversion column data type.
General considerations concerning concurrency check are available at http://www.infoq.com/articles/multiple-databases, the Concurrency Check section.