Suppose there is a wcf service that is hosted in win service.

If the user decides to cut down the service, then how can I properly shut down the wcf service?

Suppose one user called the file transfer method, another user does something with the base.

If the Win Service is terminated without logic, it will lead to irreversible consequences.

  • Can you give more details on exactly how the WCF service is hosted and who is responsible for its work? There is such a situation - you need to create either locks or shared resources. To not complete the service prematurely. How I think to post a check before closing so that nothing is taken and wait. If to create through ServiceHost, then to finish work through Close. Specify the question, it is not quite clear what and how it will work for you. Add some code. - Denis Bubnov
  • If you are given an exhaustive answer, mark it as correct (tick the selected answer). - andreycha

2 answers 2

You can try when you stop the Win Service to signal the WCF Service using the CancellationToken that the Win Service is about to stop. The WCF Service inside itself should check the status of this CancellationToken , and, if cancellation is requested, throw an exception for the newly requested operations, and those that are already running, for example, abort. Win Service should wait until the cancellation of active operations performed by the WCF Service is completed. To track this in WCF Service, for example, you ManualResetEvent use a ManualResetEvent and a counter of active operations.

Below is an example implementation (with a sufficient number of simplifications).

The contract:

 [ServiceContract] public interface IWCFService { [OperationContract] string Echo(string str); } 

WCF Service:

 public class WCFService : IWCFService { CancellationToken ctOperations; public WCFService() { //Ρƒ WinService Π±Π΅Ρ€Ρ‘ΠΌ CancellationToken остановки ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ctOperations = MyService.GetOperationsCancellationToken(); } public string Echo(string str) { //Π½Π΅ выполняСм Π½ΠΎΠ²Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ, Ссли Π½Π°Ρ‡Π°Π»Π°ΡΡŒ остановка if (ctOperations.IsCancellationRequested) throw new Exception("Service stopping."); try { //ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ счётчик Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ IncActiveCnt(); //имитация Π΄Π΅ΡΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ int i = 0; while (i++ < 10) { ctOperations.ThrowIfCancellationRequested(); Thread.Sleep(500); } return "Echo " + str; } catch (OperationCanceledException) { throw new Exception("Service stopping."); } catch (Exception) { throw; } finally { //ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Π΅ΠΌ счётчик Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ DecActiveCnt(); } } static object lockObj = new object(); static int activeOperationCnt = 0; static ManualResetEvent evtNoActiveOperations = new ManualResetEvent(true); //свойство для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π² WinService static public ManualResetEvent NoActiveOperations { get { return evtNoActiveOperations; } } private void DecActiveCnt() { lock (lockObj) { if (--activeOperationCnt == 0) //сигналим, Ссли Π½Π΅Ρ‚ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ evtNoActiveOperations.Set(); } } private void IncActiveCnt() { lock (lockObj) { activeOperationCnt++; //сбрасываСм сигнал, Ссли Π΅ΡΡ‚ΡŒ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ evtNoActiveOperations.Reset(); } } } 

Win Service (in this example, not an external application, but it is itself a client of WCF Service):

 public class MyService : ServiceBase { static CancellationTokenSource ctsOperations = null; static ServiceHost svcHost = null; public MyService() { } protected override void OnStart(string[] args) { //Ρ‚ΠΎΠΊΠ΅Π½ для остановки ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ctsOperations = new CancellationTokenSource(); svcHost = new ServiceHost(typeof(WCFService)); svcHost.Open(); //запускаСм ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° WCF Service ThreadPool.QueueUserWorkItem(UseWCFService); //ΠΆΠ΄Ρ‘ΠΌ 13 сСк. Thread.Sleep(13000); //Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ остановим Win Service Stop(); } protected override void OnStop() { //сигналим ΠΎΡ‚ΠΌΠ΅Π½Ρƒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ctsOperations.Cancel(); int msecExtend = 3000; RequestAdditionalTime(msecExtend); //ΠΆΠ΄Ρ‘ΠΌ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ WCFService.NoActiveOperations.WaitOne(msecExtend); svcHost.Close(); ctsOperations.Dispose(); } public static CancellationToken GetOperationsCancellationToken() { return ctsOperations.Token; } private void UseWCFService(object state) { //ΠΈΠΌΠΈΡ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Uri tcpUri = new Uri(@"http://localhost:8733/WCFService/"); EndpointAddress address = new EndpointAddress(tcpUri); BasicHttpBinding binding = new BasicHttpBinding(); ChannelFactory<IWCFService> f = new ChannelFactory<IWCFService>(binding, address); IWCFService svc = f.CreateChannel(); int i = 0; while (!ctsOperations.IsCancellationRequested) try { string echo = svc.Echo((++i).ToString()); Console.WriteLine(echo); } catch { Console.WriteLine("Error"); } } } 
  • I was already going to write the answer myself :). - andreycha
  • @ i-one, I have a question. Suppose I have N operations that the client can call. I need to check that the token is not stopped, do I need to perform in each method, or can I somehow be freeing myself from the operation to perform a check when the user jerks something? - iluxa1810
  • @ iluxa1810, I'm still immersed in the WCF topic. As far as I understand it is possible to generalize the "binding" (everything that in the implementation of the Echo method stands up to the line int i = 0; and after the return ), adding the appropriate IOperationInvoker ( example ), however, there is no IOperationInvoker checking the status of the CancellationToken inside check it and in what places of the code - it is always at the discretion of the called code, and it must be subject to its logic). - i-one
  • @ i-one, and there are no events? Simply, it turns out that I should do ctOperations.ThrowIfCancellationRequested () after each method, which is not correct. - iluxa1810
  • @ iluxa1810, look at the example by reference, there is a sense that the necessary checks and actions can be carried out so that they are performed in one place (in the class inherited from IOperationInvoker ), only this is done not through events, but through attributes. An attribute is inherited from IOperationBehavior , which specifies that when performing an operation, a certain Invoker should be used (ours, with the desired behavior). Then this attribute can be assigned to all operations (or, if you want, only specific). Those. creates a kind of "layer". - i-one

Slightly generalize and supplement the answer @ i-one. In order to correctly complete the service, you need two things:

  • be able to tell the right code that it is time to round out
  • wait for this code to complete its execution

You can signal a message in different ways, the most reasonable one is CancellationToken , which is sent to all the necessary code when it starts.

Waiting for completion is also possible in different ways. In the case of streams / synchronous execution, the @ i-one led recipe.

If the application is fully asynchronous (using async/await ), the task is remembered in the startup service and the task is await on the foot.

Another point worth mentioning is that by default, Service Console Manager allocates 30 seconds for the service to terminate (if this value has not been redefined in the registry). If the service did not meet the allotted time, then it is marked in the console as stopped, but the process continues to work, which is not very correct. If the correct completion requires a lot of time, then you can request additional time - up to 125 seconds in total:

 protected override void OnStop() { int timeout = 10000; while (!mainTask.Wait(timeout)) { RequestAdditionalTime(timeout); } } 

However, in any case, it is useful to have some kind of general timeout (for example, 120 seconds) and to shut down the work forcibly (if suddenly some code is frozen).