I namespace sono fondamentali perché consentono di discriminare due entità che hanno lo stesso nome.
Nella vita reale in nostro namespace è il cognome (questo perché al Concilio di Trento quando sono è stato formalizzato l'uso dei cognomi, il guid non c'era ancora ).
Nelle classi del Framework il namespace permette di disambiguare un Control tra System.Windows.Forms.Control, System.Windows.Controls e System.Web.UI.
In WCF (ma non solo) il namespace è un Uri ma non necessariamente indica una risorsa esistente (vale a dire che il namespace http://schemas.iamraf.net/Calculator non deve essere una risorsa esistente in rete).
Alla creazione di un progetto WCF, Visual Studio non assegna alcun namespace e di conseguenza le librerie di WCF assegnano automaticamente il valore di http://tempuri.org. Mettere in produzione un servizio con questo namespace implica una alta percentuale di collisione, specie se viene pubblicato su internet. Cambiarlo in un secondo tempo può diventare un incubo perché tutti i proxy devono essere rigenerati. Appare chiaro quindi che l'assegnazione di un namespace sensato è molto importante.
Il modo più semplice per vedere se un servizio si è liberato di tempuri.org, è quello di scaricare i file dei metadati dal servizio utilizzando svcutil utilizzando la command prompt di Visual Studio (o del Framework.NET sdk) in modo che le variabili di environment conoscano la path del tool svcutil:
X:\Temp>svcutil /t:metadata http://localhost:34422/LabelsService.svc?wsdl
Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 4.0.30319.1]
Copyright (c) Microsoft Corporation. All rights reserved.
Attempting to download metadata from 'http://localhost:34422/LabelsService.svc?wsdl' using WS-Metadata Exchange or DISCO.
Saving downloaded metadata files...
X:\Temp\schemas.vevy.com.Printing.wsdl
X:\Temp\tempuri.org.wsdl
X:\Temp\schemas.microsoft.com.2003.10.Serialization.xsd
X:\Temp\schemas.vevy.com.Printing.xsd
X:\Temp\Vevy.PrintEngineGDI.xsd
X:\Temp\schemas.microsoft.com.2003.10.Serialization.Arrays.xsd
X:\Temp\Vevy.Printing.xsd
X:\Temp\System.xsd
Se nella cartella viene generato almeno un file di nome tempuri.org.* significa che abbiamo da qualche parte il namespace di default ci sta affliggendo.
Ma allora come si rimuove tempuri.org?
Per eliminarlo basta assegnare un nostro namespace, e in WCF ci sono tre distinte assegnazioni di namespace.
1. Sul ServiceContract, tipicamente l'interfaccia del servizio:
[ServiceContract(Namespace = "http://schemas.vevy.com/Printing")]
public interface ILabelsService
{...}
2. Sull'implementazione del servizio, applicando un behavior di tipo ServiceBehavior:
[ServiceBehavior(Namespace = "http://schemas.vevy.com/Printing")]
public class LabelsService : ILabelsService
{ ... }
3. Sull'Endpoint che deve assegnare il namespace del Binding
<endpoint address="net.pipe://localhost/LabelsService"
binding="netNamedPipeBinding" bindingConfiguration="Binding1"
name="PipeEndpoint" bindingNamespace="http://schemas.vevy.com/Printing"
contract="..." />
Personalmente ritengo che questo diverso trattamento del namespace del binding suoni un po' come un'inconsistenza.
Fino alla versione 3.5, l'assegnazione di bindingNamespace non era un problema ma adesso, nella versione 4, WCF permette la configurazione semplificata e non è più necessario esplicitare l'endpoint o addirittura si può omettere completamente la configurazione, lasciando i default.
La configurazione semplificata di WCF 4 non si limita a impostare dei valori di default, ma permette di stabilire quali sono i default per behavior, i binding e i protocolli di tutti i servizi per quell'host. Il problema è già stato segnalato.
E qui le cose si complicano ancora di più. Quando il servizio viene messo in host su IIS molte delle impostazioni del servizio vengono totalmente ignorate e tra queste bindingNamespace.
Sembrava un ginepraio da cui non poter venire fuori, sembrava che ci saremmo dovuti accontentare di avere un namespace impostato a tempuri.org.
Ho trovato una soluzione banale di cui non ho trovato traccia in giro, tanto che puzza di bruciato, ma funziona.
Perciò ecco di nuovo il punto 3.
3. (revisited) Scrivere un Service Behavior che sostituisce il namespace al volo:
public class BindingNamespaceBehavior : Attribute, IServiceBehavior
{
public string bindingNamespace { get; set; }
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (var endpoint in serviceHostBase.Description.Endpoints)
{
endpoint.Binding.Namespace = bindingNamespace;
}
}
}
Fatto questo, è sufficiente applicare l'attributo sull'implementazione del servizio (senza toccare la configurazione in alcun modo) che diventa perciò:
[ServiceBehavior(Namespace = "http://schemas.vevy.com/Printing")]
[BindingNamespaceBehavior(bindingNamespace = "http://schemas.vevy.com/Printing")]
public class LabelsService : ILabelsService
{ ... }
Non era semplice? Forse troppo?
A questo punto basta ri-scaricare i metadati con svcutil per verificare che tempuri.org sia definitivamente sparito.
Happy namespace everybody!