Entity Framework(4.3)在实体名称以“s”结尾时寻找单数名称而不是复数名称

13
这是我的情况: 我已经在一个ASP.NET MVC 3应用程序上工作了一段时间。它有一个数据库(由db项目构建而成;我正在使用db-first方法),我有一个edmx模型和一组POCOs。我的实体在数据库中具有复数名称,而POCOs具有单数名称。一切都很顺利,没有问题。 直到我添加了一个新表(称为TransactionStatuses),现在所有旧实体仍然可以正常工作,但新的实体却不能。当我尝试与相关实体一起进行急切加载时:
var transactions = (from t in db.Transactions.Include(s => s.TransactionStatus) //TransactionStatus - navigation property in Transactions to TransactionStatuses
                    where t.CustomerID == CustomerID
                    select t).ToList();

我遇到了一个问题:

无效的对象名称 'dbo.TransactionStatus'。

我甚至进行了一个更简单的测试:

List<TransactionStatus> statuses = db.TransactionStatuses.ToList();

= 相同的结果。

我已经从数据库更新(甚至重新创建)了edmx,并且反复查看它,试图弄清楚dbo.TransactionStatus*es*的映射有什么不同,导致整个过程出现问题。

如果有人能指导我修复它,那就太棒了。

P.S. 关闭复数形式不是一个选项,谢谢。

更新: 我找到了解决方法 - 在下面给出了答案。


你能创建一个TransactionStatuses的新实例吗?如果可以,请查看EF生成的出站SQL,并确保它指定了正确的表。 - BentOnCoding
那就是问题所在,SQL有单数名称...我正在努力弄清楚为什么以及如何修复它。 - Dmitry Selitskiy
4个回答

32
这可能是因为尽管意图是使用“数据库优先”工作流,但实际上应用程序正在使用“代码优先”进行映射。让我解释一下,因为这可能会令人困惑。 :-)
在使用 EF Designer 和 Visual Studio 中的 DbContext 模板以进行“数据库优先”操作时,会发生三件非常重要的事情。首先,新的 Entity Data Model 向导会向您的应用添加一个连接字符串,其中包含“数据库优先”模型(即 EDMX)的详细信息,以便在运行应用程序时可以找到此模型。连接字符串的格式如下:
<connectionStrings>
    <add name="MyEntities"
    connectionString="metadata=res://*/MyModel.csdl|res://*/MyModel.ssdl|res://*/MyModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\sqlexpress;initial catalog=MyEntities;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;"
    providerName="System.Data.EntityClient" />
</connectionStrings>

其次,生成的上下文类会调用基类DbContext构造函数,并指定该连接字符串的名称:

public MyEntities()
: base("name=MyEntities")
{
}

这告诉DbContext在配置中找到并使用"MyEntities"连接字符串。使用"name="意味着如果DbContext没有找到连接字符串,它会抛出异常--它不会只是按照惯例创建一个连接。

如果您想使用Database First,则必须使用生成的连接字符串。具体而言,它必须包含模型数据(来自EDMX的csdl、msl、ssdl),并且您必须确保DbContext能够找到它。在更改对基础构造函数的调用时要非常小心。

第三件事是在生成的上下文中覆盖OnModelCreating并使其抛出异常:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    throw new UnintentionalCodeFirstException();
}

这样做是因为 OnModelCreating 仅在使用 Code First 时调用。因为 OnModelCreating 主要是用于创建模型,而当使用 Database First 时,模型已经存在--运行时不需要创建任何内容。所以如果调用了 OnModelCreating,则可能是因为您无意中开始使用 Code First,通常是由于更改连接字符串或调用基类构造函数引起的。

