11

EF defaults to no concurrency control (last write wins) which allows lost updates. Enforcing optimistic concurrency checks can explicitly be configured by setting ConcurrencyMode=Fixed on a RowVersion column.

How can we automate setting ConcurrencyMode=Fixed on RowVersion columns in all tables? Having to do this manually when recreating an EF model from a database we risk forgetting it an running without concurrency control.

4
  • 1
    An XSLT in your build process? Commented Oct 4, 2012 at 17:18
  • 1
    I was hoping for something builtin that escaped me, maybe something by convention. This is is a standard requirement. Most DB apps require concurrency control. Commented Oct 4, 2012 at 18:22
  • 1
    I do not understand why such a basic feature does not exist. Made a feature request at EF CodePlex Automate setting ConcurrencyMode=Fixed Commented Oct 14, 2012 at 12:30
  • Almost 1½ year later, MS still hasn't fixed this, so I wrote a small helper class for writing unit tests in order not to forget setting ConcurrencyMode=Fixed. It's not what you asked for, but maybe it could be of some use. nuget.org/packages/EFConcurrencyModeTest Commented Mar 10, 2014 at 0:00

7 Answers 7

7

This is similar to the answer by Mohamed Cassim, but I've updated the code to use XML attribute search and replace, instead of string replace, as the designer can change the order of attributes, or other properties can have different values.

Save this as FixVersionColumnConcurrencyMode.cs, run csc FixVersionColumnConcurrencyMode.cs, and run the resulting FixVersionColumnConcurrencyMode.exe in the same folder as the .edmx file. You can also make it execute post build of the project.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

namespace Utility
{
    internal class FixVersionColumnConcurrencyMode
    {
        private static void Main(string[] args)
        {
            string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var files = Directory.GetFiles(directoryPath, "*.edmx");
            foreach (var file in files)
            {
                XDocument xmlDoc = XDocument.Load(file);

                IEnumerable<XElement> versionColumns =
                    from el in xmlDoc.Descendants()
                    where (string)el.Attribute("Name") == "Version"
                    && (string)el.Attribute("Type") == "Binary"
                    && (string)el.Attribute("ConcurrencyMode") != "Fixed"
                    select el;
                bool modified = false;
                foreach (XElement el in versionColumns)
                {
                    modified = true;
                    el.SetAttributeValue("ConcurrencyMode", "Fixed");
                }
                if (modified)
                    xmlDoc.Save(file);
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Consider changing the first where clause to where el.Attribute("Name") != null && el.Attribute("Name").Value.Contains("RowVersion") to cover a larger spectrum of column names.
5

Seems like this Feature is not going to be around for EF 5 or EF 6.

I whipped up a quick console app to update the edmx after generating DB First.

Just drop the file in the same directory of your edmx file and run after every regeneration.

Will work for any of the following columns:

RowVersion    timestamp    NOT NULL
rowversion    timestamp    NOT NULL
RowVer        timestamp    NOT NULL
rowver        timestamp    NOT NULL

You can get the console app here https://dl.dropbox.com/u/3576345/EFConcurrencyFixed.exe

or use this piece of code in your own console app.

class Program
{
    static Dictionary<string, string> replacements = new Dictionary<string, string>()
    {
        { "<Property Type=\"Binary\" Name=\"RowVersion\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Type=\"Binary\" Name=\"RowVersion\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Type=\"Binary\" Name=\"rowversion\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Type=\"Binary\" Name=\"rowversion\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Type=\"Binary\" Name=\"RowVer\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Type=\"Binary\" Name=\"RowVer\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Type=\"Binary\" Name=\"rowver\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Type=\"Binary\" Name=\"rowver\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},
    };

    static void Main(string[] args)
    {
        // find all .edmx
        string directoryPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        foreach (var file in Directory.GetFiles(directoryPath))
        {
            // only edmx
            if (!file.EndsWith(".edmx"))
                continue;

            // read file
            var fileContents = System.IO.File.ReadAllText(file);

            // replace lines
            foreach (var item in replacements)
                fileContents = fileContents.Replace(item.Key, item.Value);

            // overwite file
            System.IO.File.WriteAllText(file, fileContents);
        }
    }
}

Comments

4

In EF6 the designer will set ConcurrencyMode=Fixed on rowversion columns when creating a model from the database.See Designer: Automate setting ConcurrencyMode=Fixed on rowversion columns. Until then we will have to do this manually.

1 Comment

Unfortunately the latest status post by RoMiller was "We are only taking minimal changes to the designer in EF6 because we are still converting the code base to open source. We'll consider this bug for the next release."
1

I have not done it myself (yet, but will probably need to soon), but it should be possible to change the tool used for generating code from the .edmx.

Here is an article explaining it for VS2008. I guess the process will be about the same for VS2010 and VS2012.

I'm not currently sure, though, whether this will make it possible to muck around with the ConcurrencyMode in any way.

Comments

0

I got bored with setting ConcurrencyMode manually, so I wrote a tiny utility to automate it. You can either use it to set the mode for certain types (timestamp/rowversion) or for column names matching certain regex patterns.

http://blog.wezeku.com/2014/04/28/fixefconcurrencymodes/

https://github.com/wezeku/FixEFConcurrencyModes

Comments

0

When you have added ConcurrencyMode=Fixed once in your edmx file, then afterwards every time you update your model, before checkin in code (assuming you have some version control life TFS setup), compare with the latest version and merge changes accordingly. This way you dont have to update every single line manually. Not the best way, but better than doing it manually atleast.

Comments

0

This is Mohamed Cassim answer but it's working for EF6

static Dictionary<string, string> replacements = new Dictionary<string, string>()
    {
        { "<Property Name=\"RowVersion\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"RowVersion\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"rowversion\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"rowversion\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"RowVer\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"RowVer\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"rowver\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"rowver\" Type=\"Binary\" Nullable=\"false\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"RowVersion\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"RowVersion\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"rowversion\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"rowversion\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"RowVer\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"RowVer\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},

        { "<Property Name=\"rowver\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" />",
          "<Property Name=\"rowver\" Type=\"Binary\" MaxLength=\"8\" FixedLength=\"true\" annotation:StoreGeneratedPattern=\"Computed\" ConcurrencyMode=\"Fixed\" />"},
    };

    static void Main(string[] args)
    {
        // find all .edmx
        string directoryPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        foreach (var file in Directory.GetFiles(directoryPath))
        {
            // only edmx
            if (!file.EndsWith(".edmx"))
                continue;

            Console.WriteLine("File Name Found : " + file);

            // read file
            var fileContents = File.ReadAllText(file);

            // replace lines
            foreach (var item in replacements)
                fileContents = fileContents.Replace(item.Key, item.Value);

            // overwite file
            File.WriteAllText(file, fileContents);

            Console.WriteLine("\nFile : " + file + "Changed");
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.