c# - keys - ¿Puedo alojar automáticamente todos los servicios en app.config cuando uso SelfHosting?
c# web.config appsettings (3)
Estoy escribiendo una aplicación que necesita alojar varios servicios WCF. Una de las ventajas de WCF es la capacidad de configurar servicios sin tener que volver a compilar, especificando las configuraciones en el archivo app.config.
Cuando se autoejecuta, no parece haber una manera predeterminada de hospedar automáticamente los servicios que se encuentran en el archivo app.config. Encontré esta pregunta que menciona una posible solución de enumerar dinámicamente servicios enumerados en app.config en tiempo de ejecución y crear un ServiceHost para cada uno.
Sin embargo, mis servicios, contratos y la aplicación de alojamiento están en diferentes conjuntos. Esto hace que Type.GetType(string name)
no pueda localizar mi tipo de servicio (devuelve null
) porque está definido en un ensamblaje diferente.
¿Cómo puedo hospedar de manera confiable todos los servicios enumerados en el archivo app.config de forma dinámica (es decir, sin una new ServiceHost(typeof(MyService))
codificación de new ServiceHost(typeof(MyService))
en mi aplicación de alojamiento autónomo?
Nota: Mi app.config se generó usando el "Editor de configuración de WCF" en Visual Studio 2010.
Tenga en cuenta también: Mi objetivo principal es tener esto impulsado por el archivo app.config para que haya un único punto de configuración. No quiero tener que configurar esto en una ubicación separada.
EDITAR : puedo leer el archivo app.config (ver aquí ), pero necesito poder resolver los tipos en diferentes ensamblajes.
EDIT : una de las respuestas a continuación me indicó que intentara especificar AssemblyQualifiedName en app.config en lugar de solo el nombre de tipo básico. Esto fue capaz de evitar el problema Type.GetType()
, sin embargo, ServiceHost.Open()
ahora falla con una InvalidOperationException
independientemente de cómo logre el 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
Detalles de la excepción:
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.
Supongo que WCF intenta hacer coincidir la cadena literal del nombre del servicio al analizar internamente el archivo app.config.
EDITAR / RESPUESTA : Lo que terminé haciendo fue básicamente lo que estaba en la respuesta a continuación. En lugar de usar Type.GetType()
, sé que todos mis servicios están en el mismo ensamblado, así que cambié 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);
¡Eso definitivamente debería ser posible! Mira este fragmento de código: úsalo como base y ve desde aquí:
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);
}
}
Esto ahora solo imprime la información, ¡pero definitivamente podría usar esto para construir sus servidores de servicio dentro de su propio servicio NT!
Actualización: bien, lo siento, me perdí su punto más importante: el hecho de que los servicios reales se encuentran en diferentes conjuntos.
En ese caso, necesita cargar dinámicamente esos ensambles, según sea necesario; podría, por ejemplo, "simplemente saber" qué ensamblajes cargar, o podría ponerlos todos en subdirectorios específicos y cargar todos los ensamblajes en ese directorio, o podría simplemente inspeccione todos los ensamblajes en el mismo lugar donde reside MyOwnServiceHost.exe
y verifique si encuentra los tipos que necesita.
Esta parte, que tipo de servicio encontrar en qué ensamblaje, no es manejada por la configuración de WCF, debe hacer esto usted mismo, de la forma que le parezca más lógica.
// 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
}
}
}
Identificó correctamente la respuesta a su problema en el enlace de su pregunta y la respuesta de @marc_s también proporciona el enfoque correcto también. El problema real que tiene es que necesita obtener dinámicamente la instancia de Tipo de un ensamblaje al que solo se puede hacer referencia a través de un archivo de configuración, por lo que no se puede cargar en el Dominio de aplicación actual.
Mire esta publicación de blog para conocer de manera dinámica los ensamblajes en su código. Aunque la publicación es específicamente para una aplicación ASP.NET, el enfoque general debería funcionar en un escenario autogestionado. La idea es reemplazar la llamada Type.GetType (cadena) con una llamada a método privado que carga dinámicamente el ensamblado solicitado (si es necesario) y devuelve el objeto Type. El parámetro que envíe este método seguirá siendo el elemento.Name y tendrá que averiguar cuál es el ensamblaje correcto para cargar. Un simple esquema de nombramiento de ensamblaje basado en convenciones debería funcionar. Por ejemplo, si el tipo de servicio es:
MyNamespace.MyService.MyServiceImpl
entonces suponga que el conjunto es:
MyNamespace.MyService
// 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();
}
En función de las otras respuestas, extendí el código a lo siguiente, que busca todos los ensamblados para los servicios en la aplicación.config