Partager via


Migration de .NET Remoting vers WCF

Cet article explique comment migrer une application qui utilise la communication à distance .NET pour utiliser Windows Communication Foundation (WCF). Il compare des concepts similaires entre ces produits, puis décrit comment accomplir plusieurs scénarios de communication à distance courants dans WCF.

.NET Remoting est un produit hérité pris en charge uniquement pour la compatibilité descendante. Il n’est pas sécurisé dans les environnements d’approbation mixte, car il ne peut pas maintenir les niveaux d’approbation distincts entre le client et le serveur. Par exemple, vous ne devez jamais exposer un point de terminaison de communication à distance .NET à Internet ou à des clients non approuvés. Nous vous recommandons de migrer des applications de communication à distance existantes vers des technologies plus récentes et plus sécurisées. Si la conception de l’application utilise uniquement HTTP et est RESTful, nous vous recommandons de ASP.NET API web. Pour plus d’informations, consultez ASP.NET API web. Si l’application est basée sur SOAP ou nécessite des protocoles non Http tels que TCP, nous vous recommandons WCF.

Comparaison de la communication à distance .NET à WCF

Cette section compare les blocs de construction de base de .NET Remoting à leurs équivalents WCF. Nous allons utiliser ces blocs de construction ultérieurement pour créer des scénarios de serveur client courants dans WCF. Le graphique suivant récapitule les principales similitudes et différences entre la communication à distance .NET et WCF.

Communication à distance .NET WCF (Windows Communication Foundation)
type de serveur Sous-classe MarshalByRefObject Marquer avec l’attribut [ServiceContract]
Opérations de service Méthodes publiques sur le type de serveur Marquer avec l’attribut [OperationContract]
Sérialisation ISerializable ou [Serializable] DataContractSerializer ou XmlSerializer
Objets passés Par valeur ou par référence Par valeur uniquement
Erreurs/exceptions Toute exception sérialisable FaultContract<TDetail>
Objets proxy client Les proxys transparents fortement typés sont créés automatiquement à partir de MarshalByRefObjects Les proxys fortement typés sont générés à la demande à l’aide de ChannelFactory<TChannel>
Plateforme requise Le client et le serveur doivent utiliser le système d’exploitation Microsoft et .NET Multiplateforme
Format du message Privé Normes du secteur (par exemple, SOAP et WS-*)

Comparaison de l’implémentation du serveur

Création d’un serveur dans la communication à distance .NET

Les types de serveurs .NET Remoting doivent dériver de MarshalByRefObject et définir des méthodes que le client peut appeler, comme suit :

public class RemotingServer : MarshalByRefObject  
{  
    public Customer GetCustomer(int customerId) { … }  
}  

Les méthodes publiques de ce type de serveur deviennent le contrat public disponible pour les clients. Il n’existe aucune séparation entre l’interface publique du serveur et son implémentation : un type gère les deux.

Une fois le type de serveur défini, il peut être mis à la disposition des clients, comme dans l’exemple suivant :

TcpChannel channel = new TcpChannel(8080);  
ChannelServices.RegisterChannel(channel, ensureSecurity : true);  
RemotingConfiguration.RegisterWellKnownServiceType(  
    typeof(RemotingServer),
    "RemotingServer",
    WellKnownObjectMode.Singleton);  
Console.WriteLine("RemotingServer is running.  Press ENTER to terminate...");  
Console.ReadLine();  

Il existe de nombreuses façons de rendre le type de communication à distance disponible en tant que serveur, notamment à l’aide de fichiers de configuration. Il s’agit simplement d’un exemple.

Création d’un serveur dans WCF

L’étape équivalente dans WCF implique la création de deux types : le « contrat de service » public et l’implémentation concrète. Le premier est déclaré en tant qu’interface marquée avec [ServiceContract]. Les méthodes disponibles pour les clients sont marquées avec [OperationContract] :

[ServiceContract]  
public interface IWCFServer  
{  
    [OperationContract]  
    Customer GetCustomer(int customerId);  
}  

L’implémentation du serveur est définie dans une classe concrète distincte, comme dans l’exemple suivant :

public class WCFServer : IWCFServer  
{  
    public Customer GetCustomer(int customerId) { … }  
}  

Une fois ces types définis, le serveur WCF peut être mis à la disposition des clients, comme dans l’exemple suivant :

