WCF configuration simplification with SO-Aware

As I discussed in the previous post, everything in SO-Aware is exposed as resources via OData. You can simply take a look at this by browsing at the main feed of the service repository.

This means that we can import a new service into the repository and configure it with the correct bindings and behaviors a simple set of http requests. In SO-Aware you will able to find three different kind of services, SOAP services, REST services, and OData services. And although an OData service is in essence a REST service, we made a distinction for the simple fact that an OData service exposes metadata that we can use to import the service automatically into the repository. We are doing the same thing with a SOAP service, as you don’t need to manually create the different endpoints, contracts or operations that a SOAP service exposes. All that information is automatically inferred from the service WSDL, so when you specify either the WSDL url or the WSDL content (the whole xml chunk), the repository parses the WSDL, and decomposes the WSDL into different entities that are finally associated to the service. Therefore, importing a new SOAP or OData service into the repository is quite trivial, you only need to provide the metadata url. REST services on the other hand requires some more work, as all the resources or http operations with the corresponding media types must be created manually and imported in the repository using either the SO-Aware portal or the OData api (We have plans to import this automatically from the help feed in the WCF 4.0 REST services or the WCF services that use the Help attribute in the REST Starter kit).

It’s always very useful for the enterprise to have a catalog of services and available operations into the repository for documentation purposes, and also for testing and monitoring.

The following code illustrates how a new SOAP service can be imported into the repository using the OData api (The example uses a WCF Data service generated proxy).

Importing a service from an existing WSDL

   1: var repository = new ResourceRepositoryContext(RepositoryUri);
   2:  
   3: var service = new Service
   4: {
   5:   Name = "Customers",
   6:   Namespace = "http://soaware/demo",
   7:   Style = "SOAP"
   8: };
   9:  
  10: repository.AddToServices(service);
  11:  
  12: var serviceDescription = new SoapDescription
  13: {
  14:   MetadataURI = "http://localhost:8080/?wsdl"
  15: };

Associate the WSDL to an specific service version

   1: var serviceVersion = new ServiceVersion
   2: {
   3:   MajorVersion = 1,
   4:   MinorVersion = 0,
   5: };
   6:  
   7: repository.AddToServiceVersions(serviceVersion);
   8:  
   9: repository.SetLink(serviceVersion, "Soap", serviceDescription);
  10: repository.SetLink(serviceVersion, "Service", service);

 

The code above is creating a new version (1.0) for the service, and associating the WSDL and all the inferred entities (endpoint, contracts, operations and schemas) to that version.

Associate a WCF binding to the imported endpoints

   1: var endpoint = serviceDescription.Endpoints.First();
   2: var binding = repository.Bindings.Where(b => b.Name == "kerberos").First();
   3:  
   4: repository.SetLink(endpoint, "Binding", binding);
   5:  
   6: repository.SaveChanges(SaveChangesOptions.Batch);

 

Configuring the service host

As you can see, we are getting an existing binding “kerberos” from the repository for using message security with kerberos (SO-Aware already ships with a bundle of pre-configured bindings for supporting different scenarios), and associating that binding to the imported endpoint. Therefore, next time you restart the service host for that service, it will start using the selected binding for that endpoint.

Ok, so far we have imported a service into the repository. As next step, something you can do is to configure and specific service host that we provide for automatically get the service configuration from the repository (in the example above, the new endpoint configuration with the “kerberos” binding)

In order to do that, you need to use a custom service host “Tellago.ServiceModel.Governance.ConfigurableServiceHost” or the custom service host factory “Tellago.ServiceModel.Governance.ConfigurableServiceHostFactory” in you case you are hosting WCF in IIS.

   1: ServiceHost host = new ConfigurableServiceHost(typeof(Customers),
   2: new Uri("http://localhost:8080"));
   3:  
   4: host.Open();

 

This “ConfigurableServiceHost” uses an specific configuration section to know how to map the service type “Customers” to the service version in the repository.

   1: <serviceRepository url="http://localhost/SoAware/ServiceRepository.svc">
   2:   <services>
   3:     <service name="ref:Customers(1.0)" type="CustomerService.Customers, CustomerService"/>
   4:   </services>
   5: </serviceRepository>

 

The prefix “ref:” tells the host to look for that service into the repository. This is when you want to download the complete service configuration and all the associated endpoints from the repository. However, this service host also supports another syntax in the configuration section for downloading only specific bindings or behaviors.

   1: <serviceRepository url="http://localhost/SoAware/ServiceRepository.svc">
   2:   <services>
   3:     <service name="CustomerService.Customers">
   4:       <endpoint name="Customers_WindowsAuthentication" binding="ws2007HttpBinding" bindingConfiguration="ref:kerberos"  contract="CustomerService.ICustomers"/>
   5:       <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
   6:     </service>
   7:   </services>
   8:  </services>
   9: </serviceRepository>

 

The example above only tells the configurable host to download the “kerberos” binding from the repository, and not the rest of the service configuration.

Configuring the client application

The configuration on the client side is also very trivial. We provide a class for automatically configuring a proxy, “Tellago.ServiceModel.Governance.ServiceConfiguration.ConfigurableProxyFactory”, or a class “Tellago.ServiceModel.Governance.ServiceConfiguration.ConfigurationResolver” for resolving individual WCF configuration objects like endpoints, bindings or behaviors that you want to inject in your existing proxy.

The following code illustrates how the ConfigurableProxyFactory can be used to invoke the service without having a single line of configuration.

   1: class Program
   2: {
   3:   static Uri RepositoryUri = new Uri("http://localhost/SOAware/ServiceRepository.svc");
   4:  
   5:   static void Main(string[] args)
   6:   {
   7:     var factory = new ConfigurableProxyFactory<ICustomers>(RepositoryUri, "Customers(1.0)");
   8:     var proxy = factory.CreateProxy();
   9:  
  10:     try
  11:     {
  12:       var customer = proxy.GetCustomer(1);
  13:  
  14:       Console.WriteLine("Customer received {0}", customer.FistName);
  15:     }
  16:     finally
  17:     {
  18:       factory.Dispose();
  19:     }
  20:   }
  21: }

As can see, everything was inferred from the configuration in the repository, even the endpoint address and the configured binding.

Download the sample code from this location.

No Comments