Consume a SOAP web service without relying on the app.config
Asked Answered
P

3

39

I'm building a .NET component that will call an external web service. I used the "Add Service Reference" dialog to add the web service to my component, which generates the code needed to consume the service and adds the settings to the app.config file.

I'm testing the component by adding a reference to its DLL from a Console application and calling the appropriate method that creates a new instance of the web service: ... = new MyServiceSoapClient(). However, when I do this, I get the following exception:

InvalidOperationException

Could not find default endpoint element that references contract 'MyServicesSoap' 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 contract could be found in the client element.

This makes sense since the app.config isn't being brought over with the component's DLL. How can I call the web service without having to rely on the settings in the App.Config?

Pinch answered 13/9, 2010 at 19:45 Comment(3)
possible duplicate of InvalidOperationException while creating wcf web service instancePerfect
In a nutshell: Those config settings from the component need to be copied over to the ConsoleApplication1.exe.configPerfect
@Josh that won't work in my case. I didn't mention this in my question, but this .NET DLL is going to be called from a VB6 COM application, so the .NET component that I'm writing has to be able to successfully call the web service without relying on anything from the consuming application.Pinch
P
82

The settings in <system.ServiceModel> in the app.config file will tell the component how to connect to the external web service. The xml is simply a textual representation of the necessary classes and enumerations required to make the default connection to the web service.

For example, this is the code that was generated for the web service that I added:

<system.serviceModel>
 <bindings>
  <basicHttpBinding>
   <binding name="MyServicesSoap" closeTimeout="00:01:00" openTimeout="00:01:00"
     receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
     bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
     maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
     messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
     useDefaultWebProxy="true">
     <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
       maxBytesPerRead="4096" maxNameTableCharCount="16384" />
     <security mode="None">
      <transport clientCredentialType="None" proxyCredentialType="None"
        realm="" />
      <message clientCredentialType="UserName" algorithmSuite="Default" />
     </security>
    </binding>
   </basicHttpBinding>
  </bindings>
 <client>
  <endpoint address="http://services.mycompany.com/WebServices/MyServices.asmx"
    binding="basicHttpBinding" bindingConfiguration="MyServicesSoap"
    contract="MyServices.MyServicesSoap" name="MyServicesSoap" />
 </client>
</system.serviceModel>

This can be translated to code like so:

    'Set up the binding element to match the app.config settings '
    Dim binding = New BasicHttpBinding()
    binding.Name = "MyServicesSoap"
    binding.CloseTimeout = TimeSpan.FromMinutes(1)
    binding.OpenTimeout = TimeSpan.FromMinutes(1)
    binding.ReceiveTimeout = TimeSpan.FromMinutes(10)
    binding.SendTimeout = TimeSpan.FromMinutes(1)
    binding.AllowCookies = False
    binding.BypassProxyOnLocal = False
    binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard
    binding.MaxBufferSize = 65536
    binding.MaxBufferPoolSize = 524288
    binding.MessageEncoding = WSMessageEncoding.Text
    binding.TextEncoding = System.Text.Encoding.UTF8
    binding.TransferMode = TransferMode.Buffered
    binding.UseDefaultWebProxy = True

    binding.ReaderQuotas.MaxDepth = 32
    binding.ReaderQuotas.MaxStringContentLength = 8192
    binding.ReaderQuotas.MaxArrayLength = 16384
    binding.ReaderQuotas.MaxBytesPerRead = 4096
    binding.ReaderQuotas.MaxNameTableCharCount = 16384

    binding.Security.Mode = BasicHttpSecurityMode.None
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None
    binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None
    binding.Security.Transport.Realm = ""
    binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName
    binding.Security.Message.AlgorithmSuite = Security.SecurityAlgorithmSuite.Default

    'Define the endpoint address'
    Dim endpointStr = "http://services.mycompany.com/WebServices/MyServices.asmx"
    Dim endpoint = New EndpointAddress(endpointStr)
    'Instantiate the SOAP client using the binding and endpoint'
    'that were defined above'
    Dim client = New MyServicesSoapClient(binding, endpoint)

Usually, when you use the parameterless constructor (i.e. new MyServicesSoapClient()), the settings in the app.config file will be used. However, you can bypass the app.config file by explicitly setting the binding and endpoint values in code and passing those instances into the constructor.

Pinch answered 13/9, 2010 at 20:50 Comment(2)
+1 Great, Thanks for this. Although I did find that on the binding I only needed to add a miniumum amount of settings. I used only the timeouts on the bindings and it went through okay.Fessler
I was having issues with a VB6 application that referenced a .NET dll which called a web service. The trouble stemmed from the dll's app.config file, as reading config files from dll's is not entirely straightforward, and this allowed me to bypass the config file entirely.Engdahl
A
0

Setting the Binding and Endpoint configuration in code is one way but there is another way to use the consumer DLL and let the configuration stay in the existing App.config file.

The reason why the mentioned InvalidOperationException occurs is because the DLL does not contain the configuration settings in it. It always rely on App.config to provide for it but since you are using the DLL in another Console application it does not find the configuration settings.

When we use the "Add Service Reference" dialog to add the web service to client component and create instance of the Web service, we let Visual Studio handle the creation of the Communication channel and load the configuration setting.So, if we are able to create such channel explicitly our self then we can manage the configuration settings.

Microsoft provides Classes for this purpose, ConfigurationChannelFactory<TChannel> Class is one. MSDN states:

Provides the generic functionality to create a channel configuration element for a specific type.

The ConfigurationChannelFactory allows central management of WCF client configuration.

Use "Add Service Reference" dialog to add the web service to client component as we need the Service Channel Interface instance.

First rename the generated App.config file to App.dll.config and in its File properties change the Copy to Output Directory property to Copy Always

Create a class that has a method which returns the Channel object to access the web Service such as this:

public class ManageService
{
    public static T CreateServiceClient<T>(string configName)
    {
        string _assemblyLocation = Assembly.GetExecutingAssembly().Location;
        var PluginConfig = ConfigurationManager.OpenExeConfiguration(_assemblyLocation);
        ConfigurationChannelFactory<T> channelFactory = new ConfigurationChannelFactory<T>(configName, PluginConfig, null);
        var client = channelFactory.CreateChannel();
        return client;
    }
}

Since we have set the property Copy Always VS copies the Project DLL as well as the App.dll.config into the bin folder. Assembly.GetExecutingAssembly().Location return the assembly location and ConfigurationManager.OpenExeConfiguration

Opens the specified client configuration file as a Configuration object.

PluginConfig holds the App.Config configuration file Object and ConfigurationChannelFactory<T> uses it to communicate with the service.

This method can be called by passing your Service Channel Interface Object like this:

Client = ManageService.CreateServiceClient<SampleService.IKeyServiceChannel>("MetadataExchangeTcpBinding_IKeyService"); 

SampleService is the namespace of my web service. Client holds the instance of the web Service.

If you need to handle Duplex Communication and Callbacks, then you can look at ConfigurationDuplexChannelFactory<TChannel> Class.

Aaronaaronic answered 15/2, 2018 at 8:30 Comment(0)
W
-1

If this is a WCF service (which it sounds like, from the error messages), then, for the most part, you are going to need something is the app.config, because it's the app.config which tells the rest of WCF that MyServiceSoapClient is a web service (with a small change to the two app.config files, this could become a named pipe service, without recompiling the code....)

Now, if you really want to do this without the app.config, then you have to toss the generated MyServiceSoapClient(), and write your own, based on HttpWebRequest.

Wafture answered 13/9, 2010 at 19:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.