Molto spesso capita di sentire domande del tipo: "il mio servizio impiega molto tempo ad eseguirsi e spesso la chiamata va in timeout. Come posso risolvere il probema ?" In WCF òa soluzione potrebbe essere quella di utilizzare il duplex service contract. Dico potrebbe perchè non è l'unica soluzione !

Il message pattern duplex è concettualmente semplice. Definisco un contratto per la chiamata e un'altro per la callback. Lato servizio avremmo quindi una cosa del tipo:

[ServiceContract(CallbackContract = typeof(IContractCallBack))]
public interface IContract
{
    [OperationContract(IsOneWay=true)]
    void SubmitContract(string contractNumber);
}

[ServiceContract()]
public interface IContractCallBack
{
    [OperationContract(IsOneWay = true)]
    void ContractStatus(string contractNumber, bool isSubmitted);
}

public class ContractService : IContract
{
    IContractCallBack callback;

    public ContractService()
    {
        callback = OperationContext.Current.GetCallbackChannel<IContractCallBack>();       
    }

    public void SubmitContract(string contractNumber)
    {
        System.Threading.Thread.Sleep(10000);
        callback.ContractStatus(contractNumber, true);
    }
}

Lato client basterà implementare l'interfaccia IContractCallBack:

Eseguendo un servizio Duplex in Windows XP SP2 riceverete un errore del tipo: "AddressAlreadyInUseException: HTTP could not register URL http://+:80/mioservizio/ because TCP port 80 is being used by another application."

Succede perchè WCF, nel creare il canale di ritorno (la callback), utilizza la porta 80, la quale è usata anche da IIS. Non essendo questa condivisibile è necessario definire un'indirizzo di ritorno. Ad esempio nel file di configurazione:

<configuration>
    <system.serviceModel>
        <client>
            <endpoint name="WSDualHttpBinding_IContract" address="
http://localhost:1429/IISHost/Service.svc"
                binding="wsDualHttpBinding"
                contract="ContractDuplexClient.IContract" bindingConfiguration="customBinding" />
        </client>
      <bindings>
        <wsDualHttpBinding>
          <binding name="customBinding" clientBaseAddress="
http://localhost:8000/ContractDuplexClient"/>
        </wsDualHttpBinding>
      </bindings>
    </system.serviceModel>
</configuration>

L'attributo clientBaseAddress serve proprio a dire a WCF, hei, usa l'indirizzo definito qui per rispondermi.