【发布时间】:2020-04-03 02:39:19
【问题描述】:
是否可以构建包含实体框架(Core 或 EF6)的 PowerShell cmdlet 以访问 SQL Server 数据库?
多年来,我一直在用 C# 编写 cmdlet,但由于似乎是程序集版本冲突,我在尝试在项目中使用实体框架的最后几天经历了一场噩梦。
项目有三个库:
-
MyProject.Commands.dll- 要加载的 cmdlet -
MyProject.Lib.dll- cmdlet 使用的公共库代码 -
MyProject.EF.dll- 仅限实体框架。
EF 是独立的,因为我使用数据库优先的方法(我必须对现有系统进行逆向工程),因此从数据库构建的各种 .cs 模型文件位于一个单独的单元中,因此请保持它们正常。
我在 Windows 10 上构建所有这些,并且该项目只需要在 这台机器上运行。它正在与 Azure 云中的 SQL 数据库通信。
第一次尝试:VS 2017 / .Net Framework 4.7.1
使用 nuget 添加所需的包后,一切正常,但在 MyProject.Commands.dll 上使用 Import-Module 让我明白:
System.IO.FileNotFoundException: Could not load file or assembly \
'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' \
or one of its dependencies. The system cannot find the file specified.
开启某种绑定调试日志后,
=== Pre-bind state information ===
LOG: DisplayName = System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
(Fully-specified)
LOG: Appbase = file:///C:/Windows/System32/WindowsPowerShell/v1.0/
LOG: Initial PrivatePath = NULL
Calling assembly : Microsoft.EntityFrameworkCore.Relational, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60.
我一直无法找到System.ComponentModel.Annotation 的确切版本,但互联网上到处都是关于同一问题的报告。我用DLL中的AssemblyResolver尝试了各种各样的东西,各种框架的不同版本,最终一无所获。
最终我发现这个页面 https://docs.microsoft.com/en-us/ef/core/platforms/ 表明任何 .NET 框架版本(在 .NET Core 上)都不支持 EF Core 3.x,但是几天前的这篇博客文章 https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-3-1-and-entity-framework-6-4/ 表明它是毕竟现在支持了。
以上是粗略的复述,因为它来自很久以前(昨晚深夜),在我去 B 计划之前。这太疯狂了。
计划 B:.NET Core 3.1 上的 EF Core
决定无论如何,EF Core 可能是未来更好的方向,我得到了 VS 2019 并将我的整个项目重新定位到 .NET Core 3.1 上的 EF Core 3.1。这是我第一次涉足 .NET Core。
整理依赖关系需要一些时间(包括将System.Management.Automation 换成Microsoft.PowerShell.SDK),但最终一切都建立了。安装 Powershell 6 后,我现在收到:
VERBOSE: Loading module from path 'D:\Dev\MyProject\MyProject.Commands\bin\Debug\netcoreapp3.1\MyProject.Commands.dll'.
Import-Module : Could not load file or assembly 'System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f
7f11d50a3a'. The system cannot find the file specified.
太棒了!!
现在找不到System.Runtime的4.2.2.0版本了,我真的很茫然。
在每个项目的构建区域bin\Debug\netcoreapp3.1\ 目录中都有一个*.deps.json 文件,其中列出了各种依赖项,但对System.Runtime 的每一个引用都是针对4.3.0 版的。
我已经尝试过 Process Monitor,各种用于查看程序集的工具,以及 GAC 周围的探索。纳芬。
让它成为一个应用程序
保持库不变,我将主要逻辑封装到控制台应用程序中,并将其编译为.EXE。
第一次成功了。
这可能吗?
构建控制台应用程序如此顺利的事实表明,试图在 PowerShell 中完成所有这些工作很可能无法解决。
一个独立的应用程序可以控制它自己的依赖项,但是构建必须托管在另一个程序 (powershell) 中的 DLL 意味着我们必须很好地处理 它的 依赖项。
让我感到震惊的是,可能可以仔细分析现有的 pwsh.exe(即 Powershell 6)并找到其确切的依赖项集,然后寻找刚刚发生的 EF Core 版本以匹配该集合。这似乎没有希望,但可能很脆弱。
作为数据点,此代码的先前版本在通过直接 SQL 调用与 SQL 服务器通信时运行良好,因此所有数据库访问包都不足以破坏 cmdlet 兼容性。但是添加 EF 内核似乎可以。
我讨厌我的生活。我注定要失败吗?
编辑为了清楚起见,我并没有尝试在 PowerShell 脚本 中使用实体框架,我什至不确定我是否想要。它在 C# DLL 中使用 EF,以编程方式与数据库进行对话的所有繁重工作
我已经有相当合理的直接 SQL 调用(同样,在 C# 中)运行良好,但我有几十个表需要逆向工程,让 EF 通过构建模型和所有易于访问的代码。
foreach (var item in DbContext.Employee
.Where(x => x.Salary > 1000.00)
.OrderBy(x => x.EmployeeId))
{
do something
}
Entity Framework 包根据数据库中的定义构建了Employee 类(尽管您也可以使用代码优先的方法),而以上实际上是我必须编写的唯一代码。
长期以来,我一直想在 PowerShell cmdlet 中使用 EF,但一直未能成功。
关于实体框架的不错的路过阅读:https://docs.microsoft.com/en-us/ef/core/
编辑今天晚上我发现我一直在为 .NET Core 而不是 .NET Standard(这又与原来的 .NET Framework 不同)构建我的代码,这解释了很多为什么加载不正确。
.NET Core 似乎是一个可移植的子集,而我只是因为我不知道有什么不同的东西而迷失了方向。另外:“核心”似乎超载(例如“实体框架核心”可以在“.Net Standard”上正常运行)。呃。
我仍然无法将它作为一个 cmdlet 正常工作和发挥作用,但从好的方面来说,我确实设法多消耗了几个小时。
可能没有好的解决方案。
EDIT (6/2020) 今天早上我发现我六个月前打开的程序集绑定日志记录正在用 gigabytes 的 HTM 文件。哎呀。
This post 包含有关如何通过注册表启用/禁用此功能的说明。
【问题讨论】:
-
你正在采取强硬的方法。 Power Shell 使用命令行实用程序 sqlcmd.exe,您可以从 msdn 下载该实用程序。请参阅:docs.microsoft.com/en-us/sql/tools/… 您可以构建一个 bat 文件或使用 c# Process 类从 c# 项目中运行 sqlcmd.exe。
-
哇,不。如果我只想使用常规 SQL,那对我来说已经很好了。实体框架显着地更容易与数据库交互,尤其是对于双向操作(获取一些数据、更新一些数据),而将其外包给批处理文件将是一个巨大的倒退。
-
有趣的是,我在尝试在 powershell 脚本中添加类型一些 nuget 包时看到了类似的问题。 Fusion Binding 扫描的路径是相对于 powershell.exe 的,因此它不能总是找到依赖项,而且我不得不自己从下往上手动“添加类型”整个树。相比之下,控制台应用程序中的绑定路径搜索是相对于控制台 .exe 的,它会自动在 \bin 文件夹中查找依赖程序集。在加载类库之前,您可以在 Annotations dll 上尝试 Add-Type ...
-
SQLCMD 比使用 SQL Client 高效得多。 SQLCMD 设计用于归档大型数据库和使用 SQL 客户端在几分钟内运行需要 30 多分钟才能运行的查询。我通常使用带有 SQLCMD 的 CSV 选项来与数据库交互。正如我所说,您可以使用网络库 Process 来运行命令行实用程序。
-
@Luca - 不。有太多冲突的依赖项,其中 EF 有一个程序集+版本要求链,其中一些与 Powershell 的程序集+版本要求明确冲突。我的感觉是可以通过做不受支持的事情在狭窄的情况下解决这个问题,但它会在更新期间中断。这太令人失望了。我最终只是做了一个可以正常工作的标准 .exe。所有那些建议
sqlcmd的人显然都误解了这个问题。
标签: c# powershell entity-framework-core cmdlets