NetTcpBinding binding = new NetTcpBinding();  
Uri baseAddress = new Uri("net.tcp://localhost:8000/wcfserver");  
  
using (ServiceHost serviceHost = new ServiceHost(typeof(WCFServer), baseAddress))  
{  
    serviceHost.AddServiceEndpoint(typeof(IWCFServer), binding, baseAddress);  
    serviceHost.Open();  
  
    Console.WriteLine($"The WCF server is ready at {baseAddress}.");
    Console.WriteLine("Press <ENTER> to terminate service...");  
    Console.WriteLine();  
    Console.ReadLine();  
}  

Remarque

TCP est utilisé dans les deux exemples pour les conserver aussi similaires que possible. Reportez-vous aux procédures pas à pas du scénario plus loin dans cette rubrique pour obtenir des exemples d’utilisation de HTTP.

Il existe de nombreuses façons de configurer et d’héberger des services WCF. Il s’agit simplement d’un exemple, appelé « auto-hébergé ». Pour plus d’informations, consultez les rubriques suivantes :

Comparaison de l’implémentation du client

Création d’un client dans la communication à distance .NET

Une fois qu’un objet serveur de communication à distance .NET a été mis à disposition, il peut être consommé par les clients, comme dans l’exemple suivant :

TcpChannel channel = new TcpChannel();  
ChannelServices.RegisterChannel(channel, ensureSecurity : true);  
RemotingServer server = (RemotingServer)Activator.GetObject(  
                            typeof(RemotingServer),
                            "tcp://localhost:8080/RemotingServer");  
  
RemotingCustomer customer = server.GetCustomer(42);  
Console.WriteLine($"Customer {customer.FirstName} {customer.LastName} received.");

L’instance RemotingServer retournée à partir d’Activateor.GetObject() est appelée « proxy transparent ». Il implémente l’API publique pour le type RemotingServer sur le client, mais toutes les méthodes appellent l’objet serveur s’exécutant dans un autre processus ou ordinateur.

Création d’un client dans WCF

L’étape équivalente dans WCF implique l’utilisation d’une fabrique de canaux pour créer le proxy explicitement. Comme La communication à distance, l’objet proxy peut être utilisé pour appeler des opérations sur le serveur, comme dans l’exemple suivant :

NetTcpBinding binding = new NetTcpBinding();  
String url = "net.tcp://localhost:8000/wcfserver";  
EndpointAddress address = new EndpointAddress(url);  
ChannelFactory<IWCFServer> channelFactory =
    new ChannelFactory<IWCFServer>(binding, address);  
IWCFServer server = channelFactory.CreateChannel();  
  
Customer customer = server.GetCustomer(42);  
Console.WriteLine($"  Customer {customer.FirstName} {customer.LastName} received.");

Cet exemple montre la programmation au niveau du canal, car elle est la plus similaire à l’exemple de communication à distance. Vous trouverez également l’approche Add Service Reference dans Visual Studio qui génère du code pour simplifier la programmation du client. Pour plus d’informations, consultez les rubriques suivantes :

Utilisation de la sérialisation

La communication à distance .NET et WCF utilisent la sérialisation pour envoyer des objets entre le client et le serveur, mais elles diffèrent de ces manières importantes :

  1. Ils utilisent différents sérialiseurs et conventions pour indiquer ce qu’il faut sérialiser.

  2. La communication à distance .NET prend en charge la sérialisation « par référence » qui permet à la méthode ou à la propriété d’accéder à un niveau pour exécuter du code sur l’autre niveau, qui se trouve au-delà des limites de sécurité. Cette fonctionnalité expose les vulnérabilités de sécurité et constitue l’une des principales raisons pour lesquelles les points de terminaison remoting ne doivent jamais être exposés à des clients non approuvés.

  3. La sérialisation utilisée par Remoting est un refus (exclut explicitement ce qui ne doit pas sérialiser) et la sérialisation WCF est opt-in (marquez explicitement les membres à sérialiser).

Sérialisation dans la communication à distance .NET

La communication à distance .NET prend en charge deux façons de sérialiser et désérialiser des objets entre le client et le serveur :

  • Par valeur : les valeurs de l’objet sont sérialisées entre les limites de niveau et une nouvelle instance de cet objet est créée sur l’autre niveau. Tous les appels aux méthodes ou propriétés de cette nouvelle instance s’exécutent uniquement localement et n’affectent pas l’objet ou le niveau d’origine.

  • Par référence , une « référence d’objet » spéciale est sérialisée au-delà des limites de niveau. Lorsqu’un niveau interagit avec des méthodes ou des propriétés de cet objet, il renvoie à l’objet d’origine sur le niveau d’origine. Les objets de référence peuvent circuler dans les deux sens : le serveur vers le client ou le client vers le serveur.