现在,您可能想使用 Code First 映射到现有数据库。这是一种很好的模式,并得到完全支持(请参见http://blogs.msdn.com/b/adonet/archive/2011/03/07/when-is-code-first-not-code-first.aspx),但是您需要确保设置正确的映射才能使其工作。如果映射没有正确设置,则会出现本问题中的异常。


好的,我明白了!这很棒,我会回去检查这些步骤,但我认为我错过了在DbContext构造函数中正确连接字符串的参考!这意味着它是代码优先。我会更新我的发现。 - Dmitry Selitskiy
1
是的,这绝对就是问题所在。我在我的DAL项目中有正确的连接字符串,但在我的MVC应用程序项目中却没有(我有一个普通的连接字符串)。我尝试根据您提出的要点进行更改,但我开始遇到UnintentionalCodeFirstException错误,而我无法弄清楚是什么在调用它...总之,非常感谢您澄清这一点。这是我第一次看到Code First和DB First之间设置的明确区别。 - Dmitry Selitskiy

7
明白了! 可怕,可怕的经历...
简而言之:EF无法正确地为以“s”结尾的实体名称(例如“status”,“campus”等)进行复数形式下面是我发现问题并证明的过程。
  • 我已经多次创建和重新创建我的原始设置,并得到了相同的结果。
  • 我还尝试将表格重命名为类似TransStatus之类的名称,但没有成功。
  • 当我在研究这个问题时,我看到了Scott Hanselman的复数形式文章,他为sheepgoose等单词添加了复数形式规则。这让我想到了“如果问题出在实体的实际名称上呢?
  • 我对特定单词status进行了一些搜索,并在Connect上找到了有关该问题的报告。我赶紧尝试重命名实体...
  • TransactionStatuses重命名为TransactionStates(甚至保持列为StatusID解决了问题!现在我可以得到List<TransactionState> statuses = db.TransactionStates.ToList();
  • 我原以为问题出在特定单词status的名称上...但是在向朋友抱怨后,他建议我也许问题出在以“s”结尾的单词上...所以我决定去检查一下。
  • 我设置了另一个名为Campuses的表格和匹配的POCOCampus(并更新了edmx)。进行简单测试List<Campus> test = db.Campuses.ToList();,然后得到了预期的结果:

Invalid object name 'dbo.Campus'。

所以,强大的EF无法处理以“s”结尾的单词的复数形式。希望下一个遇到这个问题的可怜人能找到这个问题并节省3-4小时的痛苦和挫败感。


4
EF不使用其复数形式服务来命名数据库,它仅将其用于命名模型中的实体集,该实体集成为上下文类中的对象集(或dbset)。如果您使用数据库优先,EF将知道您表的名称,因为它直接从数据库获取信息。在EDMX中,默认情况下它会尽力创建一个从实体复数化的实体集名称。但是,然后它的映射文件会说,该实体集映射到在创建模型时看到的任何表名称。 - Julie Lerman
1
复数服务确实有限。我同意这一点。但我不明白这与数据库表名有什么关系。 - Julie Lerman
2
Dmitry,这是默认行为的短Camtasia,通常应该发生。至少你会明白为什么我对你的问题感到困惑。观看后,我们可以查看你的db/model/profiler,看看为什么你获得了不同的体验。(我没有触碰任何默认设置) http://dl.dropbox.com/u/13420868/dmitrys.mp4(没有声音) - Julie Lerman
1
@JulieLerman看看ajcvicker的回答,他让我朝正确的方向前进,现在可能消除了你的困惑。然而,从技术上讲,答案仍然是正确的:复数化服务无法将"Status"和"Campus"变为复数形式 :) - Dmitry Selitskiy
1
@DmitrySelitskiy...我尊重地不同意您提出的答案。确实,复数服务无法将状态和校园转为复数形式,但[除非EF代码优先方面发生了某些我不知道的变化],复数服务不用于数据库表命名......只用于实体集命名。而且,您的问题是关于数据库名称,而不是实体集名称。看起来像是复数形式导致了这个问题,但它并没有涉及其中。 :) Code First 遵循其约定,期望表名称与实体名称匹配。 - Julie Lerman
显示剩余7条评论

0

唉,与类和模型照片(Photo)和视频(Video)相关的问题是相同类型的;它正在寻找表格Photoes和Videoes。我不想仅仅更改表格名称,但看起来没有太多选择。


0

你提到了EDMX,但我无法确定你是在使用EDMX进行数据库优先还是代码优先,并且只是使用EDMX来查看发生了什么。

如果你正在使用Code First,则可以使用配置来指定表名。数据注释是[Table("TransactionStatuses")],流畅的是modelBuilder.Entity().ToTable("TransactionStatuses")。 (我从记忆中输入注释和流畅的代码,所以请确认引用。;))

如果您正在使用数据库优先,则SSDL绝对应该知道数据库表的名称,因此我猜测您正在使用代码优先,而edmx仅用于探索。(???)

希望这有所帮助


抱歉表述不清,我正在进行数据库优先的开发。在edmx中命名看起来都没问题,但是在运行时出现了一些问题... - Dmitry Selitskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接