元数据异常:无法加载指定的元数据资源。

730
突然之间,我在实例化生成的ObjectContext类时一直遇到MetadataException。App.Config中的连接字符串看起来正确-自上次工作以来没有变化-并且我已经尝试从底层数据库重新生成新模型(edmx文件),但未发现改变。
有人有任何想法吗?
更多细节:我没有更改任何属性,也没有更改任何输出程序集的名称,也没有尝试将EDMX嵌入程序集中。我仅仅离开工作10个小时,然后回来时它就不再工作了。
我已经尝试重新创建EDMX。我尝试重新创建项目。我甚至尝试从头开始重新创建数据库。但是没有任何运气。

16
如果一个 Stack Overflow 上的问题关于一个拥有超过20万次浏览量的特定产品,并且该产品的功能与用户期望的不同,我希望微软能够解决这个问题。如果您有时间,可以通过以下链接向他们提供建议:http://visualstudio.uservoice.com/forums/121579-visual-studio。 - Tony L.
1
我的问题已经解决了,通过替换从数据库层项目复制的连接字符串。 - Hardik
我也遇到过这个问题,我只是清理并重新构建解决方案,这样就可以正常工作了。 - Yogesh Sharma
1
马基·德·萨德还活着,而且在微软工作。这真的是实体框架地狱。 - pwrgreg007
请查看被采纳答案下的评论。最终问题只是一个错误的连接字符串。 - Gert Arnold
我在使用VS2022时,出现了相同的错误(以及很多卡顿),看起来是突然出现的。我重启了电脑。根据@Craig Stuntz(下面的一个答案)和其他we.config设置,比较和检查了EDMX连接字符串/设置 - 仍然没有解决。我在安全模式下启动了VS2022:devenv /SafeMode,解决方案无法运行(未加载所有项目 - 在“输出”中提供原因) - 但之后正常打开VS2022(没有进行任何更改),问题就消失了。_一切都恢复正常了。_值得尝试在安全模式下打开。 - Jonno
47个回答

883

这意味着应用程序无法加载EDMX。导致此问题的原因有多种。

  • 您可能已将模型的MetadataArtifactProcessing属性更改为复制到输出目录。
  • 连接字符串可能是错误的。我知道您说您没有更改它,但如果您已更改其他内容(比如程序集的名称),它仍然可能是错误的。
  • 您可能正在使用后编译任务将EDMX嵌入程序集中,但由于某种原因它不再起作用。

简而言之,在您的问题中没有足够的详细信息,以便给出准确的答案,但希望这些想法能让您走上正确的轨道。

更新:我写了一篇博客文章,提供了更完整的故障排除步骤。


72
连接字符串,尽管我上次努力使用内容比较工具进行比较,但仍然是错误的。 - J. Steen
17
对我来说,连接字符串也是个问题。当你有需要在自己的App.config中使用连接字符串的集成测试时,如果你更新了edmx,事情可能会失去同步。 - Ray
11
好的,我通过设置"嵌入"然后进行编译,再将其重置为另一个选项来解决了问题。这解决了我的问题。 - Shimmy Weitzhandler
9
非常棒的指南。对我来说,我复制了另一个连接字符串,它使用了res:///Database.MyModel2...,而实际上我想要的是res:///MyModel1...(Database是我的集成测试项目中的一个文件夹)。 - emragins
2
根据Shimmy的说法,我将MetadataArtifactProcessing从embed(正确的)更改为copy(错误的),重新构建项目,手动清理bin文件夹,将其改回embed,重新构建,然后它就可以工作了。另外提一下,通过在edmx设计师的空白区域右键单击并从上下文菜单中选择“属性”,即可访问MetadataArtifactProcessing。 - Nathan
显示剩余12条评论

390

通过轻微修改,我解决了这个问题。

我的解决方案包含了3个项目引用:

connectionString="metadata=res://*/Model.Project.csdl|res://*/Model.Project.ssdl|res://*/Model.Project.msl;

我将其更改为:

connectionString="metadata=res://*/;

14
这解决了我的问题,但这句话到底是什么意思? - Lance Fisher
19
@Lance: 我在这篇博客文章中详细解释了这个问题。 - Craig Stuntz
4
不,它在很多情况下都不能工作,在其他情况下会很慢。阅读我的博客文章以了解原因。 - Craig Stuntz
7
我将我的.edmx文件移动到了Model文件夹中,但忘记更新连接字符串。非常有用的提示。谢谢。否则我会花费数小时才能找出问题。 - muruge
2
我已经完成了,解决了一个问题,但是又出现了另一个问题:“所有加载到ItemCollection中的工件必须具有相同的版本。遇到了多个版本。”-最终是针对Npgsql。只有完整路径才能解决这两个问题。 - Michał Powaga
显示剩余6条评论

