简体   繁体   中英

Asp.net Self Hosted WCF Service WSDL with relative paths

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

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);

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.

There is apparently a very-well-hidden option built into WCF that supports using the same hostname as the incoming request, which will usually be correct. (I assume the only reason it's not the default is for backwards compatibility, although it probably would have been better if they had made it the default anyway.)

It took much searching and pulling of hair before I found this particular gem (although now that I know the magic word, I found another answer that came from a similar journey to mine).

To enable it, add the following code in the same place where you're adding the ServiceMetadataBehavior :

host.Description.Behaviors.Remove<UseRequestHeadersForMetadataAddressBehavior>();
host.Description.Behaviors.Add(new UseRequestHeadersForMetadataAddressBehavior());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM