0

I have a WCF service with 2 endpoints (1 webHttpBinding and 1 basicHttpBinding) that is hosted in IIS. Clients are able to make http requests without a problem. I was able to connect and make calls to the SOAP endpoint via the WCFTestClient, and was able to successfully add the service reference to my client project. The problem is that there is no code generated in the Reference.cs file or the app.config file on the client...they're both empty.

My question is, why would I be able to connect and make calls to the SOAP endpoint without a problem through the WCFTestClient but not through the Add Service Reference method?

EDIT: It seems to be an issue with the references I have on the assembly I'm adding the service reference to. I'm referencing another class library (the same library exists on the service) and am getting this warning when I build my solution:

Warning 12  Custom tool warning: Cannot import wsdl:portType
Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter
Error: Referenced type 'MyNamespace.KPI.ChartObject`2, MyNamespace.KPI.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3e720b7e8a51f8d5' with data contract name 'ChartObjectOfdecimalZKPIPeriodaU4eboDJ' in namespace 'http://schemas.datacontract.org/2004/07/MyNamespace.KPI' cannot be used since it does not match imported DataContract. Need to exclude this type from referenced types.

It should be noted that this specific class is a generic class...I don't know if this has anything to do with the error.

Thelonias
  • 2,918
  • 3
  • 29
  • 63

2 Answers2

1

You need to follow your ABC's:

  • Address
  • Binding
  • Contract

An address that is exposed through HTTP just needs a deployment URL. Which the Internet Information System is handling all of that for you. However; if you attempt to use the Service Reference without the proper definitions- it will not show. As it has no details of where it is going.

Once you've defined that criteria it should appear per normal.


Update:

You can culminate SOAP and REST off the same Service Contract or you can separate them. In this example; I'll separate them.

Create the Service:

[ServiceContract]
public interface IMath
{
      [OperationContract]
      int Add(int Number1, int Number2);
}

Create the Service Contract for Rest:

[ServiceContract]
public interface IMathRest
{

     [OperationContract]
     [WebGet(UriTemplate = "/Add/{Number1}/{Number2}", RequestFormat = WebMessageFormat.Json,
              ResponseFormat = WebMessageFormat.Json)]
     int AddRest(string Number1, string Number2);
}

In the above service; it is explicitly setting the message format.

Implement the Service: "Binding"

public class Math : IMath, IMathRest
{
     public int Add(int Number1, int Number2)
     {
         return Number1 + Number2;
     }

     public int AddRest(string Number1, string Number2)
     {
         int num1 = Convert.ToInt32(Number1);
         int num2 = Convert.ToInt32(Number2);
         return num1 + num2;
     }
}

Configure the Service:

<serviceBehaviors>
      <behavior name = "servicebehavior">
          <serviceMetadata httpGetEnabled = "true" />
          <serviceDebug includeExceptionDetailInFaults = "false" />
      </behavior>
</serviceBehaviors>

The above will set the service to Basic / Web Http.

After you've configured your serviceBehavior you'll need to define the endpoint:

<endpointBehaviors>
     <behavior name="restBehavior">
          <webHttp/>
     </behavior>
</endpointBehaviors>

This will configure your Rest Endpoint to the webHttpBinding specified above. Now you need to define your Soap.

<endpoint name = "SoapEndPoint"
       contract = "Namespace in which Service Resides goes here"
       binding = "basicHttpBinding" <!-- Mirrors are above configuration -->
       address = "soap" />

The above will go into your configuration; however at the time of consumption of the service- the endpoint name is required. The endpoints will be available at the Base Address / Soap Address. The binding used is specified.

Except we have only configured our Soap, not our Rest. So we need to specify our endpoint:

<endpoint name = "RestEndPoint"
       contract = "Namespace that our Rest Interface is located goes here"
       binding = "webHttpBinding"
       address = "rest"
       behaviorCOnfiguration = "restBehavior" />

Our Rest Endpoint will be called at our Url (Base Address / Rest/ Add / Parameter / Parameter). We've specified our binds; and set our Rest Behavior.

When you put the whole thing together; it would look like:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name ="servicebehavior">        
          <serviceMetadata httpGetEnabled="true"/>        
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="restbehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name ="MultipleBindingWCF.Service1"
               behaviorConfiguration ="servicebehavior" >
        <endpoint name ="SOAPEndPoint"
                  contract ="MultipleBindingWCF.IService1"
                  binding ="basicHttpBinding"
                  address ="soap" />

        <endpoint name ="RESTEndPoint"
                  contract ="MultipleBindingWCF.IService2"
                  binding ="webHttpBinding"
                  address ="rest"
                  behaviorConfiguration ="restbehavior"/>

        <endpoint contract="IMetadataExchange"
                  binding="mexHttpBinding"
                  address="mex" />
      </service>
    </services>   
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer> 
</configuration>