117

使用不同程序集中定义的Edmx并从其他程序集中使用时,可能会出现此异常。原因是Res://*/是一个指向当前程序集中资源的URI。如果Edm在与使用它的代码不同的程序集中定义,则无法找到资源,因此res://*/将不起作用。

您需要提供程序集的完整名称(包括公钥令牌),而不是指定“*”。例如:

res://YourDataAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abcdefabcedf/YourEdmxFileName.csdl|res://...

使用EntityConnectionStringBuilder构建连接字符串是更好的方法:

public static string GetSqlCeConnectionString(string fileName)
{
    var csBuilder = new EntityConnectionStringBuilder();

    csBuilder.Provider = "System.Data.SqlServerCe.3.5";
    csBuilder.ProviderConnectionString = string.Format("Data Source={0};", fileName);

    csBuilder.Metadata = string.Format("res://{0}/YourEdmxFileName.csdl|res://{0}/YourEdmxFileName.ssdl|res://{0}/YourEdmxFileName.msl", 
        typeof(YourObjectContextType).Assembly.FullName);

    return csBuilder.ToString();
}

public static string GetSqlConnectionString(string serverName, string databaseName)
{
    SqlConnectionStringBuilder providerCs = new SqlConnectionStringBuilder();

    providerCs.DataSource = serverName;
    providerCs.InitialCatalog = databaseName;
    providerCs.IntegratedSecurity = true;

    var csBuilder = new EntityConnectionStringBuilder();

    csBuilder.Provider = "System.Data.SqlClient";
    csBuilder.ProviderConnectionString = providerCs.ToString();

    csBuilder.Metadata = string.Format("res://{0}/YourEdmxFileName.csdl|res://{0}/YourEdmxFileName.ssdl|res://{0}/YourEdmxFileName.msl",
        typeof(YourObjectContextType).Assembly.FullName);

    return csBuilder.ToString();
}
如果你仍然遇到异常,请在反编译器中打开程序集并检查您的 .csdl、.ssdl 和 .msl 文件的文件名。当资源名称与元数据值中指定的名称不同时,它将无法工作。

8
请注意,"YourEdmxFileName" 必须是合格的名称,例如 "YourNamespace.YourEdmxFileName",如果您在程序集中使用命名空间。然而,您必须删除相等于您程序集名称的命名空间部分。 - Marcel
5
“当您使用通配符(*)时,实体框架必须浏览所有程序集以找到具有正确名称的资源。”根据MSDN的说法,第二段是错误的。 - Craig Stuntz
我非常确定命名空间并不相关,但是嵌入式文件路径是有关的。因此,即使您检查与edmx文件相关联的*.Designer.cs文件并注意到自动生成的类命名空间是MyCompany...任何内容,也不应使用它。相反,路径是组件名称、解决方案文件夹名称/文件名称。例如:"metadata=res:///EntityModels.<filename>.csdl|" + "res:///EntityModels.<filename>.ssdl|" + "res://*/EntityModels.<filename>.msl;" - Daniel
1
@Daniel,大部分是正确的,但请注意命名空间和嵌入文件路径有时是相同的。您必须使用Reflector(或其免费替代品)进行查看以确保。 - Craig Stuntz
看起来它只使用程序集名称工作,而没有版本、公钥令牌等。例如:res://MyAssembly/folder.<filename>.csdl... - Ivan Ferrer Villa
确认 @Craig Stuntz 是正确的。通配符即使在EDMX被引用到另一个程序集中也可以工作。 - Sentinel

72

我遇到了类似的错误。我重新创建了项目(长话短说),并将所有内容从旧项目中移动过来。我没有意识到我的模型以前在一个名为“Model”的目录中,现在在一个名为“Models”的目录中。一旦我在Web.Config中更改了连接:

<add name="RecipeManagerEntities" connectionString="metadata=res://*/Model.Recipe.csdl 

变为这样:

<add name="RecipeManagerEntities" connectionString="metadata=res://*/Models.Recipe.csdl

所有的东西都起作用了(将Model更改为Models)。请注意,我不得不在该字符串中更改三个位置。


2
我将我的Entity Framework模型从Model移动到DAL。但是,一周后当我在测试项目中编写一个测试来测试Linq predicatebuilder时,我遇到了这个错误。我通过将测试项目的App.config与主项目的web.config相匹配(正如你在三个地方所说的那样)来纠正了这个问题。因此,你简单的回答让我找到了正确的方向。 - Patrik Lindström
8
两者之间有区别吗?! - user2609980
2
@ErwinRooijakkers 模型 vs ModelS - Marc
在阅读了Craig的博客后,我发现自己也犯了同样的错误,但是因为学到了重要的一课,即在“实体”类库中所做的更改不会自动反映在引用它的项目的配置文件中,所以还是值得的。/叹气,很高兴我不是一个人。 - ruffin

