Dynamic Wcf Hosting Using Reflection

My goal is to create a host application that can analyze multiple assemblies, discover contracts, and host services.

To load a service, we usually need to hardcode the servicehost instance. The following code works, even though this is not the behavior I'm looking for.

ServiceHost wService1Host = new ServiceHost(typeof(Service1)); wService1Host.Open(); ServiceHost wService2Host = new ServiceHost(typeof(Service2)); wService2Host.Open(); 

However, this means that I know in advance what services will be. I do not mind having a link to assemblies containing services. I just want the host not to know what services are contained in the assemblies. For example, if I add a new service to one of the assemblies, no changes will be required on the host side.

This is very similar to question , but with additional complexity for the above reason.

Here is the host code I came to. I am not opposed to managing services at the moment, I just want them to be downloaded properly.

 class Program { static void Main(string[] args) { // 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); // I've hardcoded the name of the assembly containing the services only to ease debugging if (assemblyToInspect != null && assemblyToInspect.GetName().Name == "WcfServices") { // find all types Type[] types = assemblyToInspect.GetTypes(); // enumerate types and determine if this assembly contains any types of interest // you could eg 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 (eg the list of types you need to find from // parsing the app.config file) foreach (Type ty in types) { Assembly implementationAssembly = Assembly.GetAssembly(ty); // When loading the type for the service, load it from the implementing assembly. Type implementation = implementationAssembly.GetType(ty.FullName); ServiceHost wServiceHost = new ServiceHost(implementation); // FAIL wServiceHost.Open(); } } } Console.WriteLine("Service are up and running."); Console.WriteLine("Press <Enter> to stop services..."); Console.ReadLine(); } } 

When I try to create a serviceHost, I get the following error:

 "It is illegal to reflect on the custom attributes of a Type loaded via ReflectionOnlyGetType (see Assembly.ReflectionOnly) -- use CustomAttributeData instead." 

In the link above, the guy seems to have solved his problem using typeof, since he knows in advance what service he wants to expose. Unfortunately, this is not my business.

Note. For the hosting part, I actually have 3 projects. The first of these is the host application (see above), the second is the assembly containing my entire service contract (interfaces), and the last assembly contains the implementation of services.

Here is the app.config application that I actually use to host the services. The assembly containing the implementation is called "WcfServices" and contains 2 services. One of them displays callbacks and other basic services.

 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="metadataBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="WcfServices.Service1" behaviorConfiguration="metadataBehavior"> <endpoint address="Service1Service" binding="basicHttpBinding" contract="WcfServices.IService1" name="basicHttp"/> <endpoint binding="mexHttpBinding" contract="IMetadataExchange" name="metadataExchange"/> <host> <baseAddresses> <add baseAddress="http://localhost:8000/Service1"/> </baseAddresses> </host> </service> <service name="WcfServices.Service2" behaviorConfiguration="metadataBehavior"> <endpoint address="Service2Service" binding="wsDualHttpBinding" contract="WcfServices.IService2"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> <host> <baseAddresses> <add baseAddress="http://localhost:8000/Service2"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration> 

So, to be clear, here is what I am looking for:
1. Load assemblies in the current application directory
2. Understands if there are any contracts in it.

3. If so, create these services (using app.config for now)

First, is this possible? (My guess would be like this, since an application called wcfstorm alread looks like this)
Obviously, how can I make the code above work?

Thanks!

+4
source share
1 answer

Here is what I ended up doing:

 private static void LoadServices() { // find currently executing assembly Assembly Wcurr = Assembly.GetExecutingAssembly(); // get the directory where this app is running in string wCurrentLocation = Path.GetDirectoryName(Wcurr.Location); // enumerate over those assemblies foreach (string wAssemblyName in mAssemblies) { // load assembly just for inspection Assembly wAssemblyToInspect = null; try { wAssemblyToInspect = Assembly.LoadFrom(wCurrentLocation + "\\" + wAssemblyName); } catch (System.Exception ex) { Console.WriteLine("Unable to load assembly : {0}", wAssemblyName); } if (wAssemblyToInspect != null) { // find all types with the HostService attribute IEnumerable<Type> wTypes = wAssemblyToInspect.GetTypes().Where(t => Attribute.IsDefined(t, typeof(HostService), false)); foreach (Type wType in wTypes) { ServiceHost wServiceHost = new ServiceHost(wType); wServiceHost.Open(); mServices.Add(wServiceHost); Console.WriteLine("New Service Hosted : {0}", wType.Name); } } } Console.WriteLine("Services are up and running."); } 

Note. This approach requires the master project to reference the assemblies.

Note 2. To speed up parsing of an assembly, I hard-coded which assemblies to load into mAssemblies.

+4
source

Source: https://habr.com/ru/post/1441596/


All Articles