Les types par valeur dans Remoting sont marqués avec l’attribut [Serializable] ou implémentent ISerializable, comme dans l’exemple suivant :

[Serializable]  
public class RemotingCustomer  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public int CustomerId { get; set; }  
}  

Les types de référence dérivent de la classe MarshalByRefObject, comme dans l’exemple suivant :

public class RemotingCustomerReference : MarshalByRefObject  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public int CustomerId { get; set; }  
}  

Il est extrêmement important de comprendre les implications des objets de référence de Remoting. Si un niveau (client ou serveur) envoie un objet de référence à l’autre niveau, tous les appels de méthode s’exécutent sur le niveau propriétaire de l’objet. Par exemple, un client appelant des méthodes sur un objet de référence retourné par le serveur exécute du code sur le serveur. De même, un serveur appelant des méthodes sur un objet de référence fourni par le client exécute du code sur le client. Pour cette raison, l’utilisation de la communication à distance .NET est recommandée uniquement dans les environnements entièrement approuvés. L’exposition d’un point de terminaison de communication à distance .NET public à des clients non approuvés rend un serveur de communication à distance vulnérable aux attaques.

Sérialisation dans WCF

WCF prend uniquement en charge la sérialisation par valeur. La façon la plus courante de définir un type à échanger entre le client et le serveur est semblable à l’exemple suivant :

[DataContract]  
public class WCFCustomer  
{  
    [DataMember]  
    public string FirstName { get; set; }  
  
    [DataMember]  
    public string LastName { get; set; }  
  
    [DataMember]  
    public int CustomerId { get; set; }  
}  

L’attribut [DataContract] identifie ce type comme un type qui peut être sérialisé et désérialisé entre le client et le serveur. L’attribut [DataMember] identifie les propriétés ou champs individuels à sérialiser.

Lorsque WCF envoie un objet à plusieurs niveaux, il sérialise uniquement les valeurs et crée une instance de l’objet sur l’autre niveau. Toutes les interactions avec les valeurs de l’objet se produisent uniquement localement : elles ne communiquent pas avec l’autre niveau de la façon dont les objets de communication à distance .NET font des objets de référence. Pour plus d’informations, consultez Sérialisation et Désérialisation.

Fonctionnalités de gestion des exceptions

Exceptions dans la communication à distance .NET

Les exceptions levées par un serveur de communication à distance sont sérialisées, envoyées au client et levées localement sur le client comme toute autre exception. Les exceptions personnalisées peuvent être créées en classant le type d’exception et en le marquant avec [Serializable]. La plupart des exceptions de framework sont déjà marquées de cette façon, ce qui permet le plus d’être levées par le serveur, sérialisé et re-levée sur le client. Bien que cette conception soit pratique pendant le développement, les informations côté serveur peuvent être divulguées par inadvertance au client. Il s’agit de l’une des nombreuses raisons pour lesquelles la communication à distance doit être utilisée uniquement dans des environnements entièrement approuvés.

Exceptions et erreurs dans WCF

WCF n’autorise pas les types d’exceptions arbitraires à retourner du serveur au client, car il peut entraîner une divulgation d’informations par inadvertance. Si une opération de service lève une exception inattendue, elle provoque la levée d’une exception FaultException à usage général sur le client. Cette exception ne contient aucune information sur la raison ou l’endroit où le problème s’est produit, et pour certaines applications, cela suffit. Les applications qui doivent communiquer des informations d’erreur plus riches au client le font en définissant un contrat d’erreur.

Pour ce faire, créez d’abord un type [DataContract] pour transporter les informations d’erreur.

[DataContract]  
public class CustomerServiceFault  
{  
    [DataMember]  
    public string ErrorMessage { get; set; }  
  
    [DataMember]  
    public int CustomerId {get;set;}  
}  

Spécifiez le contrat d’erreur à utiliser pour chaque opération de service.

[ServiceContract]  
public interface IWCFServer  
{  
    [OperationContract]  
    [FaultContract(typeof(CustomerServiceFault))]  
    Customer GetCustomer(int customerId);  
}  

