Petr
Petr

Reputation: 334

ORACLE ManagedDataAccess Core in PowerShell 7

I have a simple .NET 7 class library referencing this NuGet package (NO other ORACLE packages are included):

<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="3.21.100" />

Then I have a PowerShell module written in C# (also targeting .NET 7). It references the above mentioned project.

<ProjectReference Include="..\Core\Core.csproj" />

The PowerShell module has only one cmdlet, Test-Oracle. It invokes a method from the Core.dll. The method attempts to connect to ORACLE and execute SELECT * FROM dual.

Works perfectly on my machines - it connects to ORACLE and outputs data. My client, running the same code, gets this exception:

System.TypeLoadException: Could not load type 'System.Security.Principal.WindowsImpersonationContext' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral......

Any ideas how to troubleshoot that?


EDIT:

The "core" project, that does the actual querying to ORACLE, references these NuGet packages:

<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="3.21.100" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="serilog.sinks.console" Version="4.1.0" />
<PackageReference Include="serilog.sinks.file" Version="5.0.0" />

The PowerShell module project includes this:

<ItemGroup>
   <PackageReference Include="PowerShellStandard.Library" Version="5.1.0-preview-06">
      <PrivateAssets>All</PrivateAssets>
   </PackageReference>
</ItemGroup>

<ItemGroup>
    <ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>

Upvotes: 1

Views: 433

Answers (1)

mklement0
mklement0

Reputation: 440297

Preface:

  • The answer below does not solve the problem, but may be of interest for authoring PowerShell modules that should enforce use in PowerShell (Core) 7+ only, i.e. should refuse to load in Windows PowerShell.

  • To further narrow down your problem, I suggest implementing an assembly-resolve event handler and logging the results:

    • It is invoked when the runtime itself fails to locate an assembly whose loading is being requested.
    • The ResolveEventArgs.RequestingAssembly property of the event-arguments object contains the assembly that requested the load.

I may not have the complete picture, but my understanding is this:

You're using the PowerShellStandard.Library NuGet package, which implies that your PowerShell module (cmdlet) can also run in Windows PowerShell, not just in PowerShell (Core) 7+.

  • Conceivably, the problem arose from your client running the module in Windows PowerShell, causing an attempt to reference the no-long-present-in .NET (Core) System.Security.Principal.WindowsImpersonationContext type - see this GitHub issue for background.

However, in order to create a PowerShell module that can be used in a regular PowerShell session (as opposed to creating an application that itself hosts PowerShell), you must use the PowerShellStandard.Library package - but there is no built-in way that I know of to then constrain the module to only run in one edition or the other (providing better targeting in the future is the subject of GitHub issue #5541).

Thus, if you want to enforce that your module can be used in PowerShell (Core) only, you'll have to implement that yourself:[1]

  • Binary PowerShell modules support an initialization hook that is called when the module is loaded (imported).

  • In that hook, you can test whether you're running inside .NET Framework (which implies Windows PowerShell) or .NET (Core) (which implies PowerShell (Core)). If the former, you can throw an exception.

Here's a proof-of-concept, using ad hoc-compiled C# code for simplicitly:

Add-Type @'
using System.Management.Automation;

namespace YourNamespace {
  // Initialization hook.
  public class MyModuleAssemblyInitializer : IModuleAssemblyInitializer
  {
      public void OnImport()
      {
          // Test if the runtime is .NET Framework, which implies Windows PowerShell,
          // and throw an exception if so.
          if (System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework ")) 
          {
            throw new System.PlatformNotSupportedException("This module supports PowerShell (Core) only; it cannot run in Windows PowerShell.");
          }
      }
  }
  
  // Sample cmdlet.
  [Cmdlet("Get", "Foo")]
  public class GetFooCmdlet : PSCmdlet {
    protected override void ProcessRecord() {
      WriteObject("Hi from Get-Foo");
    }
  }

}
'@ -PassThru | ForEach-Object Assembly | Import-Module
  • If you execute the above in a PowerShell (Core) session, the import should succeed, and you should be able to call Get-Foo thereafter.

  • If you execute in a Windows PowerShell session, a PlatformNotSupportedException exception should be thrown.


[1] Note: Even though PowerShell module manifests have a CompatiblePSEditions entry in order to declare edition compatibility, it seems to have purely informational character and isn't enforced, up to at least PowerShell (Core) 7.3.7 (the current version as of this writing). Currently, the entry is respected solely when attempting to import Windows PowerShell's system modules, i.e. those stored in $env:windir\System32\WindowsPowerShell\v1.0\Modules, in PowerShell (Core).

Upvotes: 1

Related Questions