Using Entity Framework Core in a Powershell Cmdlet?

与世无争的帅哥 提交于 2020-05-28 06:52:55

问题


Is it possible to build a PowerShell cmdlet that incorporate Entity Framework (Core or EF6) for access to an SQL Server database?

I've been writing cmdlets in C# for years, but I've gone through a nightmare the last coupla days trying to use Entity Framework in a project due to what appear to be assembly version clashes.

Project has three libraries:

  • MyProject.Commands.dll - cmdlets to load
  • MyProject.Lib.dll - common library code used by the cmdlets
  • MyProject.EF.dll - the Entity Framework stuff only.

EF is separate because I use a database-first approach (I have to reverse engineer an existing system), so the various .cs model files built from the database are in a separate unit just so keep them straight.

I'm building all this on Windows 10, and the project only needs to run on this machine. It's talking to an SQL Database in the Azure cloud.

First try: VS 2017 / .Net Framework 4.7.1

After using nuget to add the required packages, everything built fine, but using Import-Module on MyProject.Commands.dll gets me:

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.

After turning on some kind of binding debug logging,

=== 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.

I've never been able to find that exact version of System.ComponentModel.Annotation, but the interwebs are full of reports of this same issue. I tried all kinds of things with the AssemblyResolver in the DLL, different versions of various frameworks, ultimately got nowhere.

Eventually I found this page https://docs.microsoft.com/en-us/ef/core/platforms/ that suggests that EF Core 3.x is not supported on any .NET framework version (on .NET Core), but this blog post from a few days ago https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-3-1-and-entity-framework-6-4/ suggests that it's now supported after all.

The above is a rough retelling because it's from a loooong time ago (late last night) before I went to plan B. This was maddening.

Plan B: EF Core on .NET Core 3.1

Deciding that EF Core is likely a better direction for the future anyway, I got VS 2019 and re-targeted my entire project to EF Core 3.1 on .NET Core 3.1. This is my first foray into .NET Core.

It took a little while to get dependencies sorted out (including swapping out System.Management.Automation for Microsoft.PowerShell.SDK), but eventually it all built. After installing Powershell 6, I'm now greeted with:

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.

Awesome!!

Now it's not able to find version 4.2.2.0 of System.Runtime, and I'm really at a loss.

Inside each project's build area bin\Debug\netcoreapp3.1\ directory is a *.deps.json file that lists all kinds of dependencies, but every single reference to System.Runtime is for ver 4.3.0.

I've tried Process Monitor, various tools for looking at assemblies, poking around the GAC. Nuffin.

Making it an App

Leaving the libraries unchanged, I encapsulated the main logic into a console application and compiled it down to a .EXE.

It worked the first time.

Is this even possible?

The fact that building the console app worked so smoothly suggests that trying to make all this work in PowerShell may well be unsolvable.

A standalone app is in control of its own dependencies, but building DLLs that have to host inside another program (powershell) means we have to work and play well with its dependencies.

It strikes me that it might be possible to carefully analyze the existing pwsh.exe (that's Powershell 6) and find its exact set of dependencies, then look for a version of EF Core that just happens to match that set. This does not seem promising, but would likely be brittle.

As a data point, a previous version of this code worked fine when talking to the SQL server via direct SQL calls, so all the database-access baggage wasn't enough to break cmdlet compat. But adding EF core seems to.

I hate my life. Am I doomed?

EDIT Just to be clear, I'm not trying to use Entity Framework in a PowerShell script, I'm not sure I'd even want to. It's using EF in a C# DLL that does all the heavy lifting of talking to the database programatically

I already have fairly reasonable direct SQL calls (again, in C#) that works pretty well, but I have dozens of tables to reverse engineer, and it's amazingly helpful to have EF do this for me by building the models and all the easy-access code.

    foreach (var item in DbContext.Employee
             .Where(x => x.Salary > 1000.00)
             .OrderBy(x => x.EmployeeId))
    {
       do something
    }

The Entity Framework package built the Employee class from the definition in the database (though you can also use code-first approaches), and the above is literally the only code I had to write.

I've wanted to use EF in a PowerShell cmdlet for a long time but have never been able to make it work.

A nice drive-by read on Entity Framework: https://docs.microsoft.com/en-us/ef/core/

EDIT This evening I found that I had been building my code for .NET Core rather than .NET Standard (which is yet again different from the original .NET Framework), and that explains much of why things were not loading properly.

.NET Core seems to be a portable subset, and I just got lost in the weeds from things that I didn't know were different. Also: "Core" seems to be overloaded (e.g. "Entity Framework Core" can run just fine on ".Net Standard"). Ugh.

I still haven't been able to get this to all work and play well as a cmdlet, but on the plus side I did manage to burn a few more hours.

Likely there's no good solution to this.


回答1:


Unfortunately this is just a limitation of using dlls in powershell. Dependency importing from dlls has never worked well.

If you search long enough you will come across some solutions like people writing code to dependency walk through many layers of dlls importing each one in a loop until they are all imported. Honestly just compiling and calling a exe seems far neater and supportable to me.

We frequently do DB-First scaffolding using EF, We also had the challenge of delivering reverse engineered applications and code in a supportable manor to clients. In the end we accepted that the cleanest way was to use EF Core+.Net Core based MVC Web projects in docker containers. They are cross platform and are just 1 language neatly packaged up.

To answer your question simply, Yes the moment any of us started writing code we became doomed for all eternity.




回答2:


I did some experimenting with this and actually got it to work in Powershell 7, however I couldn't get it to work with Powershell Classes as a code-first approach as powershell classes get created in a way that doesn't gel with dbset. DB-first did work however.



来源:https://stackoverflow.com/questions/59258265/using-entity-framework-core-in-a-powershell-cmdlet

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!