开发者

How do I set bindingNamespace when using fileless activation?

I'm attempting to eliminate tempuri.org from my WCF service, hosted in IIS using fileless activation. I've followed the instructions here, and I'm stuck when it comes to the bindingNamespace attribute in Web.config, because I'm using fileless activation.

My Web.config merely contains:

<serviceActivations>
    <add relativeAddress="Foo.svc"
         se开发者_开发知识库rvice="BigCorp.Services.Foo, BigCorp.Services"
         />
</serviceActivations>

I therefore don't have an <endpoint> node on which to set bindingNamespace.

What to do?


You can still use the <services> and hence <endpoint> nodes with WCF File-less activation. Take a look at the following example, where I even modify the default wsHttpBinding to add transport security and enable default behaviors as well; all for the file-less activation of the "Module1.DES.ExternalDataService" service.

  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding messageEncoding="Mtom">
          <security mode="Transport"/>
        </binding>
      </wsHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <services>
      <service name="Module1.DES.ExternalDataService">
        <endpoint binding="wsHttpBinding" bindingNamespace="" contract="Module1.DES.IExternalDataService"/>
      </service>
    </services>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
      <serviceActivations>
        <add relativeAddress="ExternalDataService.svc" service="Module1.DES.ExternalDataService"/>
      </serviceActivations>

    </serviceHostingEnvironment>
  </system.serviceModel>

Hope this helps.


To change the binding namespace you can use a custom factory (instead of the default one provided) where you can change all the properties of the binding:

  <serviceActivations>
    <add relativeAddress="Foo.svc"
         service="BigCorp.Services.Foo, BigCorp.Services"
         factory="BigCorp.Services.FooHostFactory, BigCorp.Services"/>
  </serviceActivations>

And the factory:

public class FooHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new FooServiceHost(serviceType, baseAddresses);
    }
}
public class FooServiceHost : ServiceHost
{
    public FooServiceHost(Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses) { }

    protected override void OnOpening()
    {
        base.OnOpening();
        foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
        {
            if (!endpoint.IsSystemEndpoint)
            {
                endpoint.Binding.Namespace = "http://services.bigcorp.com/foo";
            }
        }
    }
}


In your service code, you specify:

[ServiceContract(Namespace="http://your-url")]
public interface IMyInterface { ... }

and you can also specify it for data contracts:

[DataContract(Namespace="http://your-url/data")]
public class MyData { ... }


Besides the obvious change of service/data contract namespaces, you can also set a namespace on the Binding object itself, as well as a namespace on the service description:

Binding binding = new BasicHttpBinding();
binding.Namespace = "urn:binding_ns";
ServiceHost host = new ServiceHost(typeof(MyService), address);
var endpoint = host.AddServiceEndpoint(typeof(IMyService), binding, "");
host.Description.Namespace = "urn:desc_ns";

The latter one is what controls the targetNamespace of the WSDL document itself.


In the end, I used a custom BindingNamespaceAttribute, derived from this example.


If you are using the fileless service activation feature of WCF 4.0 via the serviceActivations config element, then you can override the AddDefaultEndpoints base method in your ServiceHost implementation.

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

namespace MyApp.WS.WCFServiceHost
{

    public class MyHostFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new EDOServiceHost(serviceType, baseAddresses);
        }
    }

    public class MyServiceHost : ServiceHost
    {
        public EDOServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses) { }


        public override System.Collections.ObjectModel.ReadOnlyCollection<ServiceEndpoint> AddDefaultEndpoints()
        {
            var endpoints = base.AddDefaultEndpoints();

            foreach (ServiceEndpoint endpoint in endpoints)
            {
                if (!endpoint.IsSystemEndpoint)
                {
                    endpoint.Binding.Namespace = NamespaceConstants.MyNamespace;
                }
            }

            return endpoints;
        }


    }
}

Or you could just use config only, the only down side to that is you violate the DRY principle slightly since you now have two points to maintain the namespace string, one in your constants and one in the config file.

In the following example im using the WCFExtrasPlus behaviour to 'flatten' the WSDL. You don't need this if your deploying to a .net 4.5 IIS7 server as you will have access to a flat WSDL anyway which is a new feature built into the 4.5 framework, I digress.

The example also assumes two service contracts and two service behaviour implementations of those contracts.

<system.serviceModel>

    <services>
      <service name ="MyApp.WS.ServiceBehaviour.Enquiries">
        <endpoint bindingNamespace="MyApp.WS" binding="basicHttpBinding" contract="MyApp.WS.ServiceContract.IEnquiries" />
      </service>

      <service name ="MyApp.WS.ServiceBehaviour.CallLogging">
        <endpoint bindingNamespace="MyApp.WS" binding="basicHttpBinding" contract="MyApp.WS.ServiceContract.ICallLogging" />
      </service>
    </services>

    <serviceHostingEnvironment>
      <serviceActivations>
        <add relativeAddress="Enquiries.svc" 
             service="MyApp.WS.ServiceBehaviour.Enquiries" 
             />
        <add relativeAddress="CallLogging.svc" 
             service="MyApp.WS.ServiceBehaviour.CallLogging" 
              />
      </serviceActivations>
    </serviceHostingEnvironment>

    <extensions>
      <behaviorExtensions> <!-- The namespace on the service behaviour, the service contract, the data contract and the binding must all be set to the same.-->
        <add name="wsdlExtensions" type="WCFExtrasPlus.Wsdl.WsdlExtensionsConfig, WCFExtrasPlus, Version=2.3.1.8201, Culture=neutral, PublicKeyToken=f8633fc5451b43fc" />
      </behaviorExtensions>
    </extensions>

    <behaviors>
      <endpointBehaviors>
        <behavior>
          <wsdlExtensions singleFile="true" />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- 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>
    </behaviors>
  </system.serviceModel>

For reference the service contracts;

[ServiceBehavior(Namespace = NamespaceConstants.MyNamespace)]
public class CallLogging : ICallLogging 
{
}

[ServiceBehavior(Namespace = NamespaceConstants.MyNamespace)]
public class Enquiries : IEnquiries
{
}

NB: A namespace does not need http:// in its name. It can be the namespace of your project if you like i.e. MyApp.MyProject.Somthing . See URN

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