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

时间:2015-06-29 14:26:17

标签: wcf powershell powershell-v3.0 iis-express

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

FOR /D %%a in (*) DO (
  PUSHD "%%a"
  FOR /D %%b in (*) DO RD /S /Q "%%b"
  POPD
)

这是新合同:

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

    [OperationContract]
    void GenerateFiles(GenerateFilesRequest req);
}

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

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

    [OperationContract]
    void GenerateFiles(GenerateFilesRequest req);

    [OperationContract]
    ParseFilePathResponse ParseFilePath(ParseFilePathRequest req);

    [OperationContract]
    ArchiveParsedFileResponse ArchiveParsedFile(ArchiveParsedFileRequest req);
}

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

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

通过其他方式调用此方法可以始终如一地工作;在整个应用程序中定义了SoapUI,Fiddler和WCF合同。

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

当我最初编写这个脚本时,我遇到了类似的问题(虽然它要么一直破坏,要么一直有效)但我设法通过将-Namespace和-Class参数添加到New来获得服务调用。 -WebServiceProxy cmdlet。

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

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

[DataContract]
public abstract class BaseRequest {
}

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

$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()

有时,大多数呼叫都会通过,只有奇数呼叫失败。

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

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

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

$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()

等......执行每次都不同。

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

1 个答案:

答案 0 :(得分:1)

好的,我找到了一些东西:)

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

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

Foreach成功请求,输出

  

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{}块在结束时被调用一次,以便优雅地处理代理。