Le serveur signale des conditions d’erreur en lisant une exception FaultException.

throw new FaultException<CustomerServiceFault>(  
    new CustomerServiceFault() {
        CustomerId = customerId,
        ErrorMessage = "Illegal customer Id"
    });  

Et chaque fois que le client envoie une demande au serveur, il peut intercepter les erreurs comme des exceptions normales.

try  
{  
    Customer customer = server.GetCustomer(-1);  
}  
catch (FaultException<CustomerServiceFault> fault)  
{  
    Console.WriteLine($"Fault received: {fault.Detail.ErrorMessage}");
}  

Pour plus d’informations sur les contrats d’erreur, consultez FaultException.

Considérations relatives à la sécurité

Sécurité dans la communication à distance .NET

Certains canaux de communication à distance .NET prennent en charge les fonctionnalités de sécurité telles que l’authentification et le chiffrement au niveau de la couche canal (IPC et TCP). Le canal HTTP s’appuie sur Internet Information Services (IIS) pour l’authentification et le chiffrement. Malgré cette prise en charge, vous devez prendre en compte .NET Remoting un protocole de communication non sécurisé et l’utiliser uniquement dans des environnements entièrement approuvés. N’exposez jamais un point de terminaison de communication à distance public sur Internet ou des clients non approuvés.

Sécurité dans WCF

WCF a été conçu avec une sécurité à l’esprit, en partie pour résoudre les types de vulnérabilités trouvés dans la communication à distance .NET. WCF offre une sécurité au niveau du transport et du message, et offre de nombreuses options d’authentification, d’autorisation, de chiffrement, et ainsi de suite. Pour plus d’informations, consultez les rubriques suivantes :

Migration vers WCF

Pourquoi migrer de la communication à distance vers WCF ?

  • .NET Remoting est un produit hérité. Comme décrit dans .NET Remoting, il est considéré comme un produit hérité et n’est pas recommandé pour le nouveau développement. WCF ou ASP.NET API web sont recommandés pour les applications nouvelles et existantes.

  • WCF utilise des normes multiplateformes. WCF a été conçu avec une interopérabilité multiplateforme à l’esprit et prend en charge de nombreuses normes du secteur (SOAP, WS-Security, WS-Trust, etc.). Un service WCF peut interagir avec des clients s’exécutant sur des systèmes d’exploitation autres que Windows. La communication à distance a été conçue principalement pour les environnements où les applications serveur et clientes s’exécutent à l’aide de .NET Framework sur un système d’exploitation Windows.

  • WCF dispose d’une sécurité intégrée. WCF a été conçu avec la sécurité à l’esprit et offre de nombreuses options pour l’authentification, la sécurité au niveau du transport, la sécurité au niveau du message, etc. La communication à distance a été conçue pour faciliter l’interopérabilité des applications, mais n’a pas été conçue pour être sécurisée dans des environnements non approuvés. WCF a été conçu pour fonctionner dans des environnements approuvés et non approuvés.

Recommandations relatives à la migration

Voici les étapes recommandées pour migrer de .NET Remoting vers WCF :

  • Créez le contrat de service. Définissez vos types d’interface de service et marquez-les avec l’attribut [ServiceContract]. Marquez toutes les méthodes que les clients seront autorisés à appeler avec [OperationContract].

  • Créez le contrat de données. Définissez les types de données qui seront échangés entre le serveur et le client, puis marquez-les avec l’attribut [DataContract]. Marquez tous les champs et propriétés que le client sera autorisé à utiliser avec [DataMember].

  • Créez le contrat d’erreur (facultatif). Créez les types qui seront échangés entre le serveur et le client lorsque des erreurs sont rencontrées. Marquez ces types avec [DataContract] et [DataMember] pour les rendre sérialisables. Pour toutes les opérations de service que vous avez marquées avec [OperationContract], marquez-les également avec [FaultContract] pour indiquer les erreurs qu’ils peuvent retourner.

  • Configurez et hébergez le service. Une fois le contrat de service créé, l’étape suivante consiste à configurer une liaison pour exposer le service à un point de terminaison. Pour plus d’informations, consultez Points de terminaison : Adresses, liaisons et contrats.