Consuming:

To consume our Soap it is simple; straight forward. Make the Service Reference and make the call.

static void CallingSoapFunction()
{
     SoapClient proxy = new SoapClient("SoapEndPoint");
     var result = proxy.Add(7,2); // Proxy opens the channel, we invoke our method, we input our parameters.
     Console.WriteLine(result);
}

Except our Restful consumption is slightly different; especially since we have to Format it to Json. So we need to specify the name exactly.

Rest is going to rely on Json for De-serialization of the data.

static void CallRestFunc()
{
     WebClient RestProxy = new WebClient();
     byte[] data = RestProxy.DownloadData(new Uri("http://localhost:30576/MathRest.svc/Rest/Add/7/2")); // As you see it is following the exact location of the project, invoking method / parameter and so on down the line.
Stream stream = new MemoryStream(data);
DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(string));
string result = obj.ReadObject(stream).ToString();
Console.WriteLine(result);
}

The above will download the data using the Rest Uri. Deserialize that data; and become displayed.

Hopefully that helps clarify.

If the proper items aren't created in your configuration file; then it will not allow the proper consumption. The ABC's are critical in WCF. If it isn't created in the configuration file you'll need to programatically create them in the code.


Update:

static void Main(string[] args)
{
    var binding = new BasicHttpBinding();
    var endpoint = new EndpointAddress("http://localhost:8080/");
    using (var factory = new ChannelFactory<IPerson>(binding, endpoint))
    {
        var request = new Dictionary<Guid, Person>();
        request[Guid.NewGuid()] = new Person { Name = "Bob", Email = "Bob@abc.com" };

        var client = factory.CreateChannel();
        var result = client.SetCustomer(request);

        Console.WriteLine("Name: {0} | Email: {1}", result.Name, result.Email);
        factory.Close();
    }
    Console.ReadKey(true);
}

As you see in this basic example; the binding and endpoint are both configured. You need to ensure that all of these are defined in both your server and client. It has to know where it is going. Does that make more sense?

Greg
  • 11,302
  • 2
  • 48
  • 79
  • I'm not sure I follow...I used [this](http://stackoverflow.com/questions/186631/rest-soap-endpoints-for-a-wcf-service) as a guide for implementing my service. What would allow me to use the WCFTestClient but *not* a service reference in my own application? – Thelonias Jan 04 '13 at 00:15
  • It will hopefully clarify; especially as abstraction occurs. http://www.codeproject.com/Articles/406096/A-beginners-tutorial-for-understanding-Windows – Greg Jan 04 '13 at 00:21
  • That article is still following the ABC's. – Greg Jan 04 '13 at 00:22
  • I've made plenty of WCF services and added plenty of service references without problem. I've just never combined a rest endpoint with a soap endpoint...which is what I'm doing here. – Thelonias Jan 04 '13 at 00:24
  • Then your question isn't very clear or concise; I've altered my question to reflect your response. – Greg Jan 04 '13 at 00:26
  • thanks for taking your time to explain, but it wasn't necessary. I'm past all of that. The link I included in my first comment to your answer does everything you just typed out. Any way, as I added in my edit to my question, the problem has something to do with an assembly I'm referencing in the project I'm adding the service reference to. If I remove the reference and add the service reference, everything works...except WCF generates the referenced classes for me (which I don't want, since I control both the client and server) – Thelonias Jan 04 '13 at 01:00
  • Perhaps my answer isn't clear Ryan; if you don't want to use the reference technique. You have to reference the Assembly; however you have to manually reference the given namespace following your ABC's. Everything that is auto generated; must be manually entered either within the configuration file or manually within your code. Example added. – Greg Jan 04 '13 at 17:10
  • I understand what you're saying...it's always made sense. I had already worked around my problem by implementing something similar to your latest update (I define my endpoints and binding info in the app.config file rather than in code). It still doesn't explain why I'm getting a blank Reference.cs when I try the "Add Service Reference" route. – Thelonias Jan 04 '13 at 19:08
  • I've noticed when you reference Assemblies; it doesn't always detect it. As it is expecting the host to invoke and handle all proxy request. At least you found a solution though. – Greg Jan 04 '13 at 19:27
0

Check out this url. Hope this help you.

Mike
  • 106
  • 1
  • 5