【问题标题】:Entity Framework 6: Unable to load the specified metadata resource实体框架 6:无法加载指定的元数据资源
【发布时间】:2015-05-15 02:20:42
【问题描述】:

首先,这与 SO 上的另一个问题有关:

我已经阅读并调试了以下 SO 文章和博客的问题:

MetadataException: Unable to load the specified metadata resource

http://blogs.teamb.com/craigstuntz/2010/08/13/38628/

但是...除了这个“修复”之外,我还有其他问题

我有一个 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>

这不起作用/解决问题

我能够修复它的唯一方法是使用完全限定名称:

<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 之前都有效。有没有人知道为什么以及如何/如果我可以在我的连接字符串中使用 * 作为 dll 名称?

我认为这是服务 .exe 运行位置的问题,并且没有在本地复制文件,但是不,Project.Data.dll 是在那里,这是正确的版本。

我使用 FusionLog 尝试查找错误,但没有运气。我很困惑。

【问题讨论】:

  • 您的 Windows 服务是否在不同的凭据下运行?他们是否具有连接到 sql server 的适当权限?
  • 尝试将连接字符串烘焙到代码中,看看是文件问题还是连接本身问题。

标签: c# entity-framework configuration windows-services entity-framework-6


【解决方案1】:

为什么会这样?

您遇到的问题只是在将您的应用程序作为 Windows 服务运行时防止二进制植入或 DLL 劫持攻击 (read more) 的额外安全措施的结果。

我为什么要关心?

您可能知道,在查找每个引用的 DLL 文件时,都有一个特定的 well documented 顺序。通常它会开始在当前应用程序目录中搜索 DLL,然后转到更多“公共”位置,例如 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,通配符具有特殊含义,它限制了运行时查找 DLL 文件的位置:

如果您为 assemblyFullName 指定通配符 (*),则实体 框架运行时会在下面搜索资源 位置,按以下顺序:

1) 调用程序集。

2) 引用的程序集。

3) 应用程序的 bin 目录中的程序集。

由于您的工作文件夹设置为系统文件夹,而您的引用可能不存在,EF 可能最终会查找错误的位置,并且您的包含资源的程序集可能不会被加载。

3) 使用完全限定的程序集名称确保安全

虽然我对此并不完全确定,也没有测试过,但微软可能只是不允许 Windows 服务加载 DLL 而不提供完全限定的程序集名称,以降低注入恶意 DLL 文件的风险;

关于保护您的 Windows 服务here(特别是第 5 章)的好读物。

4) 调试它!

EF6 恰好是开源项目。这意味着您可以获得它的完整源代码并对其进行调试。您可以在 CodePlex here 上找到项目。

【讨论】:

  • 嗯,我很快就会对此进行测试,但这似乎可以正确回答问题。我不知道为什么赏金分给了别人!!!!
  • @SteveStokes 我收到了赏金积分,因为在赏金的宽限期结束后,我的答案是投票最高的答案。 “如果您在 7 天内(加上宽限期)未授予您的赏金,则在赏金开始后创建的最低得分为 2 的最高投票答案将获得一半的赏金。” stackoverflow.com/help/bounty
  • 我现在看到了,但遗憾的是这不是最好的答案。
  • 使用 Phil Haack 的修复程序适用于我的 aspnet core mvc 应用程序 hosted as a windows service
【解决方案2】:

将包含 ProjectEntities 的 dll 复制到不同的路径,然后在您的服务项目中引用相同的路径。

【讨论】:

  • 你能解释清楚一点吗?我愿意尝试任何东西
【解决方案3】:

恐怕我无法重现您收到的错误,或者回答您为什么需要更改 metadata

也就是说,我确实了解到,对于 EF 连接字符串,Windows 服务需要与 WebApi 不同的provider connection string

以下是重现错误的步骤。唯一的区别是我使用的是 localdb 而不是 SQLExpress。

我的步骤复制生成的代码在 GitHub 上在线:https://github.com/bigfont/EntityFrameworkWindowsServiceWebApi

以下是这些步骤:

创建 Web 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. 使用查询数据库的Get 方法添加一个名为ValuesController 的新WebApi 控制器。
  8. 通过在 Visual Studio 中运行并转到 localhost:123456/api/get 进行测试

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

创建 Windows 服务项目

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

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 服务使用不同的连接字符串,就像我们对 WebApi 项目所做的那样,这与您发现的问题类似。在 Sql Server Management Studio、Visual Studio 和 WebApi 中,我们可以通过调用数据源 (localdb)\v.11 进行连接,而在 Web 服务中,我们需要通过其名为 pipe 的实例来调用它。

这里有一个疑问:可能是计算机上有多个 localdb 实例,我们需要绝对指定要使用哪个实例。不幸的是,这无助于回答您为什么需要更改 metadata

这是一个与您遇到的问题相似但不同的问题,因为您需要更改实体框架metadata,而我需要更改provider connection string。巧合?

【讨论】:

  • 当然 - 这不是正确的解决方案,不应该给予赏金。我不赞成将正确答案放在首位 - Windows 服务需要元资源的连接字符串中的 dll 版本
【解决方案4】:

请按照以下步骤操作:

1.写点击edmx文件,然后点击相关实体的open with。

2.选择xml编辑器,点击打开。

3.从上到下滚动 .edmx xml 文件并查找任何错误标记。

4.如果您介意错误,请修复它。 5.重建解决方案,如果没有发现错误,那么恭喜:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2014-12-31
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多