3

What I want to do is; I use the "WSDL" service link from the configuration file and use the service programmatically, taking the name of the method I will use.

the code fragment I'm using statically and running is as follows,

ServiceName.serviceClientSoapClient= new ServiceName.serviceClientSoapClient();

string xmlStr = client.getValues();

And endpoint is,

<endpoint address="http://someservice.com/Service.asmx"
        binding="basicHttpBinding" bindingConfiguration="serviceClientSoap"
        contract="ServiceName.serviceClientSoap" name="serviceClientSoap" />

But,I want all this created programmatically, For example; my config file,

 <add key="serviceLink" value="http://someservice.com/Service.asmx"/>
 <add key="serviceClientClassName" value="serviceClientSoapClient"/>
 <add key="serviceMethod" value="getValues"/>

Then i want use this config file, and use service, get results.

I looked at the following links, but here it is done through a single service structure. I want it to be installed from the config file.

How to programmatically connect a client to a WCF service? ,How to: Use the ChannelFactory

Muhammet Can TONBUL
  • 3,217
  • 3
  • 25
  • 37
  • Do you have an interface for each of those endpoints (and can you either config those as well or have a way to identify them)? Or do you always expect the body of the response to be returned as a string? – rene Dec 18 '17 at 09:00
  • It is returning the xml file. I dynamically set the next process when the xml file arrives. I have no trouble at that point. Unfortunately there is no interface for these end points. I think the real problem is here anyway. @rene – Muhammet Can TONBUL Dec 18 '17 at 09:05

2 Answers2

2

It could be better to create an interface and implement it the service clients. In this way, you should specify the methods, params and other things which are required in the config files and it is getting to difficult to manage. Also, you can't use the result objects as known type classes.

So, you can try something like that:

var url = ConfigurationManager.AppSettings["serviceLink"];
var serviceClientClassName = ConfigurationManager.AppSettings["serviceClientClassName"];
var serviceMethod = ConfigurationManager.AppSettings["serviceMethod"];
var endpoint = new EndpointAddress(new Uri(url));
//Specify the assembly of services library. I am assuming that the services are stored in the Executing Assembly
var serviceClient = Assembly.GetExecutingAssembly().GetTypes()
    .FirstOrDefault(x => x.Name == serviceClientClassName);//Find the service client type
var instance = Activator.CreateInstance(serviceClient); //Create a new instance of type
var methodInfo = serviceClient.GetMethod(serviceMethod); //Get method info
var result = methodInfo.Invoke(instance, new object[] {});  // Invoke it
rene
  • 41,474
  • 78
  • 114
  • 152
lucky
  • 12,734
  • 4
  • 24
  • 46
  • For now, I add and run the service as a reference. But if I can add the service at runtime, everything will be dynamic. But for now, your solution is working. @Rainman – Muhammet Can TONBUL Dec 18 '17 at 11:22
0

If all you need is WCF CommunicationObject to handle RequestReply endpoints, the following method will do that for you.

It takes an valid Request message, and endpoint and an soapaction and gives the raw xml that was returned by the service.

If you want to give it anything else then a Message you'll need to implement an alternative for IRequestChannel decorated with ServiceContract and OperationContract attributes.

// give it a valid request message, endpoint and soapaction
static string CallService(string xml, string endpoint, string soapaction)
{
    string result = String.Empty;

    var binding = new BasicHttpBinding();

    // create a factory for a given binding and endpoint
    using (var client = new ChannelFactory<IRequestChannel>(binding, endpoint))
    {
        var anyChannel = client.CreateChannel(); // Implements IRequestChannel

        // create a soap message
        var req = Message.CreateMessage(
            MessageVersion.Soap11,
            soapaction,
            XDocument.Parse(xml).CreateReader());

        // invoke the service
        var response = anyChannel.Request(req);

        // assume we're OK
        if (!response.IsFault)
        {
            // get the body content of the reply
            var content = response.GetReaderAtBodyContents();
            // convert to string
            var xdoc = XDocument.Load(content.ReadSubtree());
            result = xdoc.ToString();
        }
        else
        {
            //throw or handle
            throw new Exception("panic");
        }
    }
    return result;
}

To use above method you can fetch the two parameters from the config file, or use some constant values:

var result = CallService(
   @"<GetData xmlns=""http://tempuri.org/""><value>42</value></GetData>",
   ConfigurationManager.AppSettings["serviceLink"],
   ConfigurationManager.AppSettings["serviceSoapAction"]);

// example without using appSettings
var result2 = CallService(
   @"<GetValues xmlns=""http://tempuri.org/""></GetValues>",
   "http://localhost:58642/service.svc",
   "http://tempuri.org/IService/GetValues");

Notice that you won't need any other configuration in your config file beyond:

<appSettings>
    <add key="serviceLink" value="http://localhost:58642/service.svc"/>
    <add key="serviceSoapAction" value="http://tempuri.org/IService/GetData"/>
</appSettings>

Use the WSDL of the service to figure out the soapaction:

 <wsdl:binding name="BasicHttpBinding_IService" type="tns:IService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="GetData">
      <soap:operation soapAction="http://tempuri.org/IService/GetData" style="document"/>

and by following its route through its portType and message you'll find the types:

 <wsdl:types>
    <xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:import namespace="http://schemas.datacontract.org/2004/07/"/>
      <xs:element name="GetData">
        <xs:complexType>
          <xs:sequence>
            <xs:element minOccurs="0" name="value" type="xs:int"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>

from which you can construct the shape of the XML payload for GetData:

<GetData xmlns="http://tempuri.org/">
    <value>42</value>
</GetData>
rene
  • 41,474
  • 78
  • 114
  • 152