11

EF 默认为无并发控制(最后写入获胜),这允许丢失更新。可以通过在 RowVersion 列上设置 ConcurrencyMode=Fixed 来显式配置强制执行乐观并发检查。

我们如何在所有表的 RowVersion 列上自动设置 ConcurrencyMode=Fixed?从数据库重新创建 EF 模型时必须手动执行此操作,我们可能会忘记它在没有并发控制的情况下运行。

4

7 回答 7

7

这类似于 Mohamed Cassim 的答案,但我更新了代码以使用 XML 属性搜索和替换,而不是字符串替换,因为设计者可以更改属性的顺序,或者其他属性可以具有不同的值。

将其另存为FixVersionColumnConcurrencyMode.cs、运行csc FixVersionColumnConcurrencyMode.cs,然后在与 .edmx 文件相同的文件夹中运行生成的 FixVersionColumnConcurrencyMode.exe。您还可以使其执行项目的后期构建。

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);
            }
        }
    }
}
于 2014-02-18T19:45:32.760 回答
5

似乎此功能不会出现在 EF 5 或 EF 6 中。

在生成 DB First 后,我​​启动了一个快速控制台应用程序来更新 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);
        }
    }
}
于 2013-03-27T16:18:14.113 回答
4

在 EF6 中,当从数据库创建模型时,设计器将在 rowversion 列上设置 ConcurrencyMode=Fixed。请参阅Designer: Automate setting ConcurrencyMode=Fixed on rowversion columns。在此之前,我们将不得不手动执行此操作。

于 2012-10-18T16:40:21.903 回答
1

我自己还没有完成(还没有,但可能很快需要),但应该可以更改用于从 .edmx 生成代码的工具。

是一篇为 VS2008 解释它的文章。我想VS2010和VS2012的过程大致相同。

不过,我目前不确定这是否可以以任何方式处理 ConcurrencyMode。

于 2012-10-10T12:11:08.117 回答
0

我对手动设置 ConcurrencyMode 感到厌烦,所以我编写了一个小实用程序来自动化它。您可以使用它为某些类型(时间戳/行版本)或匹配某些正则表达式模式的列名设置模式。

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

https://github.com/wezeku/FixEFConcurrencyModes

于 2014-04-28T16:02:58.880 回答
0

当您ConcurrencyMode=Fixed在 edmx 文件中添加一次,然后每次更新模型时,在签入代码之前(假设您有一些版本控制寿命 TFS 设置),与最新版本进行比较并相应地合并更改。这样您就不必手动更新每一行。不是最好的方法,但至少比手动操作要好。

于 2015-08-23T08:31:42.317 回答
0

这是 Mohamed Cassim 的答案,但它适用于 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");
        }
    }
于 2017-06-27T14:53:27.570 回答