EF Code First (Oracle) Optimistic Concurrency

Discussion of open issues, suggestions and bugs regarding Entity Framework support in ADO.NET Data providers
Post Reply
Eelyen
Posts: 1
Joined: Mon 24 Jan 2011 04:26

EF Code First (Oracle) Optimistic Concurrency

Post by Eelyen » Wed 04 May 2011 18:12

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

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Post by Shalex » Fri 06 May 2011 06:44

We will investigate the Concurrency issue with Code First and notify you about the results as soon as possible.

StanislavK
Devart Team
Posts: 1710
Joined: Thu 03 Dec 2009 10:48

Post by StanislavK » Fri 06 May 2011 14:41

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.

JoeRuspante
Posts: 54
Joined: Mon 05 Jul 2010 23:08

Re: EF Code First (Oracle) Optimistic Concurrency

Post by JoeRuspante » Mon 03 Sep 2012 19:03

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

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Re: EF Code First (Oracle) Optimistic Concurrency

Post by Shalex » Thu 06 Sep 2012 14:30

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.

Post Reply