Une fois qu’une application Remoting a été migrée vers WCF, il est toujours important de supprimer les dépendances sur la communication à distance .NET. Cela garantit que toutes les vulnérabilités de communication à distance sont supprimées de l’application. Il s'agit des étapes suivantes :

  • Arrêtez l’utilisation de MarshalByRefObject. Le type MarshalByRefObject existe uniquement pour la communication à distance et n’est pas utilisé par WCF. Tous les types d’applications qui sous-classe MarshalByRefObject doivent être supprimés ou modifiés.

  • Arrêt de l’utilisation de [Serializable] et ISerializable. L’attribut [Serializable] et l’interface ISerializable ont été initialement conçus pour sérialiser des types dans des environnements approuvés, et ils sont utilisés par Remoting. La sérialisation WCF s’appuie sur les types marqués avec [DataContract] et [DataMember]. Les types de données utilisés par une application doivent être modifiés pour utiliser [DataContract] et ne pas utiliser ISerializable ou [Serializable].

Scénarios de migration

Voyons maintenant comment effectuer les scénarios de communication à distance courants suivants dans WCF :

  1. Le serveur retourne un objet par valeur au client

  2. Le serveur retourne un objet par référence au client

  3. Le client envoie un objet par valeur au serveur

Remarque

L’envoi d’un objet par référence du client au serveur n’est pas autorisé dans WCF.

Lorsque vous lisez ces scénarios, supposons que nos interfaces de base pour la communication à distance .NET ressemblent à l’exemple suivant. L’implémentation de la communication à distance .NET n’est pas importante ici, car nous voulons illustrer uniquement comment utiliser WCF pour implémenter des fonctionnalités équivalentes.

public class RemotingServer : MarshalByRefObject  
{  
    // Demonstrates server returning object by-value  
    public Customer GetCustomer(int customerId) {…}  
  
    // Demonstrates server returning object by-reference  
    public CustomerReference GetCustomerReference(int customerId) {…}  
  
    // Demonstrates client passing object to server by-value  
    public bool UpdateCustomer(Customer customer) {…}  
}  

Scénario 1 : Le service retourne un objet par valeur

Ce scénario illustre un serveur qui retourne un objet au client par valeur. WCF retourne toujours des objets du serveur par valeur, de sorte que les étapes suivantes décrivent simplement comment générer un service WCF normal.

  1. Commencez par définir une interface publique pour le service WCF et marquez-la avec l’attribut [ServiceContract]. Nous utilisons [OperationContract] pour identifier les méthodes côté serveur que notre client appellera.

    [ServiceContract]  
    public interface ICustomerService  
    {  
        [OperationContract]  
        Customer GetCustomer(int customerId);  
    
        [OperationContract]  
        bool UpdateCustomer(Customer customer);  
    }  
    
  2. L’étape suivante consiste à créer le contrat de données pour ce service. Pour ce faire, nous créons des classes (et non des interfaces) marquées avec l’attribut [DataContract]. Les propriétés ou champs individuels que nous souhaitons visibles à la fois pour le client et le serveur sont marqués avec [DataMember]. Si nous voulons que les types dérivés soient autorisés, nous devons utiliser l’attribut [KnownType] pour les identifier. Les seuls types WCF permettent d’être sérialisés ou désérialisés pour ce service sont ceux de l’interface de service et de ces « types connus ». Toute tentative d’échange d’un autre type non dans cette liste sera rejetée.

    [DataContract]  
    [KnownType(typeof(PremiumCustomer))]  
    public class Customer  
    {  
        [DataMember]  
        public string FirstName { get; set; }  
    
        [DataMember]  
        public string LastName { get; set; }  
    
        [DataMember]  
        public int CustomerId { get; set; }  
    }  
    
    [DataContract]  
    public class PremiumCustomer : Customer
    {  
        [DataMember]  
        public int AccountId { get; set; }  
    }  
    
  3. Ensuite, nous fournissons l’implémentation de l’interface de service.

    public class CustomerService : ICustomerService  
    {  
        public Customer GetCustomer(int customerId)  
        {  
            // read from database  
        }  
    
        public bool UpdateCustomer(Customer customer)  
        {  
            // write to database  
        }  
    }  
    
  4. Pour exécuter le service WCF, nous devons déclarer un point de terminaison qui expose cette interface de service à une URL spécifique à l’aide d’une liaison WCF spécifique. Cette opération est généralement effectuée en ajoutant les sections suivantes au fichier web.config du projet de serveur.

    <configuration>  
      <system.serviceModel>  
        <services>  
          <service name="Server.CustomerService">  
            <endpoint address="http://localhost:8083/CustomerService"  
                      binding="basicHttpBinding"  
                      contract="Shared.ICustomerService" />  
          </service>  
        </services>  
      </system.serviceModel>  
    </configuration>  
    
  5. Le service WCF peut ensuite être démarré avec le code suivant :

    ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService));  
        customerServiceHost.Open();  
    

    Lorsque ce ServiceHost est démarré, il utilise le fichier web.config pour établir le contrat, la liaison et le point de terminaison appropriés. Pour plus d’informations sur les fichiers de configuration, consultez Configuration des services à l’aide de fichiers de configuration. Ce style de démarrage du serveur est appelé auto-hébergement. Pour en savoir plus sur les autres choix d’hébergement des services WCF, consultez Services d’hébergement.

  6. Le app.config du projet client doit déclarer des informations de liaison correspondantes pour le point de terminaison du service. Le moyen le plus simple de procéder dans Visual Studio consiste à utiliser Ajouter une référence de service, qui met automatiquement à jour le fichier app.config. Vous pouvez également ajouter ces mêmes modifications manuellement.

    <configuration>  
      <system.serviceModel>  
        <client>  
          <endpoint name="customerservice"  
                    address="http://localhost:8083/CustomerService"  
                    binding="basicHttpBinding"  
                    contract="Shared.ICustomerService"/>  
        </client>  
      </system.serviceModel>  
    </configuration>  
    

    Pour plus d’informations sur l’utilisation d’Ajouter une référence de service, consultez Guide pratique pour ajouter, mettre à jour ou supprimer une référence de service.

  7. À présent, nous pouvons appeler le service WCF à partir du client. Pour ce faire, nous créons une fabrique de canaux pour ce service, en lui demandant un canal et en appelant directement la méthode souhaitée sur ce canal. Nous pouvons le faire, car le canal implémente l’interface du service et gère la logique de requête/réponse sous-jacente pour nous. La valeur de retour de cet appel de méthode est la copie désérialisée de la réponse du serveur.

    ChannelFactory<ICustomerService> factory =  
        new ChannelFactory<ICustomerService>("customerservice");  
    ICustomerService service = factory.CreateChannel();  
    Customer customer = service.GetCustomer(42);  
    Console.WriteLine($"  Customer {customer.FirstName} {customer.LastName} received.");
    

