EF6 - 如何在运行时断言哪些列具有并发模式 = 固定
EF6 - How to assert at runtime which columns have Concurrency mode = fixed
在 Entity FrameworkEdmx 编辑器(例如 EF6)中,有时开发人员会完全删除一个实体(table),然后重新导入它(DB 到 C# 模型)。这通常会产生副作用,即删除在 Concurrency = fixed 等列上手动设置的属性。
有没有办法添加运行时检查来检查此类属性以验证它们是否仍按最初设计设置?尤其是并发模式。
如果无法进行运行时检查,也许可以进行编译时检查?
您可以自己做部分 classes。所有 EF classes 都是部分开箱即用的,因此您可以使用自己的部分 classes 扩展它们,因此在您的部分 classes 中定义您的自定义内容,这样它们就会持续存在即使生成的部分 class 被删除(并再次生成)
更新:正如 OP 所指出的,部分 classes 无法实现这一点。但是,这里是同一个问题,其答案是以编程方式执行 edmx 文件更新。它可能对您有帮助:
How to automate setting ConcurrencyMode=Fixed on all RowVersion columns?
通过修改@narayana () 对类似问题的回答构建了单元测试。感谢@jojess link.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace CompanyName.UnitTests
{
[TestClass]
public class EntityFreameworkDbTests
{
public TestContext TestContext { get; set; }
[TestMethod]
public void VerifyEdmxConcurrencyColumns()
{
const int minExpectedEdmxPathCount = 65;
// Expected Output: List all columns in all edmx that set ConcurrencyMode = fixed
//
List<Tuple<string, string, string>> expected = new List<Tuple<string, string, string>>
{
// EMDX, TableName, ColumnName
Tuple.Create("TheFileName.edmx", "ActivityLog", "Successes"),
Tuple.Create("TheFileName.edmx", "ActivityLog", "Fails")
};
// use top of source tree to include all edmx files
string serverSourceDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string [] edmxPaths = Directory.GetFiles(serverSourceDir, "*.edmx", SearchOption.AllDirectories);
Assert.IsTrue(edmxPaths.Count() >= minExpectedEdmxPathCount, "Under the expected min count of edmx paths: Actual = {0}, Expected = {1}"
.FormatExt(edmxPaths.Count(), minExpectedEdmxPathCount));
List<Tuple<string, string, string>> actual = new List<Tuple<string, string, string>>();
TestContext.WriteLine("All Actuals:");
foreach (string edmxPath in edmxPaths)
{
XDocument xmlDoc = XDocument.Load(edmxPath);
IEnumerable<XElement> concurrencyFixedColumns = xmlDoc.Descendants()
.Where(el => (string)el.Attribute("ConcurrencyMode") == "Fixed");
foreach (XElement el in concurrencyFixedColumns)
{
string value = el.Attribute("ConcurrencyMode")?.Value; //modified = true;
if (value != null)
{
string edmxFile = Path.GetFileName(edmxPath);
string tableName = (string)el.Parent.Attribute("Name");
actual.Add(Tuple.Create(edmxFile, tableName, (string)el.Attribute("Name")));
TestContext.WriteLine("\t{0},{1},{2}", edmxFile, tableName, el.Attribute("Name"));
}
}
}
StringBuilder errors = new StringBuilder();
actual.Except(expected).ToList().ForEach(edmxColumn =>
errors.AppendLine("Actual but not expected: " + edmxColumn.Item1 + "," + edmxColumn.Item2 + "," + edmxColumn.Item3));
expected.Except(actual).ToList().ForEach(edmxColumn =>
errors.AppendLine("Expected but not actual: " + edmxColumn.Item1 + "," + edmxColumn.Item2 + "," + edmxColumn.Item3));
Assert.AreEqual(0, errors.Length, "\n\nEF Concurrency=fixed columns:\n" + errors.ToString() + "\n");
}
}
}
在 Entity FrameworkEdmx 编辑器(例如 EF6)中,有时开发人员会完全删除一个实体(table),然后重新导入它(DB 到 C# 模型)。这通常会产生副作用,即删除在 Concurrency = fixed 等列上手动设置的属性。
有没有办法添加运行时检查来检查此类属性以验证它们是否仍按最初设计设置?尤其是并发模式。
如果无法进行运行时检查,也许可以进行编译时检查?
您可以自己做部分 classes。所有 EF classes 都是部分开箱即用的,因此您可以使用自己的部分 classes 扩展它们,因此在您的部分 classes 中定义您的自定义内容,这样它们就会持续存在即使生成的部分 class 被删除(并再次生成)
更新:正如 OP 所指出的,部分 classes 无法实现这一点。但是,这里是同一个问题,其答案是以编程方式执行 edmx 文件更新。它可能对您有帮助:
How to automate setting ConcurrencyMode=Fixed on all RowVersion columns?
通过修改@narayana () 对类似问题的回答构建了单元测试。感谢@jojess link.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace CompanyName.UnitTests
{
[TestClass]
public class EntityFreameworkDbTests
{
public TestContext TestContext { get; set; }
[TestMethod]
public void VerifyEdmxConcurrencyColumns()
{
const int minExpectedEdmxPathCount = 65;
// Expected Output: List all columns in all edmx that set ConcurrencyMode = fixed
//
List<Tuple<string, string, string>> expected = new List<Tuple<string, string, string>>
{
// EMDX, TableName, ColumnName
Tuple.Create("TheFileName.edmx", "ActivityLog", "Successes"),
Tuple.Create("TheFileName.edmx", "ActivityLog", "Fails")
};
// use top of source tree to include all edmx files
string serverSourceDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string [] edmxPaths = Directory.GetFiles(serverSourceDir, "*.edmx", SearchOption.AllDirectories);
Assert.IsTrue(edmxPaths.Count() >= minExpectedEdmxPathCount, "Under the expected min count of edmx paths: Actual = {0}, Expected = {1}"
.FormatExt(edmxPaths.Count(), minExpectedEdmxPathCount));
List<Tuple<string, string, string>> actual = new List<Tuple<string, string, string>>();
TestContext.WriteLine("All Actuals:");
foreach (string edmxPath in edmxPaths)
{
XDocument xmlDoc = XDocument.Load(edmxPath);
IEnumerable<XElement> concurrencyFixedColumns = xmlDoc.Descendants()
.Where(el => (string)el.Attribute("ConcurrencyMode") == "Fixed");
foreach (XElement el in concurrencyFixedColumns)
{
string value = el.Attribute("ConcurrencyMode")?.Value; //modified = true;
if (value != null)
{
string edmxFile = Path.GetFileName(edmxPath);
string tableName = (string)el.Parent.Attribute("Name");
actual.Add(Tuple.Create(edmxFile, tableName, (string)el.Attribute("Name")));
TestContext.WriteLine("\t{0},{1},{2}", edmxFile, tableName, el.Attribute("Name"));
}
}
}
StringBuilder errors = new StringBuilder();
actual.Except(expected).ToList().ForEach(edmxColumn =>
errors.AppendLine("Actual but not expected: " + edmxColumn.Item1 + "," + edmxColumn.Item2 + "," + edmxColumn.Item3));
expected.Except(actual).ToList().ForEach(edmxColumn =>
errors.AppendLine("Expected but not actual: " + edmxColumn.Item1 + "," + edmxColumn.Item2 + "," + edmxColumn.Item3));
Assert.AreEqual(0, errors.Length, "\n\nEF Concurrency=fixed columns:\n" + errors.ToString() + "\n");
}
}
}