Laden...

WCF Service anfrage abbrechen

Erstellt von Mazo vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.573 Views
M
Mazo Themenstarter:in
255 Beiträge seit 2006
vor 13 Jahren
WCF Service anfrage abbrechen

Hallo,
ich hab ein Service der auf Nutzereingaben antworten liefert. Es kommt vor, dass der Nutzer schneller ist als der Service. Nun will ich den Service irgendwie mitteilen,dass er die vorherige Anfrage abbrechen soll und die neue Anfrage bearbeiten.

Gibt es da eine vorgefertigte Lösung ( in WCF integriert) oder muss man sich selbst darum kümmern?

Grüße Mazo

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

der WCF-Service muss asynchron ausgeführt werden. Siehe Synchronous and Asynchronous Operations.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
Mazo Themenstarter:in
255 Beiträge seit 2006
vor 13 Jahren

Hallo gfoidl,

der WCF Service wird asynchron ausgeführt. Er kann auch mehrere Anfragen parallel verarbeiten. Mir geht es drum, das Senden der Antwort an den Client zu verhindern, wenn dieser schon eine neue Anfrage gestellt hat.

Mazo

V
162 Beiträge seit 2010
vor 13 Jahren

HI,

also du hast ja ein Proxy:


//Öffnet eine Proxy
Proxy = Factory.CreateChannel();

Der Proxy implementiert ja den IService (dein eigner Service Contract):


private ChannelFactory<IServiceProxy> Factory;
public IServiceProxy Proxy;

Erstelle dir ein neues Interface IServiceProxy welches so aussehen kann:


public interface IServiceProxy : IService , IChannel { }

Factory.CreateChannel() instanciiert die Interfaces im Proxy : IChannel und IService


//Schliest und beendet die Verbindung auch wenn noch Antworten unerledigt sind
client.httpProxy.Close();

Für eine genaue Beschreibung von Close() einfach hier klicken.

Hinweis von gfoidl vor 13 Jahren

Bitte keine Links auf die lokale MSDN 😉
Hab den Link korrigiert.

Das Leben ist schön!

M
Mazo Themenstarter:in
255 Beiträge seit 2006
vor 13 Jahren

Hallo Viper78,

ich hab die Methode mit Close über ChannelFactory probiert, bekomme leider jedoch kein neuen Channel schnell genug auf.

Ich werde mich wohl nach einer anderen Lösung umschauen, womit das Problem umgangen werden kann.

Grüße Mazo

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

soweit ich weiß unterstützt WCF auch Sessions. Somit wäre es möglich dass der Client dem Server eine Aufgabe erteilt und dieser das Ergebnis in einem Sessionspeicher schreibt. Wenn der Client das Ergebnis haben will holt er dieses aus dem Sessionspeicher, wenn nicht dann eben nicht 😉
Wenn der Sessionspeicher auch noch ein Ablaufdatum für die Daten hat dann bläht sich der auch nicht unnötig auf.

Ich hab das aber noch nie probiert, denke aber dass sich das umsetzen lässt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

V
162 Beiträge seit 2010
vor 13 Jahren

Hm,

es kann auch helfen sich mit dem IChannel mal genauer zu befassen.

Wenn du da mal liest wirst du sehen das es mehr möglichkeiten gibt.
Bei einer anfrage (Request) z.B. IRequestChannel.Abort()
Das hab ich halt nicht getestet, sollte dir aber die möglichkeit geben Anfragen zu beenden und erneut wieder welche ab zu schicken ohne die Connection zu beenden.

Das mit der Session, da weiß ich leider nicht.

MfG
Björn

Das Leben ist schön!

M
Mazo Themenstarter:in
255 Beiträge seit 2006
vor 13 Jahren

Hallo,

das mit den Sessions nehm ich mir mal vor und werde es ausprobieren [morgen 😄].

IChannel Abort hab ich schon getestet. Es entsteht das gleiche Problem ich "brech" damit den Channel ab ("immediately from its current state into the closed state") und muss ihn neu aufmachen um ihn wiederzuverwenden.

Danke für eure Antworten,
Mazo

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

ich denke das lässt sich viel einfacher lösen als bisher angenommen. Anstatt einem Request/Response einfach OneWay-Requests an das Server-Objekt senden und das Ergebnis per expliziter Methode holen. Wird das Ergebnis nicht geholt gilt das im übertragenen Sinne ja auch als abgebrochen.
Sessions - wie ich vorher geschrieben habe - sind nicht unbedingt notwendig da implizit eine Session mit der Lebensdauer eines Channels verknüpft ist. Das gilt jedoch nicht für alle Bindungsarten wie zB die BasicHttpBindung (wegen Zustandlosigkeit).

Für dein Problem angewandt soll das heißen:
Der Benutzer stellt einen OneWay-Request und wenn der Benutzer langsamer ist wird das Ergebnis geholt - ist der Benutzer schneller eben nicht. Siehe noch Anmerkung fast ganz unten in dieser Antwort.

