在循环中执行 New-WebServiceProxy 时的不一致行为

Inconsistent behavior when executing New-WebServiceProxy in a loop

我编写的用于调用 WCF Web 服务上的方法的 powershell 脚本有问题。 WCF 服务有一个复合 DataContract 作为其唯一的请求参数。我们最近修改了 ServiceContract(通过添加两个新方法),但是 powershell 脚本没有调用新方法。这是原始合同:

[ServiceContract]
public interface IFileSystemService {
    [OperationContract]
    HashFileResponse HashFile(HashFileRequest req);

    [OperationContract]
    void GenerateFiles(GenerateFilesRequest req);
}

这是新合同:

[ServiceContract]
public interface IFileSystemService {
    [OperationContract]
    HashFileResponse HashFile(HashFileRequest req);

    [OperationContract]
    void GenerateFiles(GenerateFilesRequest req);

    [OperationContract]
    ParseFilePathResponse ParseFilePath(ParseFilePathRequest req);

    [OperationContract]
    ArchiveParsedFileResponse ArchiveParsedFile(ArchiveParsedFileRequest req);
}

GenerateFiles 方法是 PowerShell 脚本调用的方法。我们根本没有修改GenerateFilesRequest DataContract,定义如下:

[DataContract]
public class GenerateFilesRequest : BaseRequest {
    [DataMember(IsRequired = true)]
    public int Id { get; set; }
}

Baserequest 目前是一个空 class 供将来使用(我们所有的请求数据合约都使用它):

[DataContract]
public abstract class BaseRequest {
}

通过其他方式调用此方法始终有效; SoapUI、Fiddler 和通过在整个应用程序中定义的 WCF 合同。

添加这两个新方法后,我们的集成测试失败了,因为 powershell 脚本无法在循环中一致地调用 GenerateFiles 方法(请参阅错误输出)。

当我最初编写此脚本时,我遇到了类似的问题 运行(尽管它要么一直崩溃,要么一直工作)但我设法通过添加 -Namespace 和-Class New-WebServiceProxy cmdlet 的参数。

上下文:我们是 运行 开发人员计算机上的 powershell 脚本,连接到 IISExpress 中托管的 WCF 服务。所有开发人员都遇到相同的问题。

这是我最近修改之前的原始脚本(首选)(这工作正常,但现在大多数调用都失败了):

$sqlServer=$args[0]

function CallFSS($Id) {
    $uri = "http://localhost:1234/FileSystemService.svc?wsdl"
    $srv = New-WebServiceProxy -Uri $uri -Namespace fssNS -Class fssClass
    $req = [fssNS.GenerateFilesRequest](New-Object fssNS.GenerateFilesRequest)
    $req.Id= $Id
    $response = $srv.GenerateFiles($req)
}

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=" + $sqlServer + ";Database=MyDatabase;Integrated Security=True"
$SqlConnection.Open()

$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "select Id FROM MyTable WHERE Status = 2"
$SqlCmd.Connection = $SqlConnection
$sqlResult = $SqlCmd.ExecuteReader()

while ($sqlResult.Read()) {
    $Id = $sqlResult.GetInt32(0)
    Write-Host Generating files for Id $Id
    CallFSS $Id
}

$SqlCmd.Dispose()
$SqlConnection.Dispose()
$SqlConnection.Close()

这是脚本输出的摘录。如您所见,这非常不一致(这样标记的行成功):

Generating files for Id 1
Cannot convert argument "req", with value: "fssNS.GenerateFilesRequest", for "GenerateFiles" to type "fssNS.GenerateFilesRequest": "Cannot convert the  "fssNS.GenerateFilesRequest" value of type "fssNS.GenerateFilesRequest" to type "fssNS.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
Generating files for Id 2
Cannot convert argument "req", with value: "fssNS.GenerateFilesRequest", for "GenerateFiles" to type "fssNS.GenerateFilesRequest": "Cannot convert the  "fssNS.GenerateFilesRequest" value of type "fssNS.GenerateFilesRequest" to type "fssNS.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
**Generating files for Id 3**
Generating files for Id 4
Cannot convert argument "req", with value: "fssNS.GenerateFilesRequest", for "GenerateFiles" to type "fssNS.GenerateFilesRequest": "Cannot convert the  "fssNS.GenerateFilesRequest" value of type "fssNS.GenerateFilesRequest" to type "fssNS.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument Generating files for Id 8 Cannot convert argument "req", with value: "fssNS.GenerateFilesRequest", for "GenerateFiles" to type "fssNS.GenerateFilesRequest": "Cannot convert the  "fssNS.GenerateFilesRequest" value of type "fssNS.GenerateFilesRequest" to type "fssNS.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument  
**Generating files for Id 9**
**Generating files for Id 10**
**Generating files for Id 11**
Generating files for Id 12
Cannot convert argument "req", with value: "fssNS.GenerateFilesRequest", for "GenerateFiles" to type "fssNS.GenerateFilesRequest": "Cannot convert the  "fssNS.GenerateFilesRequest" value of type "fssNS.GenerateFilesRequest" to type "fssNS.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