Les objets retournés par WCF du serveur au client sont toujours par valeur. Les objets sont des copies désérialisées des données envoyées par le serveur. Le client peut appeler des méthodes sur ces copies locales sans risque d’appeler du code serveur via des rappels.

Scénario 2 : Le serveur retourne un objet par référence

Ce scénario illustre le serveur fournissant un objet au client par référence. Dans .NET Remoting, il est géré automatiquement pour n’importe quel type dérivé de MarshalByRefObject, qui est sérialisé par référence. Un exemple de ce scénario permet à plusieurs clients d’avoir des objets côté serveur avec session indépendants. Comme mentionné précédemment, les objets retournés par un service WCF sont toujours par valeur, il n’existe donc pas d’équivalent direct d’un objet de référence, mais il est possible d’obtenir quelque chose similaire à la sémantique par référence à l’aide d’un EndpointAddress10 objet. Il s’agit d’un objet par valeur sérialisable qui peut être utilisé par le client pour obtenir un objet de référence avec session sur le serveur. Cela permet de faire en sorte que plusieurs clients disposent d’objets côté serveur avec session indépendant.

  1. Tout d’abord, nous devons définir un contrat de service WCF qui correspond à l’objet sessionful lui-même.

    [ServiceContract(SessionMode = SessionMode.Allowed)]  
        public interface ISessionBoundObject  
        {  
            [OperationContract]  
            string GetCurrentValue();  
    
            [OperationContract]  
            void SetCurrentValue(string value);  
        }  
    

    Conseil / Astuce

    Notez que l’objet sessionful est marqué avec [ServiceContract], ce qui en fait une interface de service WCF normale. La définition de la propriété SessionMode indique qu’il s’agit d’un service avec session. Dans WCF, une session est un moyen de mettre en corrélation plusieurs messages envoyés entre deux points de terminaison. Cela signifie qu’une fois qu’un client obtient une connexion à ce service, une session est établie entre le client et le serveur. Le client utilisera une instance unique de l’objet côté serveur pour toutes ses interactions au sein de cette session unique.

  2. Ensuite, nous devons fournir l’implémentation de cette interface de service. En le dénoteant avec [ServiceBehavior] et en définissant l’InstanceContextMode, nous indiquez à WCF d’utiliser une instance unique de ce type pour chaque session.

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]  
        public class MySessionBoundObject : ISessionBoundObject  
        {  
            private string _value;  
    
            public string GetCurrentValue()  
            {  
                return _value;  
            }  
    
            public void SetCurrentValue(string val)  
            {  
                _value = val;  
            }  
    
        }  
    
  3. Nous avons maintenant besoin d’un moyen d’obtenir une instance de cet objet sessionful. Pour ce faire, nous créons une autre interface de service WCF qui retourne un objet EndpointAddress10. Il s’agit d’une forme sérialisable d’un point de terminaison que le client peut utiliser pour créer l’objet sessionful.

    [ServiceContract]  
        public interface ISessionBoundFactory  
        {  
            [OperationContract]  
            EndpointAddress10 GetInstanceAddress();  
        }  
    

    Et nous implémentons ce service WCF :

    public class SessionBoundFactory : ISessionBoundFactory  
        {  
            public static ChannelFactory<ISessionBoundObject> _factory =
                new ChannelFactory<ISessionBoundObject>("sessionbound");  
    
            public SessionBoundFactory()  
            {  
            }  
    
            public EndpointAddress10 GetInstanceAddress()  
            {  
                IClientChannel channel = (IClientChannel)_factory.CreateChannel();  
                return EndpointAddress10.FromEndpointAddress(channel.RemoteAddress);  
            }  
        }  
    

    Cette implémentation gère une fabrique de canaux singleton pour créer des objets avec session. Lorsque GetInstanceAddress() est appelé, il crée un canal et crée un objet EndpointAddress10 qui pointe efficacement vers l’adresse distante associée à ce canal. EndpointAddress10 est simplement un type de données qui peut être retourné par valeur au client.

  4. Nous devons modifier le fichier de configuration du serveur en procédant comme indiqué dans l’exemple ci-dessous :

    1. Déclarez une <section cliente> qui décrit le point de terminaison de l’objet sessionful. Cela est nécessaire, car le serveur agit également en tant que client dans cette situation.

    2. Déclarez des points de terminaison pour l’objet factory et sessionful. Cela est nécessaire pour permettre au client de communiquer avec les points de terminaison de service d’acquérir EndpointAddress10 et de créer le canal avec session.

    <configuration>  
      <system.serviceModel>  
         <client>  
          <endpoint name="sessionbound"  
                    address="net.tcp://localhost:8081/SessionBoundObject"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundObject"/>  
        </client>  
        <services>  
          <service name="Server.CustomerService">  
            <endpoint address="http://localhost:8083/CustomerService"  
                      binding="basicHttpBinding"  
                      contract="Shared.ICustomerService" />  
          </service>  
          <service name="Server.MySessionBoundObject">  
            <endpoint address="net.tcp://localhost:8081/SessionBoundObject"  
                      binding="netTcpBinding"  
                      contract="Shared.ISessionBoundObject" />  
          </service>  
          <service name="Server.SessionBoundFactory">  
            <endpoint address="net.tcp://localhost:8081/SessionBoundFactory"  
                      binding="netTcpBinding"  
                      contract="Shared.ISessionBoundFactory" />  
          </service>  
        </services>  
      </system.serviceModel>  
    </configuration>  
    

    Nous pouvons ensuite démarrer ces services :

    ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));  
    factoryHost.Open();  
    
    ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject));  
    sessionHost.Open();  
    
  5. Nous configurons le client en déclarant ces mêmes points de terminaison dans le fichier app.config de son projet.

    <configuration>  
      <system.serviceModel>  
        <client>  
          <endpoint name="customerservice"  
                    address="http://localhost:8083/CustomerService"  
                    binding="basicHttpBinding"  
                    contract="Shared.ICustomerService"/>  
          <endpoint name="sessionbound"  
                    address="net.tcp://localhost:8081/SessionBoundObject"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundObject"/>  
          <endpoint name="factory"  
                    address="net.tcp://localhost:8081/SessionBoundFactory"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundFactory"/>  
        </client>  
      </system.serviceModel>  
    </configuration>  
    
  6. Pour créer et utiliser cet objet sessionful, le client doit effectuer les étapes suivantes :

    1. Créez un canal vers le service ISessionBoundFactory.

    2. Utilisez ce canal pour appeler ce service pour obtenir un EndpointAddress10.

    3. Utilisez EndpointAddress10 pour créer un canal pour obtenir un objet sessionful.

    4. Interagissez avec l’objet sessionful pour illustrer qu’il reste la même instance entre plusieurs appels.

    ChannelFactory<ISessionBoundFactory> channelFactory =
        new ChannelFactory<ISessionBoundFactory>("factory");  
    ISessionBoundFactory sessionFactory = channelFactory.CreateChannel();  
    
    EndpointAddress10 address1 = sessionFactory.GetInstanceAddress();  
    EndpointAddress10 address2 = sessionFactory.GetInstanceAddress();  
    
    ChannelFactory<ISessionBoundObject> sessionObjectFactory1 =
        new ChannelFactory<ISessionBoundObject>(new NetTcpBinding(),
                                                address1.ToEndpointAddress());  
    ChannelFactory<ISessionBoundObject> sessionObjectFactory2 =
        new ChannelFactory<ISessionBoundObject>(new NetTcpBinding(),
                                                address2.ToEndpointAddress());  
    
    ISessionBoundObject sessionInstance1 = sessionObjectFactory1.CreateChannel();  
    ISessionBoundObject sessionInstance2 = sessionObjectFactory2.CreateChannel();  
    
    sessionInstance1.SetCurrentValue("Hello");  
    sessionInstance2.SetCurrentValue("World");  
    
    if (sessionInstance1.GetCurrentValue() == "Hello" &&  
        sessionInstance2.GetCurrentValue() == "World")  
    {  
        Console.WriteLine("sessionful server object works as expected");  
    }  
    

WCF retourne toujours des objets par valeur, mais il est possible de prendre en charge l’équivalent de la sémantique par référence via l’utilisation de EndpointAddress10. Cela permet au client de demander une instance de service WCF avec session, après quoi elle peut interagir avec elle comme n’importe quel autre service WCF.

Scénario 3 : le client envoie un serveur à une instance de By-Value

Ce scénario illustre le client qui envoie une instance d’objet non primitive au serveur par valeur. Étant donné que WCF envoie uniquement des objets par valeur, ce scénario illustre l’utilisation normale de WCF.

  1. Utilisez le même service WCF à partir du scénario 1.

  2. Utilisez le client pour créer un objet par valeur (Customer), créer un canal pour communiquer avec le service ICustomerService et lui envoyer l’objet.

    ChannelFactory<ICustomerService> factory =  
        new ChannelFactory<ICustomerService>("customerservice");  
    ICustomerService service = factory.CreateChannel();  
    PremiumCustomer customer = new PremiumCustomer {
    FirstName = "Bob",
    LastName = "Jones",
    CustomerId = 43,
    AccountId = 99};  
    bool success = service.UpdateCustomer(customer);  
    Console.WriteLine($"  Server returned {success}.");
    

    L’objet client est sérialisé et envoyé au serveur, où il est désérialisé dans une nouvelle copie de cet objet.

    Remarque

    Ce code illustre également l’envoi d’un type dérivé (PremiumCustomer). L’interface de service attend un objet Customer, mais l’attribut [KnownType] sur la classe Customer indique que PremiumCustomer a également été autorisé. WCF échoue toute tentative de sérialisation ou de désérialisation d’un autre type via cette interface de service.

Les échanges WCF normaux de données sont par valeur. Cela garantit que l’appel de méthodes sur l’un de ces objets de données s’exécute uniquement localement , il n’appelle pas le code sur l’autre niveau. Bien qu’il soit possible d’obtenir quelque chose comme des objets de référence retournés par le serveur, il n’est pas possible qu’un client passe un objet de référence au serveur. Un scénario qui nécessite une conversation entre le client et le serveur peut être réalisé dans WCF à l’aide d’un service duplex. Pour plus d’informations, consultez Duplex Services.

Résumé

.NET Remoting est un framework de communication destiné à être utilisé uniquement dans des environnements entièrement approuvés. Il s’agit d’un produit hérité et pris en charge uniquement pour la compatibilité descendante. Il ne doit pas être utilisé pour générer de nouvelles applications. À l’inverse, WCF a été conçu avec une sécurité à l’esprit et est recommandé pour les applications nouvelles et existantes. Microsoft recommande que les applications de communication à distance existantes soient migrées pour utiliser WCF ou ASP.NET API web à la place.