Nachfolgend stelle ich das beispielhaft an einem simplen Rechner dar, der Addition und Subtraktion unterstützt. Hier verwende ich aber eine explizite Session da damit die Operationen "logisch gruppiert" werden bzw. einem transaktionsähnlichen Charakter erreichen. D.h. entweder geht alles gut oder nicht 😉


using System.ServiceModel;

namespace WCF_Session_Demo.Contract
{
	[ServiceContract(
		Namespace = "http://gfoidl.WCF.Samples",
		SessionMode = SessionMode.Required)]
	public interface ICalculator
	{
		/* 
		 * By default, all operations are initiating.
		 * By default, no operations are terminating; the contract must explicitly 
		 * specify a terminating operation.
		 * 
		 * StartSession could also be merged into Clear, EndSession into 
		 * GetResult respectively.
		 */
		[OperationContract(IsOneWay = true, IsInitiating = true, IsTerminating = false)]
		void StartSession();
		//---------------------------------------------------------------------
		[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
		void EndSession();
		//---------------------------------------------------------------------
		[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
		void Clear();
		//---------------------------------------------------------------------
		[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
		void Add(int number);
		//---------------------------------------------------------------------
		[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
		void Subtract(int number);
		//---------------------------------------------------------------------
		[OperationContract(IsInitiating = false, IsTerminating = false)]
		int GetResult();
	}
}

Es werden also explizte Methoden zum Erstellen und Beenden einer Session angegeben und für das Liefern des Ergebnisses gibt es eine explizite Methode GetResult. Diese muss nicht aufgerufen werden. Zu Ende einer Session/Transaktion muss jedoch (bzw. sollte) die Session beendet werden.


using System.Diagnostics;
using System.ServiceModel;
using WCF_Session_Demo.Contract;

namespace WCF_Session_Demo.Host
{
	// PerSession is the default - here for demo explicitely set:
	[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
	public class Calculator : ICalculator
	{
		private int _sum = 0;
		private int _sessionId = 0;
		private static int _sessionIdCounter = 0;
		private static int _instanceCounter = 0;
		//---------------------------------------------------------------------
		public Calculator()
		{
			Trace.WriteLine(string.Format(
				"Instance # {0} created.",
				_instanceCounter++));
		}
		//---------------------------------------------------------------------
		#region ICalculator Members
		public void StartSession()
		{
			_sessionId = _sessionIdCounter++;
			Trace.WriteLine(string.Format(
				"Session {0} started.",
				_sessionId));
		}
		//---------------------------------------------------------------------
		public void EndSession()
		{
			Trace.WriteLine(string.Format(
				"Session {0} ended.",
				_sessionId));
		}
		//---------------------------------------------------------------------
		public void Clear()
		{
			_sum = 0;
		}
		//---------------------------------------------------------------------
		public void Add(int number)
		{
			_sum += number;
		}
		//---------------------------------------------------------------------
		public void Subtract(int number)
		{
			_sum -= number;
		}
		//---------------------------------------------------------------------
		public int GetResult()
		{
			return _sum;
		}
		#endregion
	}
}

Hier wird noch explizit angegeben dass ein Server-Objekt pro Session erzeugt wird. Nota bene: Wenn die Session einmal erstellt wurde dann ist diese mit dem zugrundeliegenden Channel fest verbunden. D.h. wenn die Session beendet wird dann kann dieser Channel nicht mehr verwendet werden - es muss ein neuer Channel und auch eine neue Session erstellt werden.

Anmerkung wie oben angekündigt 😉
Da es sich um Benutzerinteraktion handelt wäre es sowieso zu bevorzugen die WCF-Interaktion asynchron - sprich in einem separaten Thread auszuführen. Dabei können die Daten ganz normal zurück zum WCF-Client gesendet werden und nur dann an die UI geleitet falls der Benutzer nicht schneller ist.

Angehängt noch das ganze VS 2010-Projekt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
Mazo Themenstarter:in
255 Beiträge seit 2006
vor 13 Jahren

Hallo gfoidl,
danke für deinen umfangreichen Lösungsansatz. Die OneWay Version hört sich in meinen Ohren nach einer guten Idee an. Ich werde mein Service jetzt umschreiben und dann berichten, ob das Problem damit wirklich gelöst wurde.

Grüße Mazo

P.S: In deinem Beispiel Projekt hast du komische Assemblys eingebunden (nuint.framewrok, moq), der Code ist trotzdem hilfreich 😉

M
Mazo Themenstarter:in
255 Beiträge seit 2006
vor 13 Jahren

Hallo nochmal,

die Lösung funktioniert relativ gut, die meisten überflüßigen Datenanfragen kann ich so verhindern. Es kommt vor, dass die Eingabe und Abfrage gleichzeitig auftreten und somit die Abfrage trotzdem durchgeführt wird, aber das ist ehr die Ausnahme.

Ich bin sozusagen glücklich 😄

Danke nochmal für eure Hilfe
Mazo

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

In deinem Beispiel Projekt hast du komische Assemblys eingebunden (nuint.framewrok, moq)

😁 Für die Tests werden diese benötigt. NUnit als Testframework und Moq fürs Mocking - wird hier aber nicht verwendet, ist aber in meiner Projektvorlage standardmäßig dabei.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"