【发布时间】:2012-03-15 19:25:41
【问题描述】:
我正在处理的数据库具有诸如“table_name”之类的表名。这很好,但我想生成格式为“TableName”的类,以便在 C#、Pascal 风格中使用。
这可能吗?
【问题讨论】:
标签: entity-framework entity-framework-4
我正在处理的数据库具有诸如“table_name”之类的表名。这很好,但我想生成格式为“TableName”的类,以便在 C#、Pascal 风格中使用。
这可能吗?
【问题讨论】:
标签: entity-framework entity-framework-4
更新:要与 EF6 一起使用,请参阅此页面上的其他答案。
感谢Alex's answer,我现在已将此代码扩展为解决此问题的完整工作解决方案。由于我花了一天的大部分时间,所以我在这里发帖以帮助其他面临同样挑战的人。这包括一个用于操作 edmx 文件的完整类(这里没有漂亮的代码),它通过了对不同输入字符串的相当详细的单元测试:
几个例子:
some_class > SomeClass
_some_class_ > SomeClass
some_1_said > Some1Said
我还有一些其他问题需要处理:
首先可选地不替换下划线,同时仍将字符串更改为帕斯卡大小写(由于“test_string”和“teststring”等列名,否则它们都解析为“TestString”,导致冲突)。
其次,我这里的代码采用了一个输入参数,该参数修改了生成的对象上下文类名。
最后,我最初不确定如何在修改 edmx 后更新设计器文件,所以我已经包含了确切的步骤。我建议在 Visual Studio 中创建一个预构建步骤,这样可以节省一些从修改后的数据库架构进行更新的剩余工作。
更新 EDMX 文件以反映数据库更改
双击 edmx 文件以显示设计图面。
右键单击设计图面并选择“从数据库更新模型”。
选择“是在连接字符串中包含敏感信息”。
取消选中“在 App Config 中保存实体连接设置”(我们已经有了这些)。
选择适当的数据库。在添加屏幕中选择表格、视图等。
将复数和外键选项保留为选中状态。如果从新鲜创建 edmx 文件,您 可以选择输入型号名称。您可以更改或保留它。
点击完成。
在 edmx 文件上运行以下代码。
根据应用程序的状态,您可能会在此处看到错误,直到下一步。
右键单击要更新的 edmx 文件,然后在 Visual Studio 中选择“运行自定义工具”。这将导致 要更新的 Designer.cs (C#) 文件。
运行构建以检查没有编译器错误。
运行测试以确保应用程序正常运行。
根据应用程序应预料到以下任何应用程序问题 已进行的更改。
替换整个 edmx 文件。
删除 edmx 文件,同时带上设计器文件。
右键实体文件夹
从创建文件对话框中,选择 ADO.NET 实体数据模型。根据您希望用于对象上下文的类名来命名它(重要)。 此值在连接字符串中引用,因此请查看您的应用配置以防出现问题。
在选择模型内容中,选择从数据库生成。
按照上述步骤 3 中的说明进行操作。
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace EdmxStringFormatter
{
public class Program
{
static void Main(string[] args)
{
if (args.Length < 1) return;
string filePath = args[0];
string entityName = null;
// Optionally do not replace underscores which
// helps with naming collisions with siimilarly named
// columns on some database tables.
bool replaceUnderscores = true;
// Allow for the replacement of the object context class name, which is useful
// where multiple databases have edmx files.
bool doEntityNameReplace = false;
if (args.Length > 1)
{
entityName = args[1];
doEntityNameReplace = true;
}
if (args.Length > 2)
{
replaceUnderscores = args[2] != "0";
}
if (!File.Exists(filePath))
{
StopWithMessage("Could not find specified file.");
return;
}
if (Path.GetExtension(filePath) != ".edmx")
{
StopWithMessage("This works only on EDMX files.");
return;
}
// Processing:
Console.WriteLine("Creating backup: " + Path.ChangeExtension(filePath, ".bak"));
File.Copy(filePath, Path.ChangeExtension(filePath, ".bak"), true);
Console.WriteLine("Reading target document...");
XDocument xdoc = XDocument.Load(filePath);
const string CSDLNamespace = "http://schemas.microsoft.com/ado/2008/09/edm";
const string MSLNamespace = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
const string DiagramNamespace = "http://schemas.microsoft.com/ado/2008/10/edmx";
const string CSNameSpace = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
XElement csdl = xdoc.Descendants(XName.Get("Schema", CSDLNamespace)).First();
XElement msl = xdoc.Descendants(XName.Get("Mapping", MSLNamespace)).First();
XElement designerDiagram = xdoc.Descendants(XName.Get("Diagram", DiagramNamespace)).First();
//modifications for renaming everything, not just table names:
#region CSDL2
Console.WriteLine("Modifying CSDL...");
Console.WriteLine(" - modifying entity sets...");
foreach (var entitySet in csdl.Element(XName.Get("EntityContainer", CSDLNamespace)).Elements(XName.Get("EntitySet", CSDLNamespace)))
{
entitySet.Attribute("Name").Value = FormatString(entitySet.Attribute("Name").Value, replaceUnderscores);
entitySet.Attribute("EntityType").Value = FormatString(entitySet.Attribute("EntityType").Value, replaceUnderscores);
}
Console.WriteLine(" - modifying association sets...");
foreach (var associationSet in csdl.Element(XName.Get("EntityContainer", CSDLNamespace)).Elements(XName.Get("AssociationSet", CSDLNamespace)))
{
foreach (var end in associationSet.Elements(XName.Get("End", CSDLNamespace)))
{
end.Attribute("EntitySet").Value = FormatString(end.Attribute("EntitySet").Value, replaceUnderscores);
}
}
Console.WriteLine(" - modifying entity types...");
foreach (var entityType in csdl.Elements(XName.Get("EntityType", CSDLNamespace)))
{
entityType.Attribute("Name").Value = FormatString(entityType.Attribute("Name").Value, replaceUnderscores);
foreach (var key in entityType.Elements(XName.Get("Key", CSDLNamespace)))
{
foreach (var propertyRef in key.Elements(XName.Get("PropertyRef", CSDLNamespace)))
{
propertyRef.Attribute("Name").Value = FormatString(propertyRef.Attribute("Name").Value, replaceUnderscores);
}
}
foreach (var property in entityType.Elements(XName.Get("Property", CSDLNamespace)))
{
property.Attribute("Name").Value = FormatString(property.Attribute("Name").Value, replaceUnderscores);
}
foreach (var navigationProperty in entityType.Elements(XName.Get("NavigationProperty", CSDLNamespace)))
{
navigationProperty.Attribute("Name").Value = FormatString(navigationProperty.Attribute("Name").Value, replaceUnderscores);
}
}
Console.WriteLine(" - modifying associations...");
foreach (var association in csdl.Elements(XName.Get("Association", CSDLNamespace)))
{
foreach (var end in association.Elements(XName.Get("End", CSDLNamespace)))
{
end.Attribute("Type").Value = FormatString(end.Attribute("Type").Value, replaceUnderscores);
}
foreach (var propref in association.Descendants(XName.Get("PropertyRef", CSDLNamespace)))
{
//propertyrefs are contained in constraints
propref.Attribute("Name").Value = FormatString(propref.Attribute("Name").Value, replaceUnderscores);
}
}
#endregion
#region MSL2
Console.WriteLine("Modifying MSL...");
Console.WriteLine(" - modifying entity set mappings...");
foreach (var entitySetMapping in msl.Element(XName.Get("EntityContainerMapping", MSLNamespace)).Elements(XName.Get("EntitySetMapping", MSLNamespace)))
{
entitySetMapping.Attribute("Name").Value = FormatString(entitySetMapping.Attribute("Name").Value, replaceUnderscores);
foreach (var entityTypeMapping in entitySetMapping.Elements(XName.Get("EntityTypeMapping", MSLNamespace)))
{
entityTypeMapping.Attribute("TypeName").Value = FormatString(entityTypeMapping.Attribute("TypeName").Value, replaceUnderscores);
foreach
(var scalarProperty in
(entityTypeMapping.Element(XName.Get("MappingFragment", MSLNamespace))).Elements(XName.Get("ScalarProperty", MSLNamespace))
)
{
scalarProperty.Attribute("Name").Value = FormatString(scalarProperty.Attribute("Name").Value, replaceUnderscores);
}
}
}
Console.WriteLine(" - modifying association set mappings...");
foreach (var associationSetMapping in msl.Element(XName.Get("EntityContainerMapping", MSLNamespace)).Elements(XName.Get("AssociationSetMapping", MSLNamespace)))
{
foreach (var endProperty in associationSetMapping.Elements(XName.Get("EndProperty", MSLNamespace)))
{
foreach (var scalarProperty in endProperty.Elements(XName.Get("ScalarProperty", MSLNamespace)))
{
scalarProperty.Attribute("Name").Value = FormatString(scalarProperty.Attribute("Name").Value, replaceUnderscores);
}
}
}
#endregion
#region Designer
Console.WriteLine("Modifying designer content...");
foreach (var item in designerDiagram.Elements(XName.Get("EntityTypeShape", DiagramNamespace)))
{
item.Attribute("EntityType").Value = FormatString(item.Attribute("EntityType").Value, replaceUnderscores);
}
#endregion
// Optionally replace the entity name in case the default of "Entity" is not
// sufficient for your needs.
if (doEntityNameReplace)
{
Console.WriteLine("Modifying entity name refs...");
// CSDL
xdoc.Descendants(XName.Get("EntityContainer", CSDLNamespace)).First().Attribute("Name").Value = entityName;
// Diagram
xdoc.Descendants(XName.Get("Diagram", DiagramNamespace)).First().Attribute("Name").Value = entityName;
// Diagram
xdoc.Descendants(XName.Get("EntityContainerMapping", CSNameSpace)).First().Attribute("CdmEntityContainer").Value = entityName;
}
Console.WriteLine("Writing result...");
using (XmlTextWriter writer = new XmlTextWriter(filePath, Encoding.Default))
{
writer.Formatting = Formatting.Indented;
xdoc.WriteTo(writer);
}
}
/// <summary>
/// Formats the string to pascal case, additionally checking for a period
/// in the string (in which case it skips past the period, which indicates
/// the use of namespace in a string.
/// </summary>
/// <param name="str"></param>
/// <param name="replaceUnderscores"></param>
/// <returns></returns>
private static string FormatString(string str, bool replaceUnderscores = true)
{
char[] chars = str.ToCharArray();
var sb = new StringBuilder();
bool previousCharWasUpper = false;
bool lastOperationWasToLower = false;
int startPos = 0;
if (str.Contains("."))
{
if (str.IndexOf(".") < (str.Length - 1))
{
startPos = str.IndexOf(".") + 1;
}
sb.Append(str.Substring(0, startPos));
}
for (int i = startPos; i < chars.Length; i++)
{
char character = chars[i];
if (Char.IsLetter(character))
{
if (Char.IsLower(character))
{
bool toUpper = false;
if (i > 0)
{
// Look at the previous char to see if not a letter
if (!Char.IsLetter(chars[i - 1]))
{
toUpper = true;
}
}
if (i == 0 || toUpper)
{
character = Char.ToUpper(character);
lastOperationWasToLower = false;
}
}
else // IsUpper = true
{
if (previousCharWasUpper || lastOperationWasToLower)
{
character = Char.ToLower(character);
lastOperationWasToLower = true;
}
}
previousCharWasUpper = Char.IsUpper(character);
sb.Append(character);
}
else
{
if (Char.IsDigit(character))
{
sb.Append(character);
previousCharWasUpper = false;
lastOperationWasToLower = false;
}
else if(!replaceUnderscores)
{
if(character == '_')
{
sb.Append(character);
}
}
}
}
return sb.ToString();
}
private static void StopWithMessage(string str)
{
Console.WriteLine(str);
Console.ReadLine();
throw new InvalidOperationException("Cannot continue.");
}
}
}
适应 Visual Studio 2013 & EF6
代码命名空间需要稍作调整才能与 EF6 一起使用:
const string CSDLNamespace = "http://schemas.microsoft.com/ado/2009/11/edm";
const string MSLNamespace = "http://schemas.microsoft.com/ado/2009/11/mapping/cs";
const string DiagramNamespace = "http://schemas.microsoft.com/ado/2009/11/edmx";
const string CSNameSpace = "http://schemas.microsoft.com/ado/2009/11/mapping/cs";
另外,您需要注意 DesignerDiagram(在我的情况下,它没有找到,所以只需将 First() 替换为 FirstOrDefault() 并添加简单的空检查)。
【讨论】:
这是在 Entity Framework 6 上下文中使用的完整 C# .edmx 修改代码(原始答案适用于 EF4)。
(由于每个答案的字符数限制,附加答案而不是编辑)
感谢克里斯的意见。我最近重新审视了这个,因为使用此工具的项目已升级到 EF6。下面复制了用于 EF6 的完整代码。
请注意,此程序代码现在在两个文件上运行 - .edmx 和 .edmx.diagram 文件。 Visual Studio 2013 将图表拆分为单独的文件,这需要编辑,否则表格/实体表示将不会显示在 .edmx 设计器表面上。设计器文件的文件路径被接受为第二个参数。
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace EdmxStringFormatter
{
public class Program
{
static void Main(string[] args)
{
if (args.Length < 1) return;
string filePath = args[0];
string designerFilePath = null;
string entityName = null;
// Optionally do not replace underscores which
// helps with naming collisions with siimilarly named
// columns on some database tables.
bool replaceUnderscores = true;
// Allow for the replacement of the object context class name, which is useful
// where multiple databases have edmx files.
bool doEntityNameReplace = false;
if (args.Length > 1)
{
designerFilePath = args[1];
}
if (args.Length > 2)
{
entityName = args[2];
doEntityNameReplace = true;
}
if (args.Length > 3)
{
replaceUnderscores = args[3] != "0";
}
if (!File.Exists(filePath))
{
StopWithMessage("Could not find specified file.");
return;
}
if (Path.GetExtension(filePath) != ".edmx")
{
StopWithMessage("This works only on EDMX files.");
return;
}
TransformEdmx(filePath, replaceUnderscores, doEntityNameReplace, entityName);
TransformEdmxDiagram(designerFilePath, replaceUnderscores);
}
private static void TransformEdmx(string filePath, bool replaceUnderscores, bool doEntityNameReplace, string entityName)
{
// Processing:
Console.WriteLine("Creating backup: " + Path.ChangeExtension(filePath, ".bak"));
File.Copy(filePath, Path.ChangeExtension(filePath, ".bak"), true);
Console.WriteLine("Reading target document...");
XDocument xdoc = XDocument.Load(filePath);
//const string CSDLNamespace = "http://schemas.microsoft.com/ado/2008/09/edm";
//const string MSLNamespace = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
//const string DiagramNamespace = "http://schemas.microsoft.com/ado/2008/10/edmx";
//const string CSNameSpace = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
const string CSDLNamespace = "http://schemas.microsoft.com/ado/2009/11/edm";
const string MSLNamespace = "http://schemas.microsoft.com/ado/2009/11/mapping/cs";
const string DiagramNamespace = "http://schemas.microsoft.com/ado/2009/11/edmx";
const string CSNameSpace = "http://schemas.microsoft.com/ado/2009/11/mapping/cs";
XElement csdl = xdoc.Descendants(XName.Get("Schema", CSDLNamespace)).First();
XElement msl = xdoc.Descendants(XName.Get("Mapping", MSLNamespace)).First();
//modifications for renaming everything, not just table names:
#region CSDL2
Console.WriteLine("Modifying CSDL...");
Console.WriteLine(" - modifying entity sets...");
foreach (
var entitySet in
csdl.Element(XName.Get("EntityContainer", CSDLNamespace)).Elements(XName.Get("EntitySet", CSDLNamespace)))
{
entitySet.Attribute("Name").Value = FormatString(entitySet.Attribute("Name").Value, replaceUnderscores);
entitySet.Attribute("EntityType").Value = FormatString(entitySet.Attribute("EntityType").Value,
replaceUnderscores);
}
Console.WriteLine(" - modifying association sets...");
foreach (
var associationSet in
csdl.Element(XName.Get("EntityContainer", CSDLNamespace))
.Elements(XName.Get("AssociationSet", CSDLNamespace)))
{
foreach (var end in associationSet.Elements(XName.Get("End", CSDLNamespace)))
{
end.Attribute("EntitySet").Value = FormatString(end.Attribute("EntitySet").Value, replaceUnderscores);
}
}
Console.WriteLine(" - modifying entity types...");
foreach (var entityType in csdl.Elements(XName.Get("EntityType", CSDLNamespace)))
{
entityType.Attribute("Name").Value = FormatString(entityType.Attribute("Name").Value, replaceUnderscores);
foreach (var key in entityType.Elements(XName.Get("Key", CSDLNamespace)))
{
foreach (var propertyRef in key.Elements(XName.Get("PropertyRef", CSDLNamespace)))
{
propertyRef.Attribute("Name").Value = FormatString(propertyRef.Attribute("Name").Value,
replaceUnderscores);
}
}
foreach (var property in entityType.Elements(XName.Get("Property", CSDLNamespace)))
{
property.Attribute("Name").Value = FormatString(property.Attribute("Name").Value, replaceUnderscores);
}
foreach (var navigationProperty in entityType.Elements(XName.Get("NavigationProperty", CSDLNamespace)))
{
navigationProperty.Attribute("Name").Value = FormatString(navigationProperty.Attribute("Name").Value,
replaceUnderscores);
}
}
Console.WriteLine(" - modifying associations...");
foreach (var association in csdl.Elements(XName.Get("Association", CSDLNamespace)))
{
foreach (var end in association.Elements(XName.Get("End", CSDLNamespace)))
{
end.Attribute("Type").Value = FormatString(end.Attribute("Type").Value, replaceUnderscores);
}
foreach (var propref in association.Descendants(XName.Get("PropertyRef", CSDLNamespace)))
{
//propertyrefs are contained in constraints
propref.Attribute("Name").Value = FormatString(propref.Attribute("Name").Value, replaceUnderscores);
}
}
#endregion
#region MSL2
Console.WriteLine("Modifying MSL...");
Console.WriteLine(" - modifying entity set mappings...");
foreach (
var entitySetMapping in
msl.Element(XName.Get("EntityContainerMapping", MSLNamespace))
.Elements(XName.Get("EntitySetMapping", MSLNamespace)))
{
entitySetMapping.Attribute("Name").Value = FormatString(entitySetMapping.Attribute("Name").Value,
replaceUnderscores);
foreach (var entityTypeMapping in entitySetMapping.Elements(XName.Get("EntityTypeMapping", MSLNamespace)))
{
entityTypeMapping.Attribute("TypeName").Value = FormatString(entityTypeMapping.Attribute("TypeName").Value,
replaceUnderscores);
foreach
(var scalarProperty in
(entityTypeMapping.Element(XName.Get("MappingFragment", MSLNamespace))).Elements(
XName.Get("ScalarProperty", MSLNamespace))
)
{
scalarProperty.Attribute("Name").Value = FormatString(scalarProperty.Attribute("Name").Value,
replaceUnderscores);
}
}
}
Console.WriteLine(" - modifying association set mappings...");
foreach (
var associationSetMapping in
msl.Element(XName.Get("EntityContainerMapping", MSLNamespace))
.Elements(XName.Get("AssociationSetMapping", MSLNamespace)))
{
foreach (var endProperty in associationSetMapping.Elements(XName.Get("EndProperty", MSLNamespace)))
{
foreach (var scalarProperty in endProperty.Elements(XName.Get("ScalarProperty", MSLNamespace)))
{
scalarProperty.Attribute("Name").Value = FormatString(scalarProperty.Attribute("Name").Value,
replaceUnderscores);
}
}
}
#endregion
// Optionally replace the entity name in case the default of "Entity" is not
// sufficient for your needs.
if (doEntityNameReplace)
{
Console.WriteLine("Modifying entity name refs...");
// CSDL
xdoc.Descendants(XName.Get("EntityContainer", CSDLNamespace)).First().Attribute("Name").Value = entityName;
// Diagram
var diagramDescendants = xdoc.Descendants(XName.Get("Diagram", DiagramNamespace)).FirstOrDefault();
if (diagramDescendants != null)
{
diagramDescendants.Attribute("Name").Value = entityName;
}
// Diagram
xdoc.Descendants(XName.Get("EntityContainerMapping", CSNameSpace)).First().Attribute("CdmEntityContainer").Value
= entityName;
}
Console.WriteLine("Writing result...");
using (XmlTextWriter writer = new XmlTextWriter(filePath, Encoding.Default))
{
writer.Formatting = Formatting.Indented;
xdoc.WriteTo(writer);
}
}
private static void TransformEdmxDiagram(string filePath, bool replaceUnderscores)
{
// Processing:
Console.WriteLine("Creating backup: " + Path.ChangeExtension(filePath, ".bak"));
File.Copy(filePath, Path.ChangeExtension(filePath, ".bak"), true);
Console.WriteLine("Reading target document...");
XDocument xdoc = XDocument.Load(filePath);
const string DiagramNamespace = "http://schemas.microsoft.com/ado/2009/11/edmx";
XElement designerDiagram = xdoc.Descendants(XName.Get("Diagram", DiagramNamespace)).FirstOrDefault();
#region Designer
Console.WriteLine("Modifying designer content...");
if (designerDiagram != null)
{
foreach (var item in designerDiagram.Elements(XName.Get("EntityTypeShape", DiagramNamespace)))
{
item.Attribute("EntityType").Value = FormatString(item.Attribute("EntityType").Value, replaceUnderscores);
}
}
#endregion
Console.WriteLine("Writing result...");
using (XmlTextWriter writer = new XmlTextWriter(filePath, Encoding.Default))
{
writer.Formatting = Formatting.Indented;
xdoc.WriteTo(writer);
}
}
/// <summary>
/// Formats the string to pascal case, additionally checking for a period
/// in the string (in which case it skips past the period, which indicates
/// the use of namespace in a string.
/// </summary>
/// <param name="str"></param>
/// <param name="replaceUnderscores"></param>
/// <returns></returns>
private static string FormatString(string str, bool replaceUnderscores = true)
{
char[] chars = str.ToCharArray();
var sb = new StringBuilder();
bool previousCharWasUpper = false;
bool lastOperationWasToLower = false;
int startPos = 0;
if (str.Contains("."))
{
if (str.IndexOf(".") < (str.Length - 1))
{
startPos = str.IndexOf(".") + 1;
}
sb.Append(str.Substring(0, startPos));
}
for (int i = startPos; i < chars.Length; i++)
{
char character = chars[i];
if (Char.IsLetter(character))
{
if (Char.IsLower(character))
{
bool toUpper = false;
if (i > 0)
{
// Look at the previous char to see if not a letter
if (!Char.IsLetter(chars[i - 1]))
{
toUpper = true;
}
}
if (i == 0 || toUpper)
{
character = Char.ToUpper(character);
lastOperationWasToLower = false;
}
}
else // IsUpper = true
{
if (previousCharWasUpper || lastOperationWasToLower)
{
character = Char.ToLower(character);
lastOperationWasToLower = true;
}
}
previousCharWasUpper = Char.IsUpper(character);
sb.Append(character);
}
else
{
if (Char.IsDigit(character))
{
sb.Append(character);
previousCharWasUpper = false;
lastOperationWasToLower = false;
}
else if (!replaceUnderscores)
{
if (character == '_')
{
sb.Append(character);
}
}
}
}
return sb.ToString();
}
private static void StopWithMessage(string str)
{
Console.WriteLine(str);
Console.ReadLine();
throw new InvalidOperationException("Cannot continue.");
}
}
}
【讨论】:
some_class > SomeClass
对我没有用,而是我得到了:
some_class > 某类
为了修复它,我搬家了
previousCharWasUpper = false;
几行。它接近旧代码块的末尾。
else
{
if (Char.IsDigit(character))
{
sb.Append(character);
previousCharWasUpper = false;
lastOperationWasToLower = false;
}
else if(!replaceUnderscores)
{
if(character == '_')
{
sb.Append(character);
}
}
}
改为:
else
{
previousCharWasUpper = false;
if (Char.IsDigit(character))
{
sb.Append(character);
lastOperationWasToLower = false;
}
else if(!replaceUnderscores)
{
if(character == '_')
{
sb.Append(character);
}
}
}
【讨论】: