Procédure : héberger un service WCF dans un service Windows managé

Cette rubrique décrit les étapes de base requises pour créer un service Windows Communication Foundation (WCF) qui est hébergé par un service Windows. Le scénario est activé par l’option d’hébergement du service Windows managé qui est service WCF à durée d’exécution longue hébergé en dehors des services IIS (Internet Information Services) dans un environnement sécurisé qui n’est pas activé par des messages. La durée de vie du service est contrôlée par le système d'exploitation. Cette option d'hébergement est disponible dans toutes les versions de Windows.

Les services Windows peuvent être gérés avec Microsoft.ManagementConsole.SnapIn dans MMC (Microsoft Management Console) et peuvent être configurés pour démarrer automatiquement lorsque le système démarre. Cette option d’hébergement consiste à inscrire le domaine d’application (AppDomain) qui héberge un service WCF en tant que service Windows managé afin que la durée de vie de processus du service soit contrôlée par le Gestionnaire de contrôle des services (SCM) pour les services Windows.

Le code du service inclut l'implémentation du contrat de service, d'une classe de service Windows et d'une classe Installer. La classe d’implémentation du service, CalculatorService, est un service WCF. Le CalculatorWindowsService est un service Windows. Pour prétendre au titre de service Windows, la classe hérite de la ServiceBase et implémente les méthodes OnStart et OnStop. Dans la méthode OnStart, un objet ServiceHost est créé pour le type CalculatorService et est ouvert. Dans la méthode OnStop, le service est arrêté et éliminé. L'hôte est également chargé de fournir une adresse de base à l'hôte de service, qui a été configuré dans les paramètres d'application. La classe Installer, qui hérite de Installer, permet à l'outil Installutil.exe d'installer le programme comme un service Windows.

Construction du service et ajout du code d'hébergement

  1. Créez un projet Application console Visual Studio appelé Service.

  2. Renommez Program.cs en Service.cs.

  3. Remplacez l’espace de noms par Microsoft.ServiceModel.Samples.

  4. Ajoutez des références aux assemblys suivants :

    • System.ServiceModel.dll

    • System.ServiceProcess.dll

    • System.Configuration.Install.dll

  5. Ajoutez les instructions using suivantes à Service.cs.

    using System.ComponentModel;
    using System.ServiceModel;
    using System.ServiceProcess;
    using System.Configuration;
    using System.Configuration.Install;
    
    Imports System.ComponentModel
    Imports System.ServiceModel
    Imports System.ServiceProcess
    Imports System.Configuration
    Imports System.Configuration.Install
    
  6. Définissez le contrat de service ICalculator, tel qu'indiqué dans le code suivant.

    // Define a service contract.
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
    }
    
    ' Define a service contract.
    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
    Public Interface ICalculator
        <OperationContract()> _
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface
    
  7. Implémentez le contrat de service dans une classe appelée CalculatorService, tel qu'indiqué dans le code suivant.

    // Implement the ICalculator service contract in a service class.
    public class CalculatorService : ICalculator
    {
        // Implement the ICalculator methods.
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            return result;
        }
    
        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            return result;
        }
    
        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            return result;
        }
    
        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            return result;
        }
    }
    
    ' Implement the ICalculator service contract in a service class.
    Public Class CalculatorService
        Implements ICalculator
        ' Implement the ICalculator methods.
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
            Return n1 + n2
    
        End Function
    
        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
            Return n1 - n2
    
        End Function
    
        Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
            Return n1 * n2
        End Function
    
        Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
            Return n1 / n2
    
        End Function
    End Class
    
  8. Créez une classe appelée CalculatorWindowsService qui hérite de la classe ServiceBase. Ajoutez une variable locale appelée serviceHost pour faire référence à l'instance ServiceHost. Définissez la méthode Main qui appelle ServiceBase.Run(new CalculatorWindowsService)

    public class CalculatorWindowsService : ServiceBase
    {
        public ServiceHost serviceHost = null;
        public CalculatorWindowsService()
        {
            // Name the Windows Service
            ServiceName = "WCFWindowsServiceSample";
        }
    
        public static void Main()
        {
            ServiceBase.Run(new CalculatorWindowsService());
        }
    
    Public Class CalculatorWindowsService
        Inherits ServiceBase
        Public serviceHost As ServiceHost = Nothing
        Public Sub New()
            ' Name the Windows Service
            ServiceName = "WCFWindowsServiceSample"
        End Sub
    
        Public Shared Sub Main()
            ServiceBase.Run(New CalculatorWindowsService())
        End Sub
    
  9. Substituez la méthode OnStart(String[]) en créant et en ouvrant une nouvelle instance ServiceHost, comme illustré dans le code suivant.

    // Start the Windows service.
    protected override void OnStart(string[] args)
    {
        if (serviceHost != null)
        {
            serviceHost.Close();
        }
    
        // Create a ServiceHost for the CalculatorService type and
        // provide the base address.
        serviceHost = new ServiceHost(typeof(CalculatorService));
    
        // Open the ServiceHostBase to create listeners and start
        // listening for messages.
        serviceHost.Open();
    }
    
    ' Start the Windows service.
    Protected Overrides Sub OnStart(ByVal args() As String)
        If serviceHost IsNot Nothing Then
            serviceHost.Close()
        End If
    
        ' Create a ServiceHost for the CalculatorService type and 
        ' provide the base address.
        serviceHost = New ServiceHost(GetType(CalculatorService))
    
        ' Open the ServiceHostBase to create listeners and start 
        ' listening for messages.
        serviceHost.Open()
    End Sub
    
  10. Substituez la méthode OnStop fermant ServiceHost, comme illustré dans le code suivant.

    protected override void OnStop()
    {
        if (serviceHost != null)
        {
            serviceHost.Close();
            serviceHost = null;
        }
    }
    
    Protected Overrides Sub OnStop()
        If serviceHost IsNot Nothing Then
            serviceHost.Close()
            serviceHost = Nothing
        End If
    End Sub
    
  11. Créez une classe appelée ProjectInstaller qui hérite de Installer et qui est marquée avec RunInstallerAttribute défini avec la valeur true. Cela permet au service Windows d'être installé par l'outil Installutil.exe.

    // Provide the ProjectInstaller class which allows
    // the service to be installed by the Installutil.exe tool
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;
    
        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFWindowsServiceSample";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
    
    ' Provide the ProjectInstaller class which allows 
    ' the service to be installed by the Installutil.exe tool
    <RunInstaller(True)> _
    Public Class ProjectInstaller
        Inherits Installer
        Private process As ServiceProcessInstaller
        Private service As ServiceInstaller
    
        Public Sub New()
            process = New ServiceProcessInstaller()
            process.Account = ServiceAccount.LocalSystem
            service = New ServiceInstaller()
            service.ServiceName = "WCFWindowsServiceSample"
            Installers.Add(process)
            Installers.Add(service)
        End Sub
    End Class
    
  12. Supprimez la classe Service qui a été générée lors de la création du projet.

  13. Ajoutez un fichier de configuration d'application au projet. Remplacez le contenu du fichier par le fichier XML de configuration suivant.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>    <services>
          <!-- This section is optional with the new configuration model
               introduced in .NET Framework 4. -->
          <service name="Microsoft.ServiceModel.Samples.CalculatorService"
                   behaviorConfiguration="CalculatorServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
              </baseAddresses>
            </host>
            <!-- this endpoint is exposed at the base address provided by host: http://localhost:8000/ServiceModelSamples/service  -->
            <endpoint address=""
                      binding="wsHttpBinding"
                      contract="Microsoft.ServiceModel.Samples.ICalculator" />
            <!-- the mex endpoint is exposed at http://localhost:8000/ServiceModelSamples/service/mex -->
            <endpoint address="mex"
                      binding="mexHttpBinding"
                      contract="IMetadataExchange" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="CalculatorServiceBehavior">
              <serviceMetadata httpGetEnabled="true"/>
              <serviceDebug includeExceptionDetailInFaults="False"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>
    

    Cliquez avec le bouton droit sur le fichier App.config dans l’Explorateur de solutions, puis sélectionnez Propriétés. Sous Copier dans le répertoire de sortie, sélectionnez Copier si plus récent.

    L'exemple spécifie explicitement les points de terminaison dans le fichier de configuration. Si vous n'ajoutez pas de points de terminaison au service, le runtime ajoute les points de terminaison par défaut. Dans cet exemple, étant donné que le service a un ServiceMetadataBehavior défini sur la valeur true, la publication des métadonnées est également activée pour votre service. Pour plus d’informations sur les points de terminaison, les liaisons et les comportements par défaut, consultez Configuration simplifiée et Configuration simplifiée pour les services WCF.

Démarrez et exécutez le service.

  1. Générez la solution pour créer l'exécutable Service.exe.

  2. Ouvrez l’invite de commandes développeur pour Visual Studio et accédez au répertoire du projet. Pour installer le service Windows, tapez installutil bin\service.exe à l'invite de commandes.

    Tapez services.msc à l'invite de commandes pour accéder au Gestionnaire de contrôle des services (SCM). Le service Windows doit apparaître dans les services comme « WCFWindowsServiceSample ». Le service WCF peut répondre aux clients uniquement si le service Windows est en cours d’exécution. Pour démarrer le service, cliquez avec le bouton droit sur ce dernier dans le Gestionnaire de contrôle des services (SCM), puis sélectionnez « Démarrer » ou tapez net start WCFWindowsServiceSample à l’invite de commandes.

  3. Si vous apportez des modifications au service, vous devez d'abord l'arrêter et le désinstaller. Pour arrêter le service, cliquez avec le bouton droit sur ce dernier dans le Gestionnaire de contrôle des services (SCM), puis sélectionnez « Arrêter » ou tapez net stop WCFWindowsServiceSample à l’invite de commandes. Notez que si vous arrêtez le service Windows puis exécutez un client, une exception EndpointNotFoundException se produit lorsqu'un client tente d'accéder au service. Pour désinstaller le service Windows, tapez installutil /u bin\service.exe à l’invite de commandes.

Exemple

L’intégralité du code utilisé dans cette rubrique est présentée ci-dessous :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;
using System.ServiceModel;
using System.ServiceProcess;
using System.Configuration;
using System.Configuration.Install;

namespace Microsoft.ServiceModel.Samples
{
    // Define a service contract.
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
    }

    // Implement the ICalculator service contract in a service class.
    public class CalculatorService : ICalculator
    {
        // Implement the ICalculator methods.
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            return result;
        }

        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            return result;
        }

        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            return result;
        }

        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            return result;
        }
    }

    public class CalculatorWindowsService : ServiceBase
    {
        public ServiceHost serviceHost = null;
        public CalculatorWindowsService()
        {
            // Name the Windows Service
            ServiceName = "WCFWindowsServiceSample";
        }

        public static void Main()
        {
            ServiceBase.Run(new CalculatorWindowsService());
        }

        // Start the Windows service.
        protected override void OnStart(string[] args)
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
            }

            // Create a ServiceHost for the CalculatorService type and
            // provide the base address.
            serviceHost = new ServiceHost(typeof(CalculatorService));

            // Open the ServiceHostBase to create listeners and start
            // listening for messages.
            serviceHost.Open();
        }

        protected override void OnStop()
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
                serviceHost = null;
            }
        }
    }

    // Provide the ProjectInstaller class which allows
    // the service to be installed by the Installutil.exe tool
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;

        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFWindowsServiceSample";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text

Imports System.ComponentModel
Imports System.ServiceModel
Imports System.ServiceProcess
Imports System.Configuration
Imports System.Configuration.Install

Namespace Microsoft.ServiceModel.Samples
    ' Define a service contract.
    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
    Public Interface ICalculator
        <OperationContract()> _
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface

    ' Implement the ICalculator service contract in a service class.
    Public Class CalculatorService
        Implements ICalculator
        ' Implement the ICalculator methods.
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
            Return n1 + n2

        End Function

        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
            Return n1 - n2

        End Function

        Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
            Return n1 * n2
        End Function

        Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
            Return n1 / n2

        End Function
    End Class

    Public Class CalculatorWindowsService
        Inherits ServiceBase
        Public serviceHost As ServiceHost = Nothing
        Public Sub New()
            ' Name the Windows Service
            ServiceName = "WCFWindowsServiceSample"
        End Sub

        Public Shared Sub Main()
            ServiceBase.Run(New CalculatorWindowsService())
        End Sub

        ' Start the Windows service.
        Protected Overrides Sub OnStart(ByVal args() As String)
            If serviceHost IsNot Nothing Then
                serviceHost.Close()
            End If

            ' Create a ServiceHost for the CalculatorService type and 
            ' provide the base address.
            serviceHost = New ServiceHost(GetType(CalculatorService))

            ' Open the ServiceHostBase to create listeners and start 
            ' listening for messages.
            serviceHost.Open()
        End Sub

        Protected Overrides Sub OnStop()
            If serviceHost IsNot Nothing Then
                serviceHost.Close()
                serviceHost = Nothing
            End If
        End Sub
    End Class
    ' Provide the ProjectInstaller class which allows 
    ' the service to be installed by the Installutil.exe tool
    <RunInstaller(True)> _
    Public Class ProjectInstaller
        Inherits Installer
        Private process As ServiceProcessInstaller
        Private service As ServiceInstaller

        Public Sub New()
            process = New ServiceProcessInstaller()
            process.Account = ServiceAccount.LocalSystem
            service = New ServiceInstaller()
            service.ServiceName = "WCFWindowsServiceSample"
            Installers.Add(process)
            Installers.Add(service)
        End Sub
    End Class
End Namespace

Comme pour l'option d'auto-hébergement, l'environnement d'hébergement du service Windows requiert que le code d'hébergement soit écrit dans le cadre de l'application. Le service est implémenté en tant qu'application console et contient son propre code d'hébergement. Dans d'autres environnements d'hébergement, tels que le service d'activation des processus Windows (WAS) dans les services IIS (Internet Information Services), il n'est pas nécessaire que les développeurs écrivent le code d'hébergement.

Voir aussi