有时,大多数调用都会通过,只有奇数调用会失败。

这是脚本的另一个版本,它使用 New-WebServiceProxy 自动生成的命名空间:

$sqlServer=$args[0]

function CallFSS($Id) {
    $uri = "http://localhost:1234/FileSystemService.svc?wsdl"
    $srv = New-WebServiceProxy -Uri $uri
    $type = $srv.GetType().Namespace
    $datatype = ($type + '.GenerateFilesRequest')
    $req = New-Object($datatype)
    $req.Id = $Id
    $response = $srv.GenerateFiles($req)
}

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=" + $sqlServer + ";Database=MyDatabase;Integrated Security=True"
$SqlConnection.Open()

$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "select Id FROM MyTable WHERE Status = 2"
$SqlCmd.Connection = $SqlConnection
$sqlResult = $SqlCmd.ExecuteReader()

while ($sqlResult.Read()) {
    $Id = $sqlResult.GetInt32(0)
    Write-Host Generating files for Id $Id
    CallFSS $Id
}

$SqlCmd.Dispose()
$SqlConnection.Dispose()
$SqlConnection.Close()

同样,结果不一致,但我现在得到的错误与自动生成的命名空间有关:

**Generating files for Id 1**
**Generating files for Id 2**
Generating files for Id 3
Cannot convert argument "req", with value:  "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest", for "GenerateFiles"  to type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest": "Cannot  convert the "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest" value of  type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest" to type  "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
Generating files for Id 4
Cannot convert argument "req", with value: "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest", for "GenerateFiles"  to type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest": "Cannot  convert the "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest" value of  type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest" to type  "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3234_FileSystemService_svc_wsdl.GenerateFilesRequest"." At C:\SolutionPath\GENERATE_FILES.ps1:23 char:2
+     $response = $srv.GenerateFiles($req)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

等等...每次执行都不一样

我非常希望这是我做错的事情/误解,因为我即将完全放弃 PowerShell。这可能是缓存问题吗?任何帮助将不胜感激。

好的,我发现了一些东西:)

我在 CallFSS 函数中添加了 GenerateFiles 方法定义的输出:

$srv | gm -Name GenerateFiles | select -ExpandProperty Definition

对于每个成功的请求,输出是

void GenerateFiles(fssNS.GenerateFilesRequest req)

如果遇到错误定义不同:

void GenerateFiles(fssNS.GenerateFilesRequest, oehlvn0y, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null req)

因此,如果您使用完整限定名创建对象,它应该可以工作:

function CallFSS($Id) 
{
    $uri = "http://localhost:11662/Service1.svc?wsdl"
    $srv = New-WebServiceProxy -Uri $uri -Namespace fssNS -Class fssClass 

    # Get the definition of the GenerateFiles method
    $definition = $srv | gm -Name GenerateFiles | select -ExpandProperty Definition

    # Extract the full qualified type name of the first parameter
    $paramType = [regex]::Match($definition, 'GenerateFiles\((.*)\s\w+').Groups[1].Value

    $bar = new-object $paramType
    $bar.Id = $Id

    $response = $srv.GenerateFiles($bar)
}

注意:由于正则表达式,此解决方案仅适用于具有一个参数的方法。但是,这是我推荐的实现:

function Invoke-FSS # Invoke is a valid Verb (see Get-Verb)
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
        [int]$Id
    )

    Begin
    {
        $uri = "http://localhost:11662/Service1.svc?wsdl"
        $srv = New-WebServiceProxy -Uri $uri -Namespace fssNS -Class fssClass 

        # Get the definition of the GenerateFiles method
        $definition = $srv | gm -Name GenerateFiles | select -ExpandProperty Definition

        # Extract the full qualified type name of the first parameter
        $paramType = [regex]::Match($definition, 'GenerateFiles\((.*)\s\w+').Groups[1].Value
    }
    Process
    {        
        $bar = new-object $paramType
        $bar.Id = $Id
        $response = $srv.GenerateFiles($bar)
    }
    End
    {
        $srv.Dispose()
    }    
}

在您的示例中,您将通过将 ID 传递给 Invoke-FFS 方法来调用该方法:

$ids = @()
while ($sqlResult.Read()) {
    $ids += $sqlResult.GetInt32(0)
}

$ids | Invoke-FSS

Begin{} 块仅被调用一次(以初始化您的代理)并且 Process{} 块被调用用于 $ids 中的每个项目。最后,End{} 块在最后被调用一次以优雅地处理代理。