无法在Powershell

时间:2019-03-29 12:18:14

标签: oracle powershell oracle-manageddataaccess

我正在尝试使用Powershell和Oracle.ManagedDatabaseAccess.dll连接到现有的oracle数据库。我使用相同的库在C#中运行代码。 该程序集已加载,但我无法使用。

我正在使用Win10和Powershell ISE。 我也使用VS2017 / 15.3.2和.Net 4.7.02046。

我尝试使用根本不起作用的“ Add-Type -AssemblyName”和“ Add-Type -Path”。这两个选项都立即显示错误。

我可以打电话

[Reflection.Assembly]::LoadFile("myrootpath\.nuget\packages\oracle.manageddataaccess.core\2.18.5\lib\netstandard2.0\Oracle.ManagedDataAccess.dll")

但是我得到了

GAC    Version        Location 
---    -------        --------                                                                                                                                                                
False   v4.0.30319     C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Oracle.ManagedDataAccess\v4.0_2.0.18.3__89b483f429c47342\Oracle.ManagedDataAccess.dll       

我使用gcautil将程序集手动添加到了GAC。 现在我得到了

GAC    Version        Location   
---    -------        --------                                                                                                                                                                
True   v4.0.30319     C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Oracle.ManagedDataAccess\v4.0_2.0.18.3__89b483f429c47342\Oracle.ManagedDataAccess.dll  

现在我打电话给

$command = New-Object Oracle.DataAccess.Client.OracleCommand($sql,$conn)

我收到以下错误:

New-Object : The Type [Oracle.ManagedDataAccess.Client.OracleConnection] cannot be found. Make sure that you load the Assembly that contains this Type.
In Line:2 Character:8
+ $conn= New-Object Oracle.ManagedDataAccess.Client.OracleConnection($c ...
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

我了解了有关编译小型C#控制台应用程序(自可行)并从powershell调用该解决方案的解决方案。这可能是可行的,但是我真的很想了解这里的问题。 任何帮助将不胜感激。

更新/编辑:

PS H:\> Add-Type -Path "C:\mypath\Oracle.ManagedDataAccess.dll"
Add-Type : At least one type in the assembly could not be loaded.
    + CategoryInfo          : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
    + FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeCommand

PS H:\> $error[0].Exception.LoaderExceptions[0]
The type "System.IO.Stream" in the assembly "netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" could not be loaded.

Netstandard2.0.0.0是Oracle.ManagedDataAccess.dll的要求,该文件也必须安装在我的VS项目中。

System.IO.Stream应该在PS中可用而无需加载。可能是版本冲突,因为我的主要开发环境是.Net 4.7.02046?

1 个答案:

答案 0 :(得分:1)

首先,您需要确保所使用的NuGet程序集版本与所使用的PowerShell版本兼容。您提到您是在Windows 10上的PowerShell ISE中编写代码。由于PowerShell ISE不支持PowerShell Core,并且Windows 10 Anniversary包含5.1 out of the box,因此我假设您在Windows PowerShell中编写代码,而不是较新的PowerShell Core

这很重要,因为在Windows PowerShell 5.1之前,PowerShell基于.NET Framework,而在使用PowerShell Core的情况下,Microsoft切换到了多平台.NET Core。而且, Oracle为每个发布了不同版本的Oracle数据提供程序

在使用.NET Framework(Windows PowerShell 5.1及更低版本)时,请使用Oracle.ManagedDataAccess
使用.NET Core(PowerShell Core 6及更高版本)时,请使用Oracle.ManagedDataAccess.Core

第二,您需要将程序集导入到当前会话中。尽管这听起来很容易,但是Oracle有一个绝妙的主意,即对于托管和非托管版本的Core版本和非Core版本都使用相同的程序集名称,因此您将希望使用完整的程序集路径来实现。否则,您可能会不可靠地导入不兼容的版本。

幸运的是,您可以结合使用找到程序集和下载程序集(如果缺少该程序集)到快速操作中,而仅使用在用户空间中运行的本机PowerShell命令进行操作。

  1. 使用Get-Package检查ODP.NET是否已安装并获取其位置。
  2. 如果返回ObjectNotFound异常,请使用Install-Package从NuGet.org下载ODP.NET。
  3. 如果返回ObjectNotFound异常,则将NuGet.org安装为不受信任的PackageProvider。
  4. 通过Add-Type和程序集路径显式导入新安装的程序集。

然后可以按预期在PowerShell中使用ODP.NET。

示例实现

#Requires -PSEdition Desktop

try { $nugetPkg = (Get-Package Oracle.ManagedDataAccess.Core -ProviderName NuGet -MinimumVersion "2.18.3" -ErrorAction Stop -Debug:$false).Source }
catch [System.Exception] {
    if($_.CategoryInfo.Category -eq "ObjectNotFound") {
        # Register NuGet.org as a package source if it is missing.
        try { Get-PackageSource -Name "nuget.org" -ProviderName NuGet -Debug:$false -ErrorAction Stop }
        catch [System.Exception] {
            if($_.CategoryInfo.Category -eq "ObjectNotFound") {
                Register-PackageSource -Name "nuget.org" -Location "https://www.nuget.org/api/v2/" -ProviderName NuGet -Debug:$false
            }
            else { throw $_ }
        }

        # Install Oracle drivers.
        $pkg = (Install-Package Oracle.ManagedDataAccess.Core -ProviderName NuGet -MinimumVersion "2.18.3" -Verbose -Scope CurrentUser -Force -Debug:$false).Payload.Directories[0]
        $nugetPkg = Join-Path -Path $pkg.Location -ChildPath $pkg.Name
        Remove-Variable pkg
    }
    else { throw $_ }
}
# Add ODP.NET to the assemblies available in-session.  Using the path instead of AssemblyName avoids conflicts with ODP.NET (Core).
# See https://docs.oracle.com/en/database/oracle/oracle-data-access-components/18.3/odpnt/index.html for documentation.
Add-Type -Path (Get-ChildItem (Split-Path ($nugetPkg) -Parent) -Filter "Oracle.ManagedDataAccess.dll" -Recurse -File)[0].FullName


# Begin Query Operations
try {
    # TESTB connection string.  You can replace the Data Source value with a tnsnames alias.
    $con = New-Object Oracle.ManagedDataAccess.Client.OracleConnection(
        "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testb)(PORT=1521)) (CONNECT_DATA=(SERVICE_NAME=testb))); User Id=test;Password=user;"
        )
    $con.Open()
    Write-Host ("Connected to database: {0} – running on host: {1} – Servicename: {2} – Serverversion: {3}" -f $con.DatabaseName, $con.HostName, $con.ServiceName, $con.ServerVersion) -ForegroundColor Cyan -BackgroundColor Black

    # Get a single value
    Write-Host "Returning a Scalar Value from Oracle" -ForegroundColor Cyan -BackgroundColor Black
    $cmd = $con.CreateCommand()
    $cmd.CommandText = "SELECT 'Hello World!' FROM DUAL"
    $cmd.ExecuteScalar()

    # Get several defined values
    Write-Host "Retrieving manually typed ordinals from v`$version via OracleDataReader" -ForegroundColor Cyan -BackgroundColor Black
    $cmd.CommandText = 'SELECT BANNER FROM v$version'
    $rdr = $cmd.ExecuteReader()
    while ($rdr.Read()) {
        $rdr.GetString(0)
    }
    $rdr.Dispose()

    # Use a DataAdapter to get defined Data objects.
    Write-Host "Using a DataAdapter to return NLS_SESSION_PARAMETERS from Oracle" -ForegroundColor Cyan -BackgroundColor Black
    $adap = New-Object Oracle.ManagedDataAccess.Client.OracleDataAdapter("SELECT * FROM NLS_SESSION_PARAMETERS",$con)
    # Create the builder for the adapter to automatically generate the Command when needed.
    $oraCmdBldr = New-Object Oracle.ManagedDataAccess.Client.OracleCommandBuilder($adap)

    [System.Data.DataSet]$dataset = New-Object System.Data.DataSet
    $adap.Fill($dataset,"NLSSesParams")
    $dataset.Tables["NLSSesParams"]
    Remove-Variable dataset
}
catch {
    Write-Error ("Can't open connection: {0}n{1}" -f $con.ConnectionString, $_.Exception.ToString())
}
finally {
    if ($con.State -eq 'Open') { $con.close() }
    $con.Dispose()
    Write-Host "Disconnected from database" -ForegroundColor Cyan -BackgroundColor Black
}

示例输出

Connected to database: testb – running on host: bat – Servicename: testb – Serverversion: 11.2.0.4.0
Returning a Scalar Value from Oracle
    Hello World!
Retrieving manually typed ordinals from v$version via OracleDataReader
    Oracle Database 11g Release 11.2.0.4.0 - 64bit Production
    PL/SQL Release 11.2.0.4.0 - Production
    CORE    11.2.0.4.0  Production
    TNS for Linux: Version 11.2.0.4.0 - Production
    NLSRTL Version 11.2.0.4.0 - Production
Using a DataAdapter to return NLS_SESSION_PARAMETERS from Oracle
    17

Disconnected from database
    PARAMETER               VALUE                       
    ---------               -----                       
    NLS_LANGUAGE            AMERICAN                    
    NLS_TERRITORY           AMERICA                     
    NLS_CURRENCY            $                           
    NLS_ISO_CURRENCY        AMERICA                     
    NLS_NUMERIC_CHARACTERS  .,                          
    NLS_CALENDAR            GREGORIAN                   
    NLS_DATE_FORMAT         DD-MON-RR                   
    NLS_DATE_LANGUAGE       AMERICAN                    
    NLS_SORT                BINARY                      
    NLS_TIME_FORMAT         HH.MI.SSXFF AM              
    NLS_TIMESTAMP_FORMAT    DD-MON-RR HH.MI.SSXFF AM    
    NLS_TIME_TZ_FORMAT      HH.MI.SSXFF AM TZR          
    NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
    NLS_DUAL_CURRENCY       $                           
    NLS_COMP                BINARY                      
    NLS_LENGTH_SEMANTICS    BYTE                        
    NLS_NCHAR_CONV_EXCP     FALSE

可以通过以下方式轻松修改此代码以在PowerShell Core中运行:

  1. #Requires -PSEdition Desktop代替#Requires -PSEdition Core
  2. 将安装程序集从Oracle.ManagedDataAccess更改为Oracle.ManagedDataAccess.Core
  3. -MinimumVersionGet-Package的{​​{1}}参数值从Install-Package更改为"19.3.1"