Przeglądaj źródła

Open and close client/service runtime channels in chains. Fixed bug #663278.

Atsushi Eno 15 lat temu
rodzic
commit
cae51172f8

+ 1 - 0
mcs/class/System.ServiceModel/System.ServiceModel.Dispatcher/InputOrReplyRequestProcessor.cs

@@ -60,6 +60,7 @@ namespace System.ServiceModel.Dispatcher
 			}
 			else
 				contextChannel = new ServiceRuntimeChannel (reply_or_input, dispatch_runtime);
+			contextChannel.Open (); // FIXME: timeout?
 			OperationContext opCtx = new OperationContext (contextChannel);
 			opCtx.IncomingMessage = incoming;
 			opCtx.EndpointDispatcher = dispatch_runtime.EndpointDispatcher;

+ 4 - 0
mcs/class/System.ServiceModel/System.ServiceModel.Dispatcher/SessionInstanceContextProvider.cs

@@ -54,6 +54,10 @@ namespace System.ServiceModel.Dispatcher
 		{
 			var key = channel.SessionId ?? String.Empty;
 			pool [key] = instanceContext;
+			channel.Closed += delegate {
+				pool.Remove (key);
+				instanceContext.Close (); // FIXME: timeout?
+			};
 		}
 
 		public bool IsIdle (InstanceContext instanceContext)

+ 5 - 2
mcs/class/System.ServiceModel/System.ServiceModel/ServiceRuntimeChannel.cs

@@ -118,6 +118,7 @@ namespace System.ServiceModel.MonoInternal
 		public ServiceRuntimeChannel (IChannel channel, DispatchRuntime runtime)
 		{
 			this.channel = channel;
+			channel.Closing += delegate { Close (); };
 			this.runtime = runtime;
 		}
 
@@ -197,7 +198,8 @@ namespace System.ServiceModel.MonoInternal
 
 		protected override void OnClose (TimeSpan timeout)
 		{
-			channel.Close (timeout);
+			if (channel.State == CommunicationState.Opened)
+				channel.Close (timeout);
 		}
 
 		protected override IAsyncResult OnBeginOpen (
@@ -213,7 +215,8 @@ namespace System.ServiceModel.MonoInternal
 
 		protected override void OnOpen (TimeSpan timeout)
 		{
-			channel.Open (timeout);
+			if (channel.State == CommunicationState.Created)
+				channel.Open (timeout);
 		}
 
 		// IChannel

+ 77 - 0
mcs/class/System.ServiceModel/Test/System.ServiceModel/IntegratedConnectionTest.cs

@@ -34,6 +34,8 @@ using System.Runtime.Serialization;
 using System.ServiceModel;
 using System.ServiceModel.Channels;
 using System.ServiceModel.Description;
+using System.ServiceModel.Dispatcher;
+using System.Threading;
 using System.Xml;
 using NUnit.Framework;
 
@@ -134,6 +136,81 @@ namespace MonoTests.System.ServiceModel
 					host.Close ();
 			}
 		}
+
+		[ServiceContract(SessionMode = SessionMode.Required)]
+		interface IFoo3
+		{
+			[OperationContract]
+			int GetInstanceCounter ();
+		}
+
+			[ServiceBehavior (InstanceContextMode = InstanceContextMode.PerSession)]
+		class Foo3 : IFoo3, IDisposable
+		{
+			public static int CreatedInstances { get; set; }
+			public static int DisposedInstances { get; set; }
+
+			public static int InstanceCounter {
+				get { return CreatedInstances - DisposedInstances; }
+			}
+
+			public Foo3 ()
+			{
+				CreatedInstances++;
+			}
+
+			public int GetInstanceCounter ()
+			{
+				return InstanceCounter;
+			}
+
+			public virtual void Dispose ()
+			{
+				DisposedInstances++;
+			}
+
+		}
+
+		private void TestSessionbehaviour (Binding binding, Uri address)
+		{
+			ServiceHost host = new ServiceHost (typeof (Foo3), address);
+			host.AddServiceEndpoint (typeof (IFoo3), binding, address);
+			host.Description.Behaviors.Add (new ServiceThrottlingBehavior () { MaxConcurrentSessions = 1 });
+			Foo3.CreatedInstances = Foo3.DisposedInstances = 0; // Reset
+			host.Open ();
+			Assert.AreEqual (0, Foo3.InstanceCounter, "Initial state wrong"); // just to be sure
+			var cd = (ChannelDispatcher) host.ChannelDispatchers [0];
+			for (int i = 1; i <= 3; ++i) {
+				var factory = new ChannelFactory<IFoo3Client> (binding, address.ToString ());
+				var proxy = factory.CreateChannel ();
+				Assert.AreEqual (1, proxy.GetInstanceCounter (), "One server instance after first call in session #" + i);
+				Assert.AreEqual (1, proxy.GetInstanceCounter (), "One server instance after second call in session #" + i);
+				factory.Close (); // should close session even when no IsTerminating method has been invoked
+				Thread.Sleep (500); // give WCF time to dispose service object
+				Assert.AreEqual (0, Foo3.InstanceCounter, "Service instances must be disposed after channel is closed");
+				Assert.AreEqual (i, Foo3.CreatedInstances, "One new instance per session");
+			}
+			host.Close ();
+		}
+
+		interface IFoo3Client : IFoo3, IClientChannel {}
+
+		[Test (Description = "Tests InstanceContextMode.PerSession behavior for NetTcp binding")]
+		public void TestSessionInstancesNetTcp ()
+		{
+			Binding binding = new NetTcpBinding (SecurityMode.None, false);
+			Uri address = new Uri (binding.Scheme + "://localhost:9999/test");
+			TestSessionbehaviour (binding, address);
+		}
+
+		[Test (Description = "Tests InstanceContextMode.PerSession behavior for WsHttp binding")]
+		[Category ("NotWorking")] // no working WSHttpBinding in mono.
+		public void TestSessionInstancesWsHttp ()
+		{
+			Binding binding = new WSHttpBinding (SecurityMode.None, true);
+			Uri address = new Uri (binding.Scheme + "://localhost:9999/test");
+			TestSessionbehaviour(binding, address);
+		}
 	}
 
 	#region SimpleConnection classes