30

不用Reflector的快速检查模型名称的方法是查看目录

 

...obj/{配置输出}/edmxResourcesToEmbed

并检查是否存在.csdl、.msl和.ssdl资源文件。如果它们位于子目录中,则必须在模型名称前加上子目录的名称。

例如,我的三个资源文件位于子目录Data中,因此我的连接字符串必须为

 

metadata=res://*/Data.MyModel.csdl|res://*/Data.MyModel.ssdl|res://*/Data.MyModel.msl;

(而不是 metadata=res://*/MyModel.csdl|res://*/MyModel.ssdl|res://*/MyModel.msl;)


这正是我的问题。我在这上面浪费了好几个小时。非常感谢您提供的简单解释。 - Fernando Carvalhosa
很棒的答案,实际上解释了如何找到你的字符串。并且展示了子文件夹使用'.'作为分隔符而不是''或'/'。 - cjb110

18

当我在构建新的.edmx设计器之前没有清理解决方案时,就会出现这种情况。因此,在构建新的.edmx设计器之前不要忘记清理解决方案。这帮助我避免了更多相关问题。如果您是Visual Studio的新手,请参考以下导航说明。

点击->生成->清理解决方案

然后点击->生成->重新生成解决方案

希望这可以帮到您。谢谢大家。


17

我也遇到了这个问题,原因是我的web.config中的connectionstring与EDMX所在程序集的app.config中的略有不同。不知道为什么会改变,但这里有两个不同版本的连接字符串。

App.config:

<add name="SCMSEntities" connectionString="metadata=res://*/Model.SMCSModel.csdl|res://*/Model.SMCSModel.ssdl|res://*/Model.SMCSModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=SANDIEGO\sql2008;initial catalog=SCMS;integrated security=True;multipleactiveresultsets=True;application name=EntityFramework&quot;" providerName="System.Data.EntityClient" />

Web.config:

<add name="SCMSEntities" connectionString="metadata=res://*/Model.SCMSModel.csdl|res://*/Model.SCMSModel.ssdl|res://*/Model.SCMSModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=SANDIEGO\sql2008;initial catalog=SCMS;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />

解决方法很简单,只需要将app.config的字符串复制到web.config中(注意末尾的微小差别-它需要 application name=EntityFramework而不是App=EntityFramework),问题就解决了。:)


1
谢谢,这确实是我的问题。我有一个使用EF访问DB的项目和另一个WCF项目。 在更改第一个项目的名称后,连接字符串已在第一个项目的App.config中更改。所以我还必须在web.config中更改WCF项目的connectionString :) - Volkan
从MSDN文档关于https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings:".NET Framework数据提供程序SQL Server(SqlClient)支持许多来自旧API的关键字,但通常更灵活,并接受许多常见连接字符串关键字的同义词。"Entity Framework连接字符串不具备这种灵活性,因此您必须仅使用它所期望的关键字。 - Suncat2000

16

这是我的解决方案 - 我将我的edmx重命名为.old作为备份,并尝试了一些东西,之后当我将其重命名回来时,构建操作设置为none,因此出现了这个错误,将其设置回EntityDeploy解决了我的问题 :) - eth0
我已将我的EDMX文件移动到另一个文件夹,并且必须更改构建操作,以便更新嵌入资源名称。谢谢! - David
这对我来说是解决方案;在升级到.NET Standard的过程中,我丢失了那个设置。谢谢你拯救了我的理智! - NetherGranite
这也是我的解决方法。 必须设置与旧项目相同的edmx文件的属性设置:EntityDeploy / Do not copy / EntityModelCodeGenerator。 - Flou

10
如果您正在使用来自不同项目的edmx,请在连接字符串中更改...
metadata=res://*/Data.DataModel.csdl

...至...

metadata=res://*/DataModel.csdl

这是正确的。如果你想将它移动到你的新项目子文件夹中,你需要在它前面添加 folder.subfolder - qakmak
谢谢,这个解决方案对我很有用。我将我的.edmx文件从一个项目的目录移动到另一个项目的根目录,并需要从整个解决方案中删除所有连接字符串中的目录名称。 - Chris

9
有时候我在我的项目中会遇到这个错误。我通过以下步骤解决了它:
1 - 右键单击EDMX文件 2 - 选择“运行自定义工具”选项 3 - 重新构建项目

1
这对我有用,但之后我还必须重新构建。 - rdans

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