Powershell: Error consuming WCF services with MTOM message encoding
Asked Answered
E

2

11

I'm currently exploring powershell capabilities, but I have encountered a problem that I have not been able to solve. Any quick tips would be greatly appreciated =)

My goal: Invoke methods from a WCF service (configured with MTOM message encoding) from powershell v2.0 (hopefully using the new-webserviceproxy cmdlet)

My problem: the new-webserviceproxy cmdlet cannot parse the service's response correctly when message encoding is set to Mtom. I receive the following error:

PowerShell:

$proxyObject = New-WebServiceProxy -URI "http://myserver.com/AccessService.svc?wsdl"
$proxyObject.TestWebServiceConnection()

Exception calling "TestWebServiceConnection" with "0" argument(s): "Client found response content type of 'multipart/related; type="application/xop+xml";start="<http://tempuri.org/0>";boundary="uuid:
4001d529-32b9-4560-9f4b-550c35c67b03+id=4";start-info="text/xml"', but expected 'text/xml'.
The request failed with the error message:
--
--uuid:4001d529-32b9-4560-9f4b-550c35c67b03+id=4
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<TestWebServiceConnectionResponse xmlns="http://myserver.com/">
<TestWebServiceConnectionResult>success</TestWebServiceConnectionResult>
</TestWebServiceConnectionResponse>
</s:Body>
</s:Envelope>
--uuid:4001d529-32b9-4560-9f4b-550c35c67b03+id=4--
--."
At line:1 char:38
+ $proxyObject.TestWebServiceConnection <<<< () >> error.txt
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Note I am able to consume the WCF service through other clients and even the wcfclient tool provided by Microsoft. You can see that the TestWebServiceConnectionResult returned success, but it doesn't seem like the proxy object was able to parse the response.

Behavior:

<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceThrottling maxConcurrentCalls="100" maxConcurrentSessions="100"/>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>

Binding(I've excluded the timeout values/ reader quota and message sizes since the permutations of their values do seem not relevant to my problem):


<basicHttpBinding>
<binding name="basicHttpEndpointBinding" messageEncoding="Mtom">
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</basicHttpBinding>

Service

<service behaviorConfiguration="MyServiceBehavior" name="MyService.AccessService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpEndpointBinding" name="basicHttpEndpointAccessService" bindingNamespace="http://myserver.com/" contract="MyService.IAccessService"/>
<endpoint address="mex" binding="basicHttpBinding" bindingConfiguration="basicHttpEndpointBinding" name="mexEndpointAccess" contract="IMetadataExchange"/>
</service>
Excruciating answered 6/4, 2011 at 20:53 Comment(3)
As a test, can you change the service message coding to text and see if it starts working? (This would prove that the MTOM encoding is the source of the problem)Waac
I think the encoding for WCF must be the same when sending and receiving (see #284649). New-WebServiceProxy might only use text/xml. You could try creating a wrapper class in .NET that invokes the service, and use that class within PowerShell?Mastin
Bob, I do believe this is the case, but I cannot seem to find any sort of property that would allow me to set the message encoding used by the object returned by this cmdlet. Creating wrapper method in .Net will probably work (just slightly disappointed that there's no way to do this with the already-provided cmdlet). I actually leaning towards writing some functions in powershell to call svcutil on the wsdl, compile the generated proxy class, then specify my own binding (with desired configuration) and endpoints.Excruciating
E
7

As of the time of this writing, I still have not been able to successfully use the New-WebServiceProxy cmdlet with a WCF service with MTOM enabled; it does not look like the cmdlet supports it. My workaround involved running svcutil.exe against the wsdl, and then compiling the class into a dll using csc.exe. I then loaded the generated assembly into the powershell run time, and then configured the endpoint and binding of the proxy class manually:

Generating the .cs file from your wsdl:

$svcUri = "http://yourdomain/yourService.svc?wsdl";
$csFile = $className + '.cs';   # The name of the generated .cs file
$dllName = [System.IO.Path]::Combine($temp, $className + ".dll")
$svcUtilresult = svcutil.exe /noConfig /out:$csFile $svcUri

Note svcutil.exe and csc.exe may not be in your powershell's PATH. You can either add it to your PATH or use the full path. Svcutil can be found within your Microsoft SDKs\Windows\<version>\bin. csc.exe is located in your %windir%Microsoft .Net folder

Once you have generated the .cs file, you will need to compile it into a dll:

&"csc.exe" /t:library /out:$dllName $csFile

Load the compiled dll into powershell:

$fileStream = ([System.IO.FileInfo] (Get-Item ".\$dllName")).OpenRead()
$dllBytes = new-object byte[] $fileStream.Length
$fileStream.Read($dllBytes, 0, $fileStream.Length)
$fileStream.Close()

[System.Reflection.Assembly]::Load($dllBytes)

Instantiate the proxy client in powershell:

# Load System.ServiceModel, which can be found in your Framework\v3.0\Windows Communication Foundation folder
[System.Reflection.Assembly]::LoadFile($pathToSystemServiceModel)

# className is the name of your service
$serviceClientName = $className + "Client"

$basicHttpBinding = New-Object System.ServiceModel.BasicHttpBinding
$basicHttpBinding.MessageEncoding = [System.ServiceModel.WSMessageEncoding]::Mtom

$endPoint = New-Object System.ServiceModel.EndpointAddress($svcUri)
$wsClient = New-Object $serviceClientname($basicHttpBinding, $endPoint)
Excruciating answered 4/4, 2012 at 22:9 Comment(3)
@Kiquenet, at that point it depends on what your service exposes. Without reviving the project that prompted all this, I can tell you an example of such call would be: $wsClient.Foo() or $wsClient.getItem($itemId). I think you can even run the Get-Member cmdlet on your $wsClient to enumerate its members/methods: $wsClient | Get-Member.Excruciating
Discover members/methods using Get-MemberMahan
@Mahan , did you embolden the word Discover for a reason? Just to make sure that we're on the same page, I expressly chose the word enumerate to convey the same idea.Excruciating
S
1

I was having a similar problem. However I happened to already have the ClientBase generated code compiled into a local assembly.

My solution was:

add-type -path "..\..\bin\MYassemblyWithWCFCLient.dll"
$binding = new-object system.servicemodel.basichttpbinding
$binding.MessageEncoding = "Mtom"
$endpoint = new-object System.ServiceModel.EndpointAddress("http://whodunit.oops/mtomandjerry.svc")
$regProxy = new-object MySpecialNamespace.OopsServiceContractClient($binding, $endpoint)
Sommerville answered 11/5, 2013 at 0:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.