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=&quot;          
     data source=192.168.0.1;          
     initial catalog=Project;          
     persist security info=True;          
     user id=***;          
     password=***;          
     multipleactiveresultsets=True;          
     App=EntityFramework&quot;" 
     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=&quot;          
     data source=192.168.0.1;          
     initial catalog=Project;          
     persist security info=True;          
     user id=***;          
     password=***;          
     multipleactiveresultsets=True;          
     App=EntityFramework&quot;" 
     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=&quot;          
         data source=192.168.0.1;          
         initial catalog=Project;          
         persist security info=True;          
         user id=***;          
         password=***;          
         multipleactiveresultsets=True;          
         App=EntityFramework&quot;" 
         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=&quot;          
         data source=192.168.250.125\sqlexpress;          
         initial catalog=Project;          
         persist security info=True;          
         user id=***;          
         password=***;          
         multipleactiveresultsets=True;          
         App=EntityFramework&quot;" 
         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项目

  1. 创建 ASP.NET Web API 2 个空项目 (MyWebApi)
  2. 使用 NuGet,Install-Package EntityFramework -ProjectName MyWebApi
  3. 添加一个名为 MyProjectModel 的新 ADO.NET 实体数据模型。
  4. 添加名为 Entity1 的实体。
  5. 从模型生成数据库,将其命名为 MyProject 并使用 localdb。
  6. 运行 (localdb)\v11.0
  7. 上的数据库创建脚本
  8. 添加一个名为 ValuesController 的新 WebApi 控制器,其中包含查询数据库的 Get 方法。
  9. 在 Visual Studio 中由 运行 进行测试并转到 localhost:123456/api/get

参见:https://msdn.microsoft.com/en-us/data/jj205424.aspx

创建Windows 服务项目

  1. 创建Windows服务(我的Windows服务)
  2. 使用 NuGet,Install-Package EntityFramework -ProjectName MyWindowsService
  3. 添加一个名为 MyProjectModel 的新 ADO.NET 实体数据模型。
  4. 添加名为 Entity1 的实体。
  5. 使用 localdb 从模型生成数据库,将其命名为 MyService。
  6. 运行 (localdb)\v11.0
  7. 上的数据库创建脚本
  8. 向 OnStart 方法添加一些查询数据库的代码。
  9. 添加 NT AUTHORITY\SYSTEM 作为 localdb 登录和 MyService db 用户。
  10. 通过安装、启动和写入文件进行测试:

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=&quot;
    data source=np:\.\pipe\LOCALDB#4BCE6D95\tsql\query;
    initial catalog=MyService;
    Integrated Security=True;
    MultipleActiveResultSets=True;
    App=EntityFramework&quot;" 
         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=&quot;
    data source=(localdb)\v11.0;
    initial catalog=MyProject;
    integrated security=True;
    MultipleActiveResultSets=True;
    App=EntityFramework&quot;" 
         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.

上找到项目