Posso ospitare automaticamente tutti i servizi in app.config quando si utilizza l'auto -osting?

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

Domanda

Sto scrivendo un'applicazione che deve ospitare diversi servizi WCF. Uno dei punti di forza di WCF è la possibilità di configurare i servizi senza dover ricompilare, specificando le impostazioni nel file app.config.

Durante l'auto-ospite, non sembra esserci un modo fuori dalla scatola per ospitare automaticamente i servizi che si trovano nel file app.config. ho trovato questa domanda che menziona una possibile soluzione di servizi elencati dinamicamente elencati in app.config in fase di esecuzione e creazione di un servizio di servizio per ciascuno.

Tuttavia, i miei servizi, i miei contratti e la domanda di hosting sono tutti in diversi assembly. Questo causa Type.GetType(string name) non riesce a individuare il mio tipo di servizio (restituisce null) perché è definito in un assembly diverso.

Come posso ospitare in modo affidabile tutti i servizi elencati nel file app.config dinamicamente (cioè, senza codifica sodo new ServiceHost(typeof(MyService)) Nella mia applicazione autosufficiente?

Nota: il mio app.config è stato generato utilizzando "WCF Configuration Editor" in Visual Studio 2010.

Nota anche: il mio obiettivo principale è quello di avere questo guidato dal file app.config in modo che esiste un singolo punto di configurazione. Non voglio configurarlo in una posizione separata.

MODIFICARE: Sono in grado di leggere il file app.config (vedi qui), ma è necessario essere in grado di risolvere i tipi in diversi assiemi.

MODIFICARE: Una delle risposte seguenti mi ha spinto a provare a specificare il Nome AssemblyQualified in App.Config anziché solo il nome del tipo di base. Questo è stato in grado di aggirare il Type.GetType() problema, tuttavia ServiceHost.Open() ora fallisce con un InvalidOperationException Indipendentemente da come raggiungo il tipo:

// 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

Dettagli dell'eccezione:

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.

Immagino che WCF tenta di abbinare la stringa letterale per il nome del servizio quando si analizza il file app.config internamente.

Modifica/risposta: Quello che ho finito per fare era fondamentalmente ciò che era nella risposta qui sotto. Invece di usare Type.GetType() So che tutti i miei servizi sono nella stessa assieme, quindi sono passato a:

// 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);
È stato utile?

Soluzione

Hai identificato correttamente la risposta al tuo problema nel link alla domanda e anche la risposta @marc_s fornisce l'approccio corretto. Il problema effettivo che stai riscontrando è che è necessario ottenere dinamicamente l'istanza di tipo di un assembly che può essere referenziato solo tramite un file di configurazione, quindi non può essere caricato nell'appdomain corrente.

Guarda questo Post di blog per un modo per fare riferimento dinamicamente assemblee nel tuo codice. Sebbene il post sia specifico per un'applicazione ASP.NET, l'approccio generale dovrebbe funzionare in uno scenario autonomo. Le idee sono di sostituire la chiamata Type.getType (String) con una chiamata di metodo privato che carica dinamicamente il gruppo richiesto (se necessario) e restituisce l'oggetto Tipo. Il parametro che invia questo metodo sarà comunque l'elemento.name e dovrai capire qual è il gruppo corretto da caricare. Dovrebbe funzionare un semplice schema di denominazione dell'assemblea basata sulla convenzione. Ad esempio, se il tipo di servizio è:

Mynamespace.myservice.myserviceimpl

Quindi supponiamo che l'assemblea sia:

Mynamespace.myservice

Altri suggerimenti

    // 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();
    }

Sulla base delle altre risposte, ho esteso il codice a quanto segue, che cerca tutti gli assembly per i servizi in app.config

Dovrebbe assolutamente essere possibile! Dai un'occhiata a questo frammento di codice: usalo come base e vai da qui:

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);
    }
}

Questo in questo momento stampela solo le informazioni, ma potresti sicuramente usarlo per costruire effettivamente i tuoi host di servizio all'interno del tuo servizio NT!

Aggiornare: Ok, scusa, mi sono perso il tuo punto più importante: il fatto che i servizi reali siano in diversi assembly.

In tal caso, è necessario caricare dinamicamente quegli assiemi, se necessario: potresti essere solo "semplicemente sapere" quali assembly caricare, oppure potresti metterli tutti in una sottodirectory specifica e caricare tutti i gruppi in quella directory, oppure potresti semplicemente ispezionare tutti i gruppi nella stessa posizione in cui il tuo MyOwnServiceHost.exe risiede e controlla se trovi dei tipi di cui hai bisogno.

Questa parte - quale tipo di servizio trovare in quale assemblaggio - non è gestita dalla configurazione WCF - devi farlo da solo, con qualsiasi mezzo ha più senso per te.

// 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
      }
   }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top