0
votes

I am making my first attempt at creating a WCF service, to be hosted on a server with IIS. The service will have a dependency on a separate class to retrieve data from a SQL database, so am attempting to keep this loosely coupled through IoC using Castle Windsor.

Here are the various aspects of the WCF service (apologies in advance to any C#ers out there but I'm a VB man!):

Service Contract

<ServiceContract()> _
Public Interface IGetRiskDataService

    <OperationContract()>
    Function GetRisksByInsuredOpenBoxID(ByVal openBoxID As String) As List(Of Risk)

End Interface

Risk is my own class used to contain the data retrieved from the database.

Implementation of service contract

Public Class GetRiskDataService : Implements IGetRiskDataService

    Private _rep As IDataWarehouseRepository

    Public Sub New(ByVal rep As IDataWarehouseRepository)
        _rep = rep
    End Sub

    Public Function GetRisksByInsuredOpenBoxID(openBoxID As String) As List(Of Risk) Implements IGetRiskDataService.GetRisksByInsuredOpenBoxID
        Return _rep.GetRisksByInsuredOpenBoxID(CType(openBoxID, Integer))
    End Function
End Class

As you can see, I am injecting the class used to get the data from the SQL database into the constructor of the WCF service.

svc file

<% @ServiceHost Service="DataWarehouseWCFService.GetRiskDataService" Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration" %>

App.config file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="DataWarehouseWCFService.GetRiskDataServiceBehavior"
    name="DataWarehouseWCFService.GetRiskDataService">
        <!--<endpoint address="" binding="wsHttpBinding" contract="DataWarehouseWCFService.IGetRiskDataService">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>-->
    <!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />-->
    <endpoint address="../GetRiskDataService.svc"
       binding="webHttpBinding"
       contract="DataWarehouseWCFService.IGetRiskDataService"
       behaviorConfiguration="webBehaviour" />
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8732/Design_Time_Addresses/DataWarehouseWCFService/GetRiskDataService/" />
      </baseAddresses>
    </host>
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="DataWarehouseWCFService.GetRiskDataServiceBehavior">
      <!-- To avoid disclosing metadata information, 
      set the value below to false and remove the metadata endpoint above before deployment -->
      <serviceMetadata httpGetEnabled="False"/>
      <!-- To receive exception details in faults for debugging purposes, 
      set the value below to true.  Set to false before deployment 
      to avoid disclosing exception information -->
      <serviceDebug includeExceptionDetailInFaults="True" />
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="webBehaviour">
      <webHttp/>
    </behavior>
  </endpointBehaviors>
</behaviors>
  </system.serviceModel>

</configuration>

I think everything here is OK, except maybe the App.config file which I have largely left alone.

I then created a basic ASP.Net Web application which would reside on the IIS server to host the service. In the web application, the global.asax file looks like this:

Private _container As IWindsorContainer
Private _sqlConnection As String = "Data Source=[Server Name];Integrated Security=SSPI;Initial Catalog=[Database Name];"

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    ' Fires when the application is started

    _container = New WindsorContainer

    _container.AddFacility(Of WcfFacility).Register(Component.For(Of IDataWarehouseRepository).ImplementedBy(Of SQLDataWarehouseRepository).DependsOn(Dependency.OnValue("connect", _sqlConnection)), _
                                                    Component.For(Of IGetRiskDataService).ImplementedBy(Of GetRiskDataService))

End Sub

So here I register the service with Castle Windsor, including the dependency on the SQL data retrieval class. Hopefully so far, so good...

I have deployed and installed this hosting application on the IIS Server with no problems.

So all that remains is to consume the service in a client application, which is where I come unstuck. I have an MVC4 application where I wish to use the web service to display data in a View. I already use Castle Windsor in this application to register other dependencies, so all the Castle Windsor infrastructure required already exists. Therefore all I thought I needed was an Installer for the web service. My attempt at this is below:

Public Class DataWarehouseWebServicesInstaller : Implements IWindsorInstaller

Private Const _getRiskDataServiceEndpoint As String = "http://[Server Name].[Domain Name]/Data Warehouse Web Services/GetRiskDataService.svc"

Public Sub Install(container As Castle.Windsor.IWindsorContainer, store As Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore) Implements Castle.MicroKernel.Registration.IWindsorInstaller.Install
    'Data Warehouse Web Services
    container.Register(Component.For(Of IEndpointBehavior).ImplementedBy(Of WebHttpBehavior))
    container.AddFacility(Of WcfFacility).Register( _
                                            Component.For(Of IGetRiskDataService).AsWcfClient( _
                                                New DefaultClientModel( _
                                                    WcfEndpoint.BoundTo(New WebHttpBinding).At(_getRiskDataServiceEndpoint) _
                                                    ) _
                                                ) _
                                            )
End Sub
End Class

Finally I added some test code to a Controller to see if could get the WCF service to work:

    Public Sub New(ByVal ash As IGetRiskDataService)
        Dim lozza = ash.GetRisksByInsuredOpenBoxID(100005859)
        For Each lianne As Risk In lozza
            Dim neil As Date = lianne.ExpiryDate.CalendarDate
        Next
    End Sub

On testing this I get the following error:

{"The remote server returned an error: (415) Cannot process the message because the content type 'application/xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'.."}. The client and service bindings may be mismatched.

This is where I am lost. I have researched issues of client and service bindings being mismatched, but unfortunately almost every answer refers to these bindings being specified in the app.config file, which I do not do as I am relying on Castle Windsor to handle most of this, and any configurations I do make I want to do in code. So for example, in my Installer code I register an endpoint with WebHttpBinding.

I feel I am close with this but have hit the proverbial brick wall. Any help would be gladly appreciated. Please let me know if there is any other information I can provide you with.

Many thanks,

Ash

1

1 Answers

0
votes

That error is a fairly generic error when there is something mis-configured in your service. The service is not running at all properly and returning a standard wcf html error page - the client is expecting a soap response but all its getting is the error html.

To work out whats actually wrong you should navigate to your service in your browser

http://localhost:8732/Design_Time_Addresses/DataWarehouseWCFService/GetRiskDataService/GetRiskDataService.svc

the error message displayed here will be much more descriptive.

Just looking over what you have posted so far, it could be related to...

  • Is your Risk class marked up as a DataContract with the DataMember attributes?
  • You probably dont need the address="../GetRiskDataService.svc" part on your endpoint address