web-dev-qa-db-fra.com

Exécuter WCF ServiceHost avec plusieurs contrats

L'exécution d'un ServiceHost avec un seul contrat fonctionne comme suit:

servicehost = new ServiceHost(typeof(MyService1));
servicehost.AddServiceEndpoint(typeof(IMyService1), new NetTcpBinding(), "net.tcp://127.0.0.1:800/MyApp/MyService1");
servicehost.Open();

J'aimerais maintenant ajouter un deuxième contrat (3ème, 4ème, ...). Ma première hypothèse serait d'ajouter simplement d'autres points de terminaison comme celui-ci:

servicehost = new ServiceHost(typeof(MyService1));
servicehost.AddServiceEndpoint(typeof(IMyService1), new NetTcpBinding(), "net.tcp://127.0.0.1:800/MyApp/MyService1");
servicehost.AddServiceEndpoint(typeof(IMyService2), new NetTcpBinding(), "net.tcp://127.0.0.1:800/MyApp/MyService2");
servicehost.Open();

Mais bien sûr, cela ne fonctionne pas, car lors de la création de ServiceHost, je peux passer soit MyService1 en tant que paramètre, soit MyService2. Je peux donc ajouter de nombreux points finaux à mon service, mais tous doivent utiliser le même contrat, car je ne peux une mise en œuvre?
.__ J'ai le sentiment que je manque le point, ici. Bien sûr, il doit y avoir un moyen de fournir une implémentation pour chaque contrat final que j'ajoute, ou pas?

51
Sam

Vous devez implémenter les deux services (interfaces) dans la même classe.

servicehost = new ServiceHost(typeof(WcfEntryPoint));
servicehost.Open(); 

public class WcfEntryPoint : IMyService1, IMyService2
{
    #region IMyService1
    #endregion

    #region IMyService2
    #endregion
}

FYI: J'utilise fréquemment des classes partielles pour rendre mon code de classe hôte plus facile à lire:

// WcfEntryPoint.IMyService1.cs
public partial class WcfEntryPoint : IMyService1
{
    // IMyService1 methods
}

// WcfEntryPoint.IMyService2.cs
public partial class WcfEntryPoint : IMyService2
{
    // IMyService2 methods
}
59
chilltemp

Je suis actuellement confronté au même problème et j'ai décidé de procéder à la mise en œuvre ci-dessous. Je ne sais pas s'il y a des problèmes de performances avec autant de contrats de service, mais lors de ma dernière implémentation, j'aurai probablement environ 10 à 15 contrats de service, donc environ 10 à 15 ServiceHosts.

J'héberge tous mes services WCF dans un seul service Windows.

private void PublishWcfEndpoints()
{
    var mappings = new Dictionary<Type, Type>
    {
       {typeof (IAuthenticationService), typeof (AuthenticationService)},
       {typeof(IUserService), typeof(UserService)},
       {typeof(IClientService), typeof(ClientService)}
    };


    foreach (var type in mappings)
    {
        Type contractType = type.Key;
        Type implementationType = type.Value;

        ServiceHost serviceHost = new ServiceHost(implementationType);
        ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(contractType, ServiceHelper.GetDefaultBinding(),
                                                                  Properties.Settings.Default.ServiceUrl  + "/" + contractType.Name);
        endpoint.Behaviors.Add(new ServerSessionBehavior());

        ServiceDebugBehavior serviceDebugBehaviour =
            serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
        serviceDebugBehaviour.IncludeExceptionDetailInFaults = true;

        log.DebugFormat("Published Service endpoint: {0}", Properties.Settings.Default.ServiceUrl);

        serviceHost.Open();
        serviceHosts.Add(serviceHost);
    }

}

N'hésitez pas à commenter ce type d'installation et les éventuels problèmes, notamment ceux liés aux performances.

17
Saajid Ismail

Cette réponse est une réponse supplémentaire au commentaire dans la réponse acceptée de chilltemp.

Sam, vous devriez vraiment déterminer pourquoi vous avez besoin de contrats 10-50 et essayer de trouver une autre solution. J'ai examiné les normes de codage WCF de Juval Lowy (disponibles sur http://www.idesign.net/ ) et trouvé les références suivantes: 

3 Contrats de service ... 4. Évitez les contrats avec un membre . 5. S'efforcer d'avoir trois à cinq membres par contrat de service . 6. Ne pas avoir plus de vingt membres par contrat de service. Douze est probablement la limite pratique.

Il ne mentionne pas de limite sur les mises en œuvre de contrats (que je peux trouver) mais je ne peux pas l'imaginer en train de considérer 50 contrats sur un service comme quelque chose qui ressemble à une meilleure pratique. Une solution que j'ai trouvée qui fonctionne bien consiste à utiliser le partage de membres pour des fonctions similaires.

Par exemple, si vous utilisez le service WCF pour effectuer des calculs sur 2 valeurs, vous pouvez avoir 4 membres du côté service: Ajouter (x, y), Soustraire (x, y), Multiplier (x, y), Diviser (x , y). Si vous les combinez en un membre plus générique et utilisez un objet pour transmettre les données nécessaires, vous pouvez facilement réduire le nombre de vos membres et augmenter l'évolutivité. Exemple: PeformCalculation (obj) où obj a les propriétés x, y et action (addition, soustraction, multiplication, division).

J'espère que cela t'aides.

10
Chris Porter

J'ai trouvé une autre solution à ce problème en utilisant la classe RoutingService . Chaque contrat doit toujours être hébergé dans sa propre variable ServiceHost, mais il peut y avoir une RoutingService assise dessus - et les présentant sous un «point final» unifié. J'ai aussi écrit un article codeproject à ce sujet. L'exemple de code est également disponible sur Bitbucket .

8
m0sa

la réponse de chili fonctionnera si vous êtes d'accord avec les contrats partagés par le service. Si vous voulez les séparer, essayez ceci:

Host1 = new ServiceHost(typeof(MyService1));
Host2 = new ServiceHost(typeof(MyService2));

Host1.Open();
Host2.Open();

public class MyService1 : IMyService1
{
    #region IMyService1
    #endregion
}

public class MyService2 : IMyService2
{
    #region IMyService2
    #endregion
}

Edit: comme Matt l'a signalé, plusieurs points d'extrémité seraient nécessaires pour chaque service/contrat.

6
Chris Porter

Personne enpoint documenté. Si vous en utilisez plus d'un (en tant que groupe, à partir d'une URL commune, par exemple http), vous devez utiliser la même instance de liaison (pas plus), à savoir.

Votre échantillon:

servicehost = new ServiceHost(typeof(MyService1));
servicehost.AddServiceEndpoint(typeof(IMyService1), new NetTcpBinding(), "net.tcp://127.0.0.1:800/MyApp/MyService1");
servicehost.AddServiceEndpoint(typeof(IMyService2), new NetTcpBinding(), "net.tcp://127.0.0.1:800/MyApp/MyService2");
servicehost.Open();

devrait être un seul nouveau Binding (), j'ai testé sur http.

servicehost = new ServiceHost(typeof(MyService1));
 BasicHttpBinding binding = new BasicHttpBinding();
servicehost.AddServiceEndpoint(typeof(IMyService1),binding , "http://127.0.0.1:800/MyApp/MyService1");
servicehost.AddServiceEndpoint(typeof(IMyService2), binding, "http://127.0.0.1:800/MyApp/MyService2");
servicehost.Open();

Je suis totalement d'accord avec la classe partielle implémentant peu de contrats dans quelques fichiers.

2
Jacek Cz

Pourquoi ne pas le scinder avec une adresse de base et plusieurs services/contrats en dessous?

http: // myserver/myservices/serviceA
http: // myserver/myservices/serviceB
http: // myserver/myservices/serviceC

Chaque service implémentant son propre ServiceContract.

Vous pouvez changer
public class WcfEntryPoint : IMyService1, IMyService2
à
public partial class WcfEntryPoint : IMyService1
public partial class WcfEntryPoint : IMyService2

Exemple

1
Michel van Engelen

Ai-je oublié quelque chose ou la solution la plus simple n'est-elle pas mentionnée ici? La solution la plus simple est la suivante: n'utilisez pas plusieurs interfaces pour le service Web.

Mais cela ne signifie pas que vous pouvez toujours séparer vos interfaces. C'est pourquoi nous avons l'héritage d'interface.

[ServiceContract]
public interface IMetaSomeObjectService : ISomeObjectService1, ISomeObjectService2
{
}

La méta-interface hérite de toutes les autres interfaces.

[ServiceContract]
public interface ISomeOjectService1
{
    [OperationContract]
    List<SomeOject> GetSomeObjects();
}

[ServiceContract]
public interface ISomeOjectService2
{
    [OperationContract]
    void DoSomethingElse();
}

Ensuite, le service n'a plus que l'interface Meta.

public class SomeObjectService : IMetaSomeObjectService
{
   public List<SomeOject> GetSomeObjects()
   {
       // code here
   }

   public void DoSomethingElse()
   {
       // code here
   }
}
0
Rhyous