J'utilise un netNamedPipeBinding
pour effectuer une communication WCF inter-processus entre une application Windows et un service Windows.Une exception WCF a été reçue lors de la fermeture de la connexion avec les rappels utilisés
Maintenant, mon application fonctionne bien dans tous les autres comptes (après avoir combattu ma juste part des exceptions WCF comme tout le monde qui a travaillé avec WCF le sait ..) mais cette erreur est celle qui s'avère très résiliente.
Pour peindre une image de mon scénario: mon service Windows pourrait être mis en file d'attente pour faire un travail à un moment donné via un bouton enfoncé dans l'application Windows et il parle ensuite sur le netNamedPipeBinding
qui est une liaison qui prend en charge les rappels (deux -comparaison) si vous n'êtes pas familier et lance une requête pour effectuer ce travail, (dans ce cas une procédure de téléchargement de fichier), il lance également les rappels (événements) toutes les quelques secondes allant de la progression du fichier à la vitesse de transfert etc. à l'application Windows, il y a donc une certaine intégration client-serveur assez serrée; C'est ainsi que je reçois mes progrès de ce qui fonctionne dans mon service Windows dans mon application Windows. Maintenant, tout est super, les dieux de la WCF sont relativement heureux avec moi maintenant à part une mauvaise exception que je reçois chaque fois que je ferme l'application prématurément (ce qui est un scénario parfaitement valide). Alors qu'un transfert est en cours, et callbacks tirent assez fortement, je reçois cette erreur:
System.ServiceModel.ProtocolException:
The channel received an unexpected input message with Action
'http://tempuri.org/ITransferServiceContract/TransferSpeedChangedCallback'
while closing. You should only close your channel when you are not expecting
any more input messages.
Maintenant, je comprends cette erreur, mais malheureusement je ne peux pas garantir de fermer ma chaîne après ne recevoir aucune messsages plus entrée, comme l'utilisateur peut arrêter l'application à tout moment donc le travail se poursuivra en arrière-plan du service Windows (un peu comme le fonctionnement d'un scanner de virus). L'utilisateur doit être en mesure de démarrer et de fermer l'application de l'outil de gestion des gains autant qu'ils le souhaitent sans interférence.
Maintenant l'erreur, je reçois immédiatement après avoir effectué mon appel Unsubscribe()
qui est le deuxième appel avant de terminer l'application et ce que je crois est le moyen préféré pour déconnecter un client WCF. Tout ce désabonnement avant de fermer la connexion supprime simplement l'ID client d'un tableau stocké localement sur le service wcf de win service (car il s'agit d'une instance PARTAGÉE à la fois par le service win et l'application windows car le service win peut effectuer des tâches événements planifiés par lui-même) et après l'élimination du tableau d'identification client que j'effectue, ce que j'espère (sentir) devrait être une déconnexion propre. Le résultat de ceci, en plus de recevoir une exception, est que mon application se bloque, l'interface utilisateur est totalement bloquée, les barres de progression et tout à mi-chemin, avec tous les signes indiquant une condition de course ou un blocage de la WCF. mais je suis assez avisé maintenant et je pense que c'est une situation relativement isolée et en lisant l'exception telle qu'elle est, je ne pense pas que ce soit un problème en soi, car il mentionne plus une question de déconnexion précoce qui puis spirale tous mes fils dans le chaos, peut-être provoquer le verrouillage.
Mon approche Unsubscribe()
sur le client ressemble à ceci:
public void Unsubscribe()
{
try
{
// Close existing connections
if (channel != null &&
channel.State == CommunicationState.Opened)
{
proxy.Unsubscribe();
}
}
catch (Exception)
{
// This is where we receive the 'System.ServiceModel.ProtocolException'.
}
finally
{
Dispose();
}
}
Et ma méthode Dispose()
, qui doit effectuer la déconnexion propre:
public void Dispose()
{
// Dispose object
if (channel != null)
{
try
{
// Close existing connections
Close();
// Attempt dispose object
((IDisposable)channel).Dispose();
}
catch (CommunicationException)
{
channel.Abort();
}
catch (TimeoutException)
{
channel.Abort();
}
catch (Exception)
{
channel.Abort();
throw;
}
}
}
Et le service WCF Subscription()
contrepartie et classe attributs (pour référence) sur le service Windows serveur (rien de difficile ici et mon exception se produit côté client):
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TransferService : LoggableBase, ITransferServiceContract
{
public void Unsubscribe()
{
if (clients.ContainsKey(clientName))
{
lock (syncObj)
{
clients.Remove(clientName);
}
}
#if DEBUG
Console.WriteLine(" + {0} disconnected.", clientName);
#endif
}
...
}
Interface de:
[ServiceContract(
CallbackContract = typeof(ITransferServiceCallbackContract),
SessionMode = SessionMode.Required)]
public interface ITransferServiceContract
{
[OperationContract(IsInitiating = true)]
bool Subscribe();
[OperationContract(IsOneWay = true)]
void Unsubscribe();
...
}
Interface du contrat de rappel, il ne fait rien, juste appelle des événements très excitant par les délégués, etc. La raison pour laquelle j'ai inclus cela est de montrer vous mes attributs. Je l'ai fait alléger un ensemble de déjà en incluant les interblocages UseSynchronizationContext = false
:
[CallbackBehavior(UseSynchronizationContext = false,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TransferServiceCallback : ITransferServiceCallbackContract
{ ... }
espère vraiment que quelqu'un peut me aider! Merci beaucoup :) =
Je ne sais pas sur la question spécifique, mais pour plus d'informations le snafu threading sons * peut-être * en raison de la façon dont WCF utilise la synchronisation contexte (ce qui est le via le formulaire en WinForms, etc.). –
Merci Marc, yep qui m'a pris et j'ai atténué un ensemble de en lisant les interblocages sur cette question, l'affaire était de mettre sur le contrat de rappel 'UseSynchronizationContext = false';) Je vais ajouter à mes exemples. – GONeale
ah, à droite; bon de vous voir déjà couvert, p –