Stiamo utilizzando WCF per creare un semplice servizio Web che il nostro prodotto utilizza per caricare file di grandi dimensioni tramite un collegamento WAN. Dovrebbe essere un semplice HTTP PUT, e sta funzionando bene per la maggior parte.Perché WCF legge lo stream di input su EOF su Close()?
Ecco una versione semplificata del contratto di servizio:
[ServiceContract, XmlSerializerFormat]
public interface IReplicationWebService
{
[OperationContract]
[WebInvoke(Method = "PUT", UriTemplate = "agents/{sourceName}/epoch/{guid}/{number}/{type}")]
ReplayResult PutEpochFile(string sourceName, string guid, string number, string type, Stream stream);
}
Per l'attuazione di questo contratto, si legge i dati da stream
e scrivere in un file. Funziona alla grande, quindi abbiamo aggiunto una gestione degli errori per i casi in cui non c'è abbastanza spazio su disco per archiviare il file. Ecco più o meno quello che sembra:
public ReplayResult PutEpochFile(string sourceName, string guid, string number, string type, Stream inStream)
{
//Stuff snipped
try
{
//Read from the stream and write to the file
}
catch (IOException ioe)
{
//IOException may mean no disk space
try
{
inStream.Close();
}
// if instream caused the IOException, close may throw
catch
{
}
_logger.Debug(ioe.ToString());
throw new FaultException<IOException>(ioe, new FaultReason(ioe.Message), new FaultCode("IO"));
}
}
Per provare questo, sono l'invio di un file di 100 GB a un server che non dispone di spazio sufficiente per il file. Come previsto, si genera un'eccezione, ma la chiamata a inStream.Close()
sembrava bloccarsi. Ho verificato e la chiamata a Close()
si è fatta strada attraverso l'impianto idraulico WCF fino a raggiungere lo System.ServiceModel.Channels.DrainOnCloseStream.Close()
, che in base a Reflector alloca un buffer Byte[]
e continua a leggere dallo stream fino a quando non è su EOF.
In altre parole, la chiamata Close
sta leggendo l'intero 100 GB di dati di test dallo stream prima di tornare!
Ora è possibile che non sia necessario chiamare Close()
su questo stream. Se è così, mi piacerebbe una spiegazione sul perché. Ma, cosa ancora più importante, sarei grato se qualcuno potesse spiegarmi perché Close()
si comporta in questo modo, perché non è considerato un bug e come riconfigurare WCF in modo che non accada.
Grazie per la risposta. Il problema con 'ServiceHost.Abort()' è che interrompe l'intero servizio, non solo la particolare chiamata in elaborazione. Se potevo chiedere a WCF di chiudere forzatamente il socket in risposta all'eccezione, almeno in quel modo non avrei dovuto aspettare il completamento del trasferimento prima che il client sapesse che qualcosa non andava. – anelson
Non riuscivo a trovare un modo per riportare un'eccezione al client HTTP senza attendere che il client finisse di inviare il file, quindi ho dovuto accettare un'interfaccia che METTA un pezzo alla volta. Speravo di evitarlo, ma non vedo un modo per aggirarlo dato l'implementazione HTTP con cui devo lavorare – anelson