9

I have a hand-written WCF proxy in it's own assembly, it's very simple:

public class MyServiceClient : ClientBase<IMyService>, IMyService
{
    public MyServiceClient()
    {
    }

    public MyServiceClient(string endpointConfigurationName) :
        base(endpointConfigurationName)
    {
    }
}

I am loading this into a Powershell script:

Add-Type -Path "$LocalPath\MyService.Client.dll"
Add-Type -Path "$LocalPath\MyService.Contracts.dll"

I am then trying to set the App.config (as per other posts on SO) so that the client can be instantiated with an Endpoint defined in config, rather than in the script itself:

[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", "$LocalPath\MyService.Client.dll.config")

I have checked the AppDomain and the config file is set as its ConfigurationFile property.

When I create an instance of the client:

$endpointName = "MyServiceHttpEndpoint" # defined in the app.config file
$myclient = New-Object MyService.Client.MyServiceClient($endpointName)

It falls over saying:

Exception calling ".ctor" with "1" argument(s): "Could not find endpoint element with name 'MyServiceHttpEndpoint' and contract 'MyService.Contracts.IMyService' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element."

Any ideas? I don't want to manually create the endpoint in the script file - it needs to be read from config.

Community
  • 1
  • 1
jimmy_terra
  • 1,430
  • 2
  • 18
  • 36
  • 1
    The error is saying: it looked in the config file and couldn't find an endpoint named "MyServiceHttpEndpoint". You'll have to post the actual config file if you want meaningful help. – ErnieL Mar 15 '13 at 14:26
  • It also says 'This might be because no configuration file was found for your application' - which is the issue. The config file is fine, it works outside of Powershell with no problems. – MalcomTucker Mar 15 '13 at 14:43
  • I've checked the AppDomain and the config file is present and correct in the `AppDomain.ConfigurationFile` property, so it is found, I just can't tie the config file to the consuming client proxy. Added the config ... – MalcomTucker Mar 19 '13 at 09:10

5 Answers5

1

There seems to be a difference in the way that Powershell and Powershell ISE handle this.

With ISE (at least the version I'm using) you have to clear out the configuration to force it to reload. Though, I guess you could also put the contents of your .dll.config file into the powershell ISE config. However, that seems nasty. The code posted below works. I found parts of it googling this issue.

# $dllPath is the path to the dll we want to load
# first point to the correct config file
[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", "$dllPath.config")

# PowerShell ISE is a PITA we have to override the config
if ($psISE -ne $null) {
    Add-Type -AssemblyName System.Configuration
    [Configuration.ConfigurationManager].GetField("s_initState", "NonPublic, Static").SetValue($null,0)
    [Configuration.ConfigurationManager].GetField("s_configSystem", "NonPublic, Static").SetValue($null,$null)
    ([Configuration.ConfigurationManager].Assembly.GetTypes() | where {$_.FullName -eq "System.Configuration.ClientConfigPaths"})[0].GetField("s_current", "NonPublic, Static").SetValue($null, $null)
}

#Now load the DLL
$null = [Reflection.Assembly]::LoadFrom($dllPath)

# DLL and Config should be loaded - test
Steve L
  • 11
  • 1
0

Are your assemblies built as 32 bits or 64 bits target?

I met the 32/64 problem in multiple cases.

Just be careful that if you are running on 64 bits OS, you've got two PowerShells a 64 bits (usual) and a 32 bits (interresting when you need 32 external assemblies).

JPBlanc
  • 70,406
  • 17
  • 130
  • 175
0

It would be easier if you posted your complete config, but it sounds like you are missing the following section.

<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding">
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="ServiceAddress"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding"
        contract="MyService.Contracts.IMyService" name="MyServiceHttpEndpoint" />
    </client>
</system.serviceModel>

Although you sound pretty sure that is not the case.

So lets try and make sure that your powershell app is picking up the config correctly...

To be sure if the powershell app is picking up the config as expected add something like this to your powershell file:

Get-Content $LocalPath\MyService.Client.dll.config | foreach {Write-Output $_}

If it is then its not a config issue, I think we we can agree on that.

So can the dll see the settings in the config? By now we know that powershell can see the config and can call your dll.

Is the config in the same location as the dll?

Does add-type do something we dont expect? Looking at the msdn docs it looks like add-type

Adds a Microsoft .NET Framework type (a class) to a Windows PowerShell session.

If the class is now in a powershell session does it have access to the config like it normally would? I dont know.

Maybe try [Reflection.Assembly]::LoadFrom rather and add-type to see if that makes any difference?

I don't have an exact answer I'm afraid but I hope my ramblings are somewhat helpful.

David McLean
  • 1,462
  • 1
  • 12
  • 27
0

Seems like you are missing type of binding, it should be basicHttpBinding when you are in powershell

Make your code look like :

$endpointName = "MyServiceHttpEndpoint" 
$httpBinding = new-object System.ServiceModel.WSHttpBinding
$myclient = New-Object MyService.Client.MyServiceClient($httpBinding, $endpointName)
Nirav
  • 187
  • 3
  • 11
0

I would suggest skipping the config file all together if possible. If your api provides a MEX or WSDL endpoint try to create your proxies by querying them and use WsdlImporter to build up the binding configuration in memory. From that point on you can modify it in memory if necessary.

This is what I do for the project I work on that has a very heavy in terms of configuration WCF service because it is integrated with a security token service using WS-Trust.

For a similar question I've suggested try using the WcfPS module available on the gallery. Because there is an overlap with the other question I'll quote part of it.

The code for the module is open source and although its in script it depends heavily on .net framework classes and assemblies from the System.ServiceModel and System.IdentityModel assemblies. I mention this because most of the apis inside those assemblies are not available from .NET standard 2, so the module unfortunately will not work non windows operating systems. You can also read more about it in my post WCFPS - PowerShell module to work with SOAP endpoints.

This is the example from the README

#region Initialize the channel/client
$svcEndpoint="http://myserviceprovider/Service1.svc"

$wsImporter=New-WcfWsdlImporter -Endpoint $svcEndpoint -HttpGet
$proxyType=$wsImporter | New-WcfProxyType

# select the endpoint and implicitly the binding from the imported configuration
$endpoint=$wsImporter | New-WcfServiceEndpoint -Endpoint $svcEndpoint
$channel=New-WcfChannel -Endpoint $endpoint -ProxyType $proxyType
#endregion

#region Use the channel/client
$channel.Method1()
$channel.Method2()
#endregion

With this method you don't need any import of inline .net types, neither the proxies you use on Visual Studio and no configuration. Most probably you will have to adjust it to your use case. If you can improve the module's functionality then please submit a pull request.

Alex Sarafian
  • 634
  • 6
  • 17