tags:

views:

2012

answers:

2

I am working on a WCF application that will be deployed to various servers along the way and I would like to not have to remember to change the app.config every time I do a deployment. At first, my app.config serviceModel section looked like this:

<system.serviceModel>  
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />  
<behaviors>  
    <serviceBehaviors>  
        <behavior name="MyDefaultServiceBehavior">   
            <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8888/MyService" />  
            <serviceDebug includeExceptionDetailInFaults="true" />  
        </behavior>  
    </serviceBehaviors>  
</behaviors>  
<services>  
    <service behaviorConfiguration="MyDefaultServiceBehavior" name="MyService">   
        <endpoint address="net.tcp://localhost:9001/MyService" binding="netTcpBinding" contract="IMyService" name="NetTcpBinding_IMyService" />  
    </service>  
</services>

This works fine in development when I was accessing the service running on my local machine. When I deployed it, the WSDL contained absolute paths that still pointed to localhost:

<xsd:import schemaLocation=http://localhost:8888/MyService?xsd=xsd0 namespace="http://tempuri.org/" />

So, I can change the httpGetUrl in the app.config like so:

<serviceMetadata httpGetEnabled="true" httpGetUrl=http://devserver1:8888/MyService />

And now the wsdl works correctly on that server. The problem is that I have to manually set the address in each app.config that gets deployed.

Is there a way to either:
1. Have the wsdl already include everything so that there are no imports?
or
2. Use relative paths in the wsdl import statements?

Or any other suggestions would be appreciated. I have two development servers that the deployment is automated to, if only it weren't for this wsdl problem.

Since this is only for generating the proxy, I suppose I could generate the proxy and distribute it myself, but I'd rather let users generate the proxy themselves.

Thanks! Daniel

+3  A: 

You could set the value of httpGetUrl programatically and set it to an absolute address that includes the machine name of the server of that the services are being hosted on. The import statements in the generated WSDL will then also use the machine name of the server.

If your WCF host is being created for you (for example, you are hosting under IIS) then you will need to create a custom ServiceHostFactory to get access to the ServiceHost. For example:

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;

namespace WebApplication
{
  public class TestServiceHostFactory : ServiceHostFactory
  {
     protected override ServiceHost CreateServiceHost(Type serviceType, 
                                                      Uri[] baseAddresses)
      {
        ServiceHost host = base.CreateServiceHost(serviceType, 
                                                  baseAddresses);
        ServiceMetadataBehavior metadataBehavior = 
                                new ServiceMetadataBehavior();
        metadataBehavior.HttpGetEnabled = true;
        metadataBehavior.HttpGetUrl = new Uri(string.Format(
                              "http://{0}/WebApplication/TestService.svc", 
                              Environment.MachineName));
        host.Description.Behaviors.Add(metadataBehavior);
        return host;
      }
  }
}

You then specify this factory in the .svc file of the service:

<%@ ServiceHost Language="C#" 
                Service="WebApplication.TestService" 
                CodeBehind="TestService.svc.cs" 
                Factory="WebApplication.TestServiceHostFactory" %>

If you are creating the WCF host yourself then your code would look something like this:

ServiceHost host = new ServiceHost(typeof(WebApplication.TestService));
ServiceMetadataBehavior metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.HttpGetUrl = new Uri(string.Format(
                              "http://{0}/WebApplication/TestService.svc", 
                              Environment.MachineName));
host.Description.Behaviors.Add(metadataBehavior);
Daniel Richardson
thats pretty much how i do it.
Sailing Judo
The bigger problem for me is that not everyone can access the server using the machine name. For some people, it doesn't even resolve. Your solution is a good answer to my question, but unfortunately, I simplified the question too much and it doesn't solve my problem. See other comment for solution.
Daniel
+2  A: 

The answer left above by Daniel Richardson is a good one, and I think that for most people, that would be the preferred solution. However, because of our network layout and the few people that will need to access our server, I am doing something a little different.

I have changed my app.config to have an httpGetUrl that contains "myServiceServer":

<serviceMetadata httpGetEnabled="true" httpGetUrl=http://myServiceServer:8888/MyService />

To use my service, someone has to first add in a host file entry that maps "myServiceServer" to the correct IP address. This works well for our problem because the IP address can't be resolved from any common machine name or IP address. This is because of separated networks that are connected only by VPNs with some kind of NAT going on.

Daniel