Entity Framework6: 无法加载指定的元数据资源
Entity Framework 6: Unable to load the specified metadata resource
首先,这与 SO 上的另一个问题有关:
我已经通过以下 SO 文章和博客阅读并调试了我的问题:
MetadataException: Unable to load the specified metadata resource
和
http://blogs.teamb.com/craigstuntz/2010/08/13/38628/
但是...我还有其他问题 'fix'
我有一个WebAPI(2.1),我的WebAPI中的连接字符串是这样的:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://*/ProjectModel.csdl|
res://*/ProjectModel.ssdl|
res://*/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
当我在我的 WebAPI(伪代码)中对 DbSet
调用 ToList()
时:
DbContext _DbContext = new ProjectEntities();
DbSet<TEntity> _dbSet = _DbContext.Set<TEntity>();
_dbSet.ToList();
效果很好!
当我在 WINDOWS SERVICE 中调用相同的方法时,出现以下错误:
连接字符串的 app.config 条目与 web.config:
完全相同
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://*/ProjectModel.csdl|
res://*/ProjectModel.ssdl|
res://*/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
现在,博客显示手动引用 dll:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://Project.Data.dll/ProjectModel.csdl|
res://Project.Data.dll/ProjectModel.ssdl|
res://Project.Data.dll/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
这不是work/fix问题
我能够修复它的唯一方法是使用完全限定名称:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.csdl|
res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.ssdl|
res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.250.125\sqlexpress;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
为什么会这样?为什么这在 Web 项目中有效,但在 windows 服务项目中无效?我最近从 EF5 更改为 EF6,并且弹出了此错误 - 所有这些代码在升级 EF 之前都有效。有没有人知道为什么 how/if 我可以只使用 * 作为我的连接字符串中的 dll 名称?
我认为这是服务 .exe 所在位置的问题,文件未在本地复制,但不, Project.Data.dll 在那里,它是正确的版本。
我使用 FusionLog 尝试查找错误,但没有成功。我很困惑。
将包含 ProjectEntities 的 dll 复制到不同的路径,然后在您的服务项目中引用相同的路径。
请按照以下步骤操作:
1.Write 单击 edmx 文件,然后单击相关实体的打开方式。
2.Select xml 编辑器并点击打开。
3.Scroll 从 .edmx xml 文件的顶部到底部并查找任何错误标记。
4.If 你介意错误然后修正它。
5.Rebuild 解决方案,如果没有发现错误,那么恭喜 :)
恐怕我无法重现您收到的错误,也无法回答您为什么需要更改 metadata
。
也就是说,我确实了解到,对于 EF 连接字符串,Windows 服务需要与 WebApi 不同的 provider connection string
。
以下是重现错误的步骤。唯一的区别是我使用的是 localdb 而不是 SQLExpress。
我的重现步骤的结果代码在 GitHub 在线:https://github.com/bigfont/EntityFrameworkWindowsServiceWebApi。
步骤如下:
创建网站API项目
- 创建 ASP.NET Web API 2 个空项目 (MyWebApi)
- 使用 NuGet,
Install-Package EntityFramework -ProjectName MyWebApi
- 添加一个名为 MyProjectModel 的新 ADO.NET 实体数据模型。
- 添加名为 Entity1 的实体。
- 从模型生成数据库,将其命名为 MyProject 并使用 localdb。
- 运行 (localdb)\v11.0
上的数据库创建脚本
- 添加一个名为
ValuesController
的新 WebApi 控制器,其中包含查询数据库的 Get
方法。
- 在 Visual Studio 中由 运行 进行测试并转到 localhost:123456/api/get
参见:https://msdn.microsoft.com/en-us/data/jj205424.aspx
创建Windows 服务项目
- 创建Windows服务(我的Windows服务)
- 使用 NuGet,
Install-Package EntityFramework -ProjectName MyWindowsService
- 添加一个名为 MyProjectModel 的新 ADO.NET 实体数据模型。
- 添加名为 Entity1 的实体。
- 使用 localdb 从模型生成数据库,将其命名为 MyService。
- 运行 (localdb)\v11.0
上的数据库创建脚本
- 向 OnStart 方法添加一些查询数据库的代码。
- 添加
NT AUTHORITY\SYSTEM
作为 localdb 登录和 MyService db 用户。
- 通过安装、启动和写入文件进行测试:
PowerShell 安装、启动和卸载
Release> installutil .\MyWindowsService.exe
Release> Start-Service MyService
Release> installutil .\MyWindowsService.exe /u
Windows 服务中的 localdb 连接字符串
在 Windows 服务的连接字符串中,我无法使用 (localdb)\v11.0
。相反,我需要使用命名管道。我用这个命令行找到了命名管道:
> SqlLocalDB.exe info v11.0
Name: v11.0
Version: 11.0.2100.60
Shared name:
Owner: MY_COMPUTER\Shaun.Luttin
Auto-create: Yes
State: Running
Last start time: 2015-04-09 5:54:34 PM
Instance pipe name: np:\.\pipe\LOCALDB#1010101\tsql\query
使用实例管道名称生成的连接字符串如下所示。
<connectionStrings>
<add name="MyProjectModelContainer"
connectionString="
metadata=
res://*/MyProjectModel.csdl|
res://*/MyProjectModel.ssdl|
res://*/MyProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=np:\.\pipe\LOCALDB#4BCE6D95\tsql\query;
initial catalog=MyService;
Integrated Security=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
而 WebApi 连接字符串如下所示:
<add name="MyProjectModelContainer"
connectionString="
metadata=
res://*/MyProjectModel.csdl|
res://*/MyProjectModel.ssdl|
res://*/MyProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=(localdb)\v11.0;
initial catalog=MyProject;
integrated security=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
另请参阅:http://www.connectionstrings.com/sql-server-2012/
需要对 Windows 服务使用不同的连接字符串,这与您发现的问题类似。在 Sql Server Management Studio、Visual Studio 和 WebApi 中,我们可以通过调用数据源 (localdb)\v.11 进行连接,而在 Web 服务中,我们需要通过它的实例来调用它命名管道。
这里有一个怀疑:可能是计算机上有多个localdb实例,我们需要绝对指定我们要使用哪个。不幸的是,这无助于回答您需要更改 metadata
.
的原因
这是一个与您面临的问题相似但不同的问题,因为您需要更改 Entity Framework metadata
而我需要更改 provider connection string
。巧合?
为什么会这样?
您遇到的问题只是在 运行 将您的应用程序 windows 服务。
我为什么要关心?
您可能知道,在查找每个引用的 DLL 文件时都有一个特定的 well documented 顺序。通常它开始在当前应用程序目录中搜索 DLL,然后转到更多 "public" 位置,如 PATH
文件夹、GAC 等
二进制植入的主要思想是将恶意DLL文件植入到合法DLL文件夹之前被检查的文件夹中。加载此类恶意 DLL 将使攻击者获得对系统的控制权。
通常 windows 服务 运行 在高级帐户下(LocalSystem、LocalService、NetworkService 等 )因此 windows 服务是很好的目标二元种植攻击。
我能做什么?
Microsoft 已采取额外的预防措施来降低安全风险,这是有充分理由的。但您可以尝试解决您的问题。
1) 当前目录不是你期望的目录
Windows 服务在系统文件夹中启动(通常类似于 C:\Windows\System32
)
好消息是它很容易修复。您只需在服务启动时更改当前目录。
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
参见 Phil Haack 的 blog post;
2) 通读文档
根据 EF documentation,通配符具有特殊含义,它限制了 运行time 查找 DLL 文件的位置:
If you specify a wildcard (*) for assemblyFullName, the Entity
Framework runtime will search for resources in the following
locations, in this order:
1) The calling assembly.
2) The referenced assemblies.
3) The assemblies in the bin directory of an application.
由于您的工作文件夹设置为系统文件夹,而您的引用可能不在该文件夹中,因此 EF 最终可能会查找错误的位置,并且可能无法加载包含资源的程序集。
3) 使用完全限定的程序集名称确保安全
虽然我对此不完全确定并且没有测试过,但微软可能已经禁止 Windows 服务在不提供完全限定的程序集名称的情况下加载 DLL 以降低注入恶意 DLL 文件的风险;
保护您的 Windows 服务 here(特别是第 5 章)的好读物。
4)调试吧!
EF6 恰好是开源项目。这意味着您可以获得它的完整源代码并进行调试。您可以在 CodePlex here.
上找到项目
首先,这与 SO 上的另一个问题有关:
我已经通过以下 SO 文章和博客阅读并调试了我的问题:
MetadataException: Unable to load the specified metadata resource
和
http://blogs.teamb.com/craigstuntz/2010/08/13/38628/
但是...我还有其他问题 'fix'
我有一个WebAPI(2.1),我的WebAPI中的连接字符串是这样的:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://*/ProjectModel.csdl|
res://*/ProjectModel.ssdl|
res://*/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
当我在我的 WebAPI(伪代码)中对 DbSet
调用 ToList()
时:
DbContext _DbContext = new ProjectEntities();
DbSet<TEntity> _dbSet = _DbContext.Set<TEntity>();
_dbSet.ToList();
效果很好!
当我在 WINDOWS SERVICE 中调用相同的方法时,出现以下错误:
连接字符串的 app.config 条目与 web.config:
完全相同<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://*/ProjectModel.csdl|
res://*/ProjectModel.ssdl|
res://*/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
现在,博客显示手动引用 dll:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://Project.Data.dll/ProjectModel.csdl|
res://Project.Data.dll/ProjectModel.ssdl|
res://Project.Data.dll/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
这不是work/fix问题
我能够修复它的唯一方法是使用完全限定名称:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.csdl|
res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.ssdl|
res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.250.125\sqlexpress;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
为什么会这样?为什么这在 Web 项目中有效,但在 windows 服务项目中无效?我最近从 EF5 更改为 EF6,并且弹出了此错误 - 所有这些代码在升级 EF 之前都有效。有没有人知道为什么 how/if 我可以只使用 * 作为我的连接字符串中的 dll 名称?
我认为这是服务 .exe 所在位置的问题,文件未在本地复制,但不, Project.Data.dll 在那里,它是正确的版本。
我使用 FusionLog 尝试查找错误,但没有成功。我很困惑。
将包含 ProjectEntities 的 dll 复制到不同的路径,然后在您的服务项目中引用相同的路径。
请按照以下步骤操作:
1.Write 单击 edmx 文件,然后单击相关实体的打开方式。
2.Select xml 编辑器并点击打开。
3.Scroll 从 .edmx xml 文件的顶部到底部并查找任何错误标记。
4.If 你介意错误然后修正它。 5.Rebuild 解决方案,如果没有发现错误,那么恭喜 :)
恐怕我无法重现您收到的错误,也无法回答您为什么需要更改 metadata
。
也就是说,我确实了解到,对于 EF 连接字符串,Windows 服务需要与 WebApi 不同的 provider connection string
。
以下是重现错误的步骤。唯一的区别是我使用的是 localdb 而不是 SQLExpress。
我的重现步骤的结果代码在 GitHub 在线:https://github.com/bigfont/EntityFrameworkWindowsServiceWebApi。
步骤如下:
创建网站API项目
- 创建 ASP.NET Web API 2 个空项目 (MyWebApi)
- 使用 NuGet,
Install-Package EntityFramework -ProjectName MyWebApi
- 添加一个名为 MyProjectModel 的新 ADO.NET 实体数据模型。
- 添加名为 Entity1 的实体。
- 从模型生成数据库,将其命名为 MyProject 并使用 localdb。
- 运行 (localdb)\v11.0 上的数据库创建脚本
- 添加一个名为
ValuesController
的新 WebApi 控制器,其中包含查询数据库的Get
方法。 - 在 Visual Studio 中由 运行 进行测试并转到 localhost:123456/api/get
参见:https://msdn.microsoft.com/en-us/data/jj205424.aspx
创建Windows 服务项目
- 创建Windows服务(我的Windows服务)
- 使用 NuGet,
Install-Package EntityFramework -ProjectName MyWindowsService
- 添加一个名为 MyProjectModel 的新 ADO.NET 实体数据模型。
- 添加名为 Entity1 的实体。
- 使用 localdb 从模型生成数据库,将其命名为 MyService。
- 运行 (localdb)\v11.0 上的数据库创建脚本
- 向 OnStart 方法添加一些查询数据库的代码。
- 添加
NT AUTHORITY\SYSTEM
作为 localdb 登录和 MyService db 用户。 - 通过安装、启动和写入文件进行测试:
PowerShell 安装、启动和卸载
Release> installutil .\MyWindowsService.exe
Release> Start-Service MyService
Release> installutil .\MyWindowsService.exe /u
Windows 服务中的 localdb 连接字符串
在 Windows 服务的连接字符串中,我无法使用 (localdb)\v11.0
。相反,我需要使用命名管道。我用这个命令行找到了命名管道:
> SqlLocalDB.exe info v11.0
Name: v11.0
Version: 11.0.2100.60
Shared name:
Owner: MY_COMPUTER\Shaun.Luttin
Auto-create: Yes
State: Running
Last start time: 2015-04-09 5:54:34 PM
Instance pipe name: np:\.\pipe\LOCALDB#1010101\tsql\query
使用实例管道名称生成的连接字符串如下所示。
<connectionStrings>
<add name="MyProjectModelContainer"
connectionString="
metadata=
res://*/MyProjectModel.csdl|
res://*/MyProjectModel.ssdl|
res://*/MyProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=np:\.\pipe\LOCALDB#4BCE6D95\tsql\query;
initial catalog=MyService;
Integrated Security=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
而 WebApi 连接字符串如下所示:
<add name="MyProjectModelContainer"
connectionString="
metadata=
res://*/MyProjectModel.csdl|
res://*/MyProjectModel.ssdl|
res://*/MyProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=(localdb)\v11.0;
initial catalog=MyProject;
integrated security=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
另请参阅:http://www.connectionstrings.com/sql-server-2012/
需要对 Windows 服务使用不同的连接字符串,这与您发现的问题类似。在 Sql Server Management Studio、Visual Studio 和 WebApi 中,我们可以通过调用数据源 (localdb)\v.11 进行连接,而在 Web 服务中,我们需要通过它的实例来调用它命名管道。
这里有一个怀疑:可能是计算机上有多个localdb实例,我们需要绝对指定我们要使用哪个。不幸的是,这无助于回答您需要更改 metadata
.
这是一个与您面临的问题相似但不同的问题,因为您需要更改 Entity Framework metadata
而我需要更改 provider connection string
。巧合?
为什么会这样?
您遇到的问题只是在 运行 将您的应用程序 windows 服务。
我为什么要关心?
您可能知道,在查找每个引用的 DLL 文件时都有一个特定的 well documented 顺序。通常它开始在当前应用程序目录中搜索 DLL,然后转到更多 "public" 位置,如 PATH
文件夹、GAC 等
二进制植入的主要思想是将恶意DLL文件植入到合法DLL文件夹之前被检查的文件夹中。加载此类恶意 DLL 将使攻击者获得对系统的控制权。
通常 windows 服务 运行 在高级帐户下(LocalSystem、LocalService、NetworkService 等 )因此 windows 服务是很好的目标二元种植攻击。
我能做什么?
Microsoft 已采取额外的预防措施来降低安全风险,这是有充分理由的。但您可以尝试解决您的问题。
1) 当前目录不是你期望的目录
Windows 服务在系统文件夹中启动(通常类似于 C:\Windows\System32
)
好消息是它很容易修复。您只需在服务启动时更改当前目录。
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
参见 Phil Haack 的 blog post;
2) 通读文档
根据 EF documentation,通配符具有特殊含义,它限制了 运行time 查找 DLL 文件的位置:
If you specify a wildcard (*) for assemblyFullName, the Entity Framework runtime will search for resources in the following locations, in this order:
1) The calling assembly.
2) The referenced assemblies.
3) The assemblies in the bin directory of an application.
由于您的工作文件夹设置为系统文件夹,而您的引用可能不在该文件夹中,因此 EF 最终可能会查找错误的位置,并且可能无法加载包含资源的程序集。
3) 使用完全限定的程序集名称确保安全
虽然我对此不完全确定并且没有测试过,但微软可能已经禁止 Windows 服务在不提供完全限定的程序集名称的情况下加载 DLL 以降低注入恶意 DLL 文件的风险;
保护您的 Windows 服务 here(特别是第 5 章)的好读物。
4)调试吧!
EF6 恰好是开源项目。这意味着您可以获得它的完整源代码并进行调试。您可以在 CodePlex here.
上找到项目