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.