Как автоматизировать установку ConcurrencyMode = Исправлено во всех столбцах RowVersion?

EF по умолчанию не имеет управления concurrency (последние победы записи), что позволяет потерять обновления. Выполнение оптимистичных проверок concurrency можно явно настроить, установив ConcurrencyMode = Исправлено в столбце RowVersion.

Как мы можем автоматизировать установку ConcurrencyMode = Исправлено в столбцах RowVersion во всех таблицах? Чтобы сделать это вручную при воссоздании EF-модели из базы данных, мы рискуем забыть ее без управления concurrency.

Ответ 1

Это похоже на ответ Мохамеда Кассима, но я обновил код для использования поиска атрибутов XML и заменил вместо замены строки, поскольку дизайнер может изменить порядок атрибутов, или другие свойства могут иметь разные значения.

Сохраните это как FixVersionColumnConcurrencyMode.cs, запустите csc FixVersionColumnConcurrencyMode.cs и запустите полученный файл FixVersionColumnConcurrencyMode.exe в той же папке, что и файл .edmx. Вы также можете выполнить выполнение пост-сборки проекта.

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);
            }
        }
    }
}

Ответ 3

Похоже, эта функция не будет использоваться для EF 5 или EF 6.

Я взломал быстрое консольное приложение, чтобы обновить edmx после генерации базы данных сначала.

Просто отпустите файл в том же каталоге вашего файла edmx и запустите после каждой регенерации.

Будет работать для любого из следующих столбцов:

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

Здесь вы можете получить консольное приложение https://dl.dropbox.com/u/3576345/EFConcurrencyFixed.exe

или используйте этот фрагмент кода в своем консольном приложении.

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);
        }
    }
}

Ответ 4

Я не делал этого сам (но, скорее всего, скоро это нужно), но должно быть возможно изменить инструмент, используемый для генерации кода из .edmx.

Здесь - статья, объясняющая это для VS2008. Я думаю, что этот процесс будет примерно таким же для VS2010 и VS2012.

В настоящее время я не уверен, сможет ли это сделать галочку с помощью ConcurrencyMode.

Ответ 5

Когда вы добавили ConcurrencyMode=Fixed один раз в свой файл edmx, а затем каждый раз, когда вы обновляете свою модель, перед регистрацией кода (при условии, что у вас есть версия TFS для управления версиями), сравните с последней версией и смените изменения соответственно, Таким образом, вам не нужно обновлять каждую строку вручную. Не лучший способ, но лучше, чем делать это вручную по крайней мере.

Ответ 6

Мне стало скучно с установкой ConcurrencyMode вручную, поэтому я написал небольшую утилиту для ее автоматизации. Вы можете использовать его для установки режима для определенных типов (timestamp/rowversion) или для имен столбцов, соответствующих определенным шаблонам регулярных выражений.

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

https://github.com/wezeku/FixEFConcurrencyModes

Ответ 7

Это ответ Мохамеда Кассима, но он работает для 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");
        }
    }