Entity Framework Core 数据库第一种方法复数表名
Posted
技术标签:
【中文标题】Entity Framework Core 数据库第一种方法复数表名【英文标题】:EntityFramework Core database first approach pluralizing table names 【发布时间】:2017-01-09 22:26:48 【问题描述】:我们有复数表名的现有数据库。例如Documents
。我正在尝试基于本文here 使用新的EF Core
和Asp.Net Core
和数据库优先方法
我运行以下命令从现有数据库创建模型
脚手架-DbContext “服务器=(本地);数据库=MyDatabase;Trusted_Connection=True;”Microsoft.EntityFrameworkCore.SqlServer -OutputDir 模型
但是,当我运行脚手架命令时,它会创建具有复数名称的模型。例如Documents
表转换为模型名称Documents
。
如何更改它以便它可以对模型使用单数命名约定? (我无法更改数据库中的表名)
请注意,我读过一些关于 SO 与同一问题相关的帖子,但他们主要关注的是代码优先方法。我正在使用数据库优先方法。
【问题讨论】:
重命名模型时会发生什么? @PetreTurcu 当您再次搭建脚手架时,重命名的模型仍然存在,但还会使用您的“旧”数据库表名生成一个新类。 这成功了:bricelam.net/2018/03/02/efcore-pluralization.html 【参考方案1】:在 Entity Framework Core v2 中,他们引入了一个复数钩子。您可以自己复数或单数化您的对象。
您可以通过将其添加到您的项目中来做到这一点:
public class MyDesignTimeServices : IDesignTimeServices
public void ConfigureDesignTimeServices(IServiceCollection services)
services.AddSingleton<IPluralizer, MyPluralizer>();
public class MyPluralizer : IPluralizer
public string Pluralize(string name)
return Inflector.Inflector.Pluralize(name) ?? name;
public string Singularize(string name)
return Inflector.Inflector.Singularize(name) ?? name;
更多信息:What's new in EF core 2
你可以使用这个类作为你的 Inflector:
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace DMP.Generator
public static class Inflector
#region Default Rules
static Inflector()
AddPlural("$", "s");
AddPlural("s$", "s");
AddPlural("(ax|test)is$", "$1es");
AddPlural("(octop|vir|alumn|fung)us$", "$1i");
AddPlural("(alias|status)$", "$1es");
AddPlural("(bu)s$", "$1ses");
AddPlural("(buffal|tomat|volcan)o$", "$1oes");
AddPlural("([ti])um$", "$1a");
AddPlural("sis$", "ses");
AddPlural("(?:([^f])fe|([lr])f)$", "$1$2ves");
AddPlural("(hive)$", "$1s");
AddPlural("([^aeiouy]|qu)y$", "$1ies");
AddPlural("(x|ch|ss|sh)$", "$1es");
AddPlural("(matr|vert|ind)ix|ex$", "$1ices");
AddPlural("([m|l])ouse$", "$1ice");
AddPlural("^(ox)$", "$1en");
AddPlural("(quiz)$", "$1zes");
AddSingular("s$", "");
AddSingular("(n)ews$", "$1ews");
AddSingular("([ti])a$", "$1um");
AddSingular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
AddSingular("(^analy)ses$", "$1sis");
AddSingular("([^f])ves$", "$1fe");
AddSingular("(hive)s$", "$1");
AddSingular("(tive)s$", "$1");
AddSingular("([lr])ves$", "$1f");
AddSingular("([^aeiouy]|qu)ies$", "$1y");
AddSingular("(s)eries$", "$1eries");
AddSingular("(m)ovies$", "$1ovie");
AddSingular("(x|ch|ss|sh)es$", "$1");
AddSingular("([m|l])ice$", "$1ouse");
AddSingular("(bus)es$", "$1");
AddSingular("(o)es$", "$1");
AddSingular("(shoe)s$", "$1");
AddSingular("(cris|ax|test)es$", "$1is");
AddSingular("(octop|vir|alumn|fung)i$", "$1us");
AddSingular("(alias|status)$", "$1");
AddSingular("(alias|status)es$", "$1");
AddSingular("^(ox)en", "$1");
AddSingular("(vert|ind)ices$", "$1ex");
AddSingular("(matr)ices$", "$1ix");
AddSingular("(quiz)zes$", "$1");
AddIrregular("person", "people");
AddIrregular("man", "men");
AddIrregular("child", "children");
AddIrregular("sex", "sexes");
AddIrregular("move", "moves");
AddIrregular("goose", "geese");
AddIrregular("alumna", "alumnae");
AddUncountable("equipment");
AddUncountable("information");
AddUncountable("rice");
AddUncountable("money");
AddUncountable("species");
AddUncountable("series");
AddUncountable("fish");
AddUncountable("sheep");
AddUncountable("deer");
AddUncountable("aircraft");
#endregion
private class Rule
private readonly Regex _regex;
private readonly string _replacement;
public Rule(string pattern, string replacement)
_regex = new Regex(pattern, RegexOptions.IgnoreCase);
_replacement = replacement;
public string Apply(string word)
if (!_regex.IsMatch(word))
return null;
return _regex.Replace(word, _replacement);
private static void AddIrregular(string singular, string plural)
AddPlural("(" + singular[0] + ")" + singular.Substring(1) + "$", "$1" + plural.Substring(1));
AddSingular("(" + plural[0] + ")" + plural.Substring(1) + "$", "$1" + singular.Substring(1));
private static void AddUncountable(string word)
_uncountables.Add(word.ToLower());
private static void AddPlural(string rule, string replacement)
_plurals.Add(new Rule(rule, replacement));
private static void AddSingular(string rule, string replacement)
_singulars.Add(new Rule(rule, replacement));
private static readonly List<Rule> _plurals = new List<Rule>();
private static readonly List<Rule> _singulars = new List<Rule>();
private static readonly List<string> _uncountables = new List<string>();
public static string Pluralize(this string word)
return ApplyRules(_plurals, word);
public static string Singularize(this string word)
return ApplyRules(_singulars, word);
#if NET45 || NETFX_CORE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static string ApplyRules(List<Rule> rules, string word)
string result = word;
if (!_uncountables.Contains(word.ToLower()))
for (int i = rules.Count - 1; i >= 0; i--)
if ((result = rules[i].Apply(word)) != null)
break;
return result;
public static string Titleize(this string word)
return Regex.Replace(Humanize(Underscore(word)), @"\b([a-z])",
delegate (Match match)
return match.Captures[0].Value.ToUpper();
);
public static string Humanize(this string lowercaseAndUnderscoredWord)
return Capitalize(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " "));
public static string Pascalize(this string lowercaseAndUnderscoredWord)
return Regex.Replace(lowercaseAndUnderscoredWord, "(?:^|_)(.)",
delegate (Match match)
return match.Groups[1].Value.ToUpper();
);
public static string Camelize(this string lowercaseAndUnderscoredWord)
return Uncapitalize(Pascalize(lowercaseAndUnderscoredWord));
public static string Underscore(this string pascalCasedWord)
return Regex.Replace(
Regex.Replace(
Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-z\d])([A-Z])",
"$1_$2"), @"[-\s]", "_").ToLower();
public static string Capitalize(this string word)
return word.Substring(0, 1).ToUpper() + word.Substring(1).ToLower();
public static string Uncapitalize(this string word)
return word.Substring(0, 1).ToLower() + word.Substring(1);
public static string Ordinalize(this string numberString)
return Ordanize(int.Parse(numberString), numberString);
public static string Ordinalize(this int number)
return Ordanize(number, number.ToString());
#if NET45 || NETFX_CORE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static string Ordanize(int number, string numberString)
int nMod100 = number % 100;
if (nMod100 >= 11 && nMod100 <= 13)
return numberString + "th";
switch (number % 10)
case 1:
return numberString + "st";
case 2:
return numberString + "nd";
case 3:
return numberString + "rd";
default:
return numberString + "th";
public static string Dasherize(this string underscoredWord)
return underscoredWord.Replace('_', '-');
变形器类可以在here找到
对于脚手架,我使用dotnet ef
命令。
我的项目中包含以下 NuGet 包:
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
</ItemGroup>
【讨论】:
此钩子仅用于实体类型名称的单数化和 DbSet 名称的复数化。正如github.com/aspnet/EntityFramework.Docs/blob/master/… 所解释的,它实际上并没有对表名做任何事情。但是感谢上面的代码!它让我对 EF Core 2.0 有了更多的了解。 我这里有一个奇怪的错误:“无法创建堆栈的新保护页” 我一直在努力让它工作,但似乎在包管理器控制台中运行 scaffold-dbcontext 不会调用我的 MyDesignTimeServices 类。一旦我复制了 .csproj 的 ItemGroup 内容并通过 cmd 行运行它,一切正常。谢谢!【参考方案2】:您可以使用Bricelam.EntityFrameworkCore.Pluralizer。要使用该软件包,只需安装它。从现有数据库对模型进行逆向工程时,将使用复数形式。
// Tools > NuGet Package manager > Package Manager Console:
PM> Install-Package Bricelam.EntityFrameworkCore.Pluralizer
然后运行您的Scaffold-DbContext ...
命令。就是这样!
Scaffold-DbContext "ConnectionStr..." Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
【讨论】:
效果很好!不过,我仍然没有解决的是,如果它们在表名中存在多个大写字母(例如 GPSSettings),它会生成除第一个小写字母以外的所有字母(例如 Gpssettings.cs) 我收到错误“无法加载文件或程序集'Bricelam.EntityFrameworkCore.Pluralizer,Culture=neutral,PublicKeyToken=null'。系统找不到指定的文件。”使用时...为什么这不是 Entity Framework Core 内置的?它只是让我认为 Core 还没有为真正的应用程序做好准备。这似乎是 .Net Framework 4.7 的一个倒退 我喜欢它没有嵌入到 EF Core 并且是可扩展的,但我同意没有将嵌入到 EF6 中的相同算法作为 Microsoft.EntityFrameworkCore.Pluralizer NuGet 包提供是荒谬的。目前正在将 .NET FW EF6 项目迁移到 .NET Core 2.2 和 EF Core,并且模型的生成方式不同,导致使用它们的客户端代码出现大量错误。 MS真的让我们在这个问题上闲逛。 :-( 如果我遗漏了什么,请有人提供链接,让我吃掉我的话。【参考方案3】:逆向工程目前不将实体类型的表名称单数化(这是一个词吗?),或将它们复数化以用于导航属性。这是问题#3060。在此之前,您必须在生成代码后手动将它们更改为您想要的。
【讨论】:
我想可以根据现有的词尾词典创建一个脚本来执行此操作,该脚本可以在模型生成之后运行,直到由框架处理 哦!有趣的是你没有提到你自己的包裹!我在下面发布我的答案后看到了你的名字;D【参考方案4】:EF Core 工具目前不包含用于以您描述的方式自定义对现有数据库进行逆向工程输出的选项。目前,您唯一现实的选择是修改生成的代码。
【讨论】:
多年来,我们一直在定义复数表,而 EF 会生成单数实体,这在开发过程中是有意义的。为什么会有这种剧烈的变化?现在我必须写var doc = new Documents()
,即使它是单一实体
EF Core 的约定是,如果存在表,则为表使用 DbSet 名称,如果不存在,则使用实体的类名。 Core 中没有多元化服务。您可以说 reveng 工具是反向一致的。如果您想建议更改当前行为(或 CLI 工具的配置选项),您应该考虑在 EF Core Github repo 创建问题
他们现在确实支持这个,你唯一需要做的就是实现一个钩子和一个变形器:***.com/a/47410837/869033以上是关于Entity Framework Core 数据库第一种方法复数表名的主要内容,如果未能解决你的问题,请参考以下文章
使用 Entity Framework Core 更新相关数据
Entity Framework Core 数据保存原理详解