Puis-je héberger automatiquement tous les services dans app.config lors de l'utilisation de l'auto-hosting?

StackOverflow https://stackoverflow.com/questions/5993563

Question

J'écris une application qui doit héberger plusieurs services WCF. L'une des forces de WCF est la possibilité de configurer les services sans avoir à se recompiler, en spécifiant les paramètres du fichier app.config.

Lors de l'auto-hébergement, il ne semble pas y avoir de moyen prêt à l'emploi pour héberger automatiquement les services qui se trouvent dans le fichier app.config. j'ai trouvé cette question qui mentionne une solution possible d'énumération dynamiquement énumérée dans App.config au moment de l'exécution et de la création d'un servicehost pour chacun.

Cependant, mes services, mes contrats et la demande d'hébergement sont tous dans différents assemblages. Ce qui provoque Type.GetType(string name) Pour ne pas localiser mon type de service (retourne null) car il est défini dans un assemblage différent.

Comment puis-je héberger de manière fiable tous les services répertoriés dans le fichier app.config dynamiquement (c'est-à-dire sans codage dur new ServiceHost(typeof(MyService)) Dans mon application d'auto-hébergement?

Remarque: mon appl.config a été généré à l'aide de "WCF Configuration Editor" dans Visual Studio 2010.

Remarque également: Mon objectif principal est d'avoir cela piloté par le fichier app.config, il y a donc un seul point de configuration. Je ne veux pas avoir à configurer cela dans un emplacement séparé.

ÉDITER: Je suis en mesure de lire le fichier app.config (voir ici), mais il faut pouvoir résoudre les types dans différents assemblages.

ÉDITER: L'une des réponses ci-dessous m'a incité à essayer de spécifier le nom d'assemblyqualified dans app.config au lieu du nom de type de base. Cela a pu contourner le Type.GetType() Cependant, le problème ServiceHost.Open() maintenant échoue avec un InvalidOperationException Quelle que soit la façon dont j'atteins le type:

// Fails
string typeName = typeof(MyService).AssemblyQualifiedName;
Type myType = Type.GetType(typeName);
ServiceHost host = new ServiceHost(myType);
host.Open(); // throws InvalidOperationException

// Also fails
Type myType2 = typeof(MyService);
ServiceHost host2 = new ServiceHost(myType2);
host2.Open(); // throws InvalidOperationException

Détails de l'exception:

Service 'SO.Example.MyService' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

Je suppose que WCF tente de faire correspondre la chaîne littérale pour le nom du service lors de l'analyse du fichier app.config en interne.

Modifier / répondre: Ce que j'ai fini par faire, c'est essentiellement ce qui était dans la réponse ci-dessous. À la place d'utiliser Type.GetType() Je sais que tous mes services sont dans le même assemblage, alors je suis passé à:

// Get a reference to the assembly which contain all of the service implementations.
Assembly implementationAssembly = Assembly.GetAssembly(typeof(MyService));
...
// When loading the type for the service, load it from the implementing assembly.
Type implementation = implementationAssembly.GetType(serviceElement.Name);
Était-ce utile?

La solution

Vous avez correctement identifié la réponse à votre problème dans votre lien de questions et la réponse @MARC_S donne également l'approche correcte. Le problème réel que vous rencontrez est que vous devez obtenir dynamiquement l'instance de type d'un assemblage qui ne peut être référencé que via un fichier de configuration afin qu'il ne soit pas chargé dans l'approomain actuel.

Regarde ça Article de blog pour un moyen de référencer dynamiquement les assemblées dans votre code. Bien que le message soit spécifiquement pour une application ASP.NET, l'approche générale devrait fonctionner dans un scénario auto-hébergé. L'idées consiste à remplacer l'appel Type.getType (String) par un appel de méthode privé qui charge dynamiquement l'assemblage demandé (si nécessaire) et renvoie l'objet Type. Le paramètre que vous envoyez cette méthode sera toujours l'élément.nom et vous devrez déterminer quel est le bon assemblage à charger. Un schéma de dénomination d'assemblage basé sur des congrès simples devrait fonctionner. Par exemple, si le type de service est:

MyNamespace.myservice.myserviceImpl

Supposons alors que l'assemblage est:

MyNamespace.myservice

Autres conseils

    // get the <system.serviceModel> / <services> config section
    ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;

    // get all classs
    var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(s => s.GetTypes()).Where(t => t.IsClass == true);

    // enumerate over each <service> node
    foreach (ServiceElement service in services.Services)
    {
        Type serviceType = allTypes.SingleOrDefault(t => t.FullName == service.Name);
        if (serviceType == null)
        {
            continue;
        }

        ServiceHost serviceHost = new ServiceHost(serviceType);
        serviceHost.Open();
    }

Sur la base des autres réponses, j'ai étendu le code à ce qui suit, qui recherche tous les assemblages pour les services de l'application.config

Cela devrait certainement être possible! Consultez ce fragment de code - utilisez-le comme fondation et allez à partir d'ici:

using System.Configuration;   // don't forget to add a reference to this assembly!

// get the <system.serviceModel> / <services> config section
ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;

// enumerate over each <service> node
foreach(ServiceElement aService in services.Services)
{
    Console.WriteLine();
    Console.WriteLine("Name: {0} / Behavior: {1}", aService.Name, aService.BehaviorConfiguration);

    // enumerate over all endpoints for that service
    foreach (ServiceEndpointElement see in aService.Endpoints)
    {
        Console.WriteLine("\tEndpoint: Address = {0} / Binding = {1} / Contract = {2}", see.Address, see.Binding, see.Contract);
    }
}

Ceci en ce moment imprime les informations - mais vous pouvez certainement l'utiliser pour construire vos hôtes de service dans votre propre service NT!

Mise à jour: Ok, désolé, j'ai raté votre point le plus important - le fait que les services réels se trouvent dans différents assemblages.

Dans ce cas, vous devez charger dynamiquement ces assemblages, selon les besoins - vous pouvez par exemple "simplement savoir" quels assemblages à charger, ou vous pouvez tous les mettre dans des sous-répertoires spécifiques et charger tous les assemblages dans ce répertoire, ou vous pouvez simplement Inspectez tous les assemblages au même endroit où votre MyOwnServiceHost.exe réside et vérifiez si vous trouvez des types dont vous avez besoin.

Cette partie - quel type de service trouver dans quel assemblage - n'est pas géré par la configuration WCF - vous devez le faire vous-même, selon le moyen que cela soit le plus logique pour vous.

// find currently executing assembly
Assembly curr = Assembly.GetExecutingAssembly();

// get the directory where this app is running in
string currentLocation = Path.GetDirectoryName(curr.Location);

// find all assemblies inside that directory
string[] assemblies = Directory.GetFiles(currentLocation, "*.dll");

// enumerate over those assemblies
foreach (string assemblyName in assemblies)
{
   // load assembly just for inspection
   Assembly assemblyToInspect = Assembly.ReflectionOnlyLoadFrom(assemblyName);

   if (assemblyToInspect != null)
   {
      // find all types
      Type[] types = assemblyToInspect.GetTypes();

      // enumerate types and determine if this assembly contains any types of interest
      // you could e.g. put a "marker" interface on those (service implementation)
      // types of interest, or you could use a specific naming convention (all types
      // like "SomeThingOrAnotherService" - ending in "Service" - are your services)
      // or some kind of a lookup table (e.g. the list of types you need to find from
      // parsing the app.config file)
      foreach(Type ty in types)
      {
         // do something here
      }
   }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top