2012-10-23 30 views
4

我的目標是創建一個主機應用程序,能夠解析多個程序集,檢測合同並承載服務。Wcf動態主機使用反射

爲了加載服務,我們通常需要對servicehost實例進行硬編碼。儘管不是我正在尋找的行爲,但以下代碼仍在工作。

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

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

但是,這意味着我事先知道服務是什麼。 我不介意引用包含這些服務的程序集。我只想讓主機不知道組件中包含哪些服務。例如,如果我向其中一個程序集添加新的服務,則主機端不需要進行任何更改。

這與question非常相似,但由於上述原因而增加了複雜性。

這是我到目前爲止的主機代碼。我現在不介意管理服務,我只是希望他們能夠正確加載。

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

我在嘗試創建的ServiceHost如果出現以下錯誤:

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

在上面給出的鏈接,這個傢伙似乎因爲他事先什麼服務知道已經解決了使用typeof的問題,他想要揭穿。不幸的是,這不是我的情況。

注意:對於託管部分,我實際上有3個項目。第一個是宿主應用程序(見上文),第二個是包含我所有服務合同(接口)的程序集,最後一個程序集包含服務實現。

這是我實際用於託管服務的app.config。包含該實現的程序集名爲「WcfServices」幷包含2個服務。一個是公開回調,另一個是基本服務。

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

因此,要明確,這裏就是我在尋找:在當前app目錄
1.裝載組件
2.外貌是否有它
3.如果任何合同執行有是,實例化那些服務(使用app.config的那一刻)

首先,這甚至有可能嗎? (我的猜測是,因爲名爲wcfstorm alread的應用程序似乎是這樣做的)
很明顯,我怎樣才能使代碼高於作品?

謝謝!

+0

可怕的解決方案,但...您可以使用激活器來創建實現的實例,然後通過在該實例上調用GetType將該類型傳遞給ServiceHost。 – RobH

+0

@Munchies通過添加以下內容:'var test = Activator.CreateInstance(implementation);'我收到錯誤「請求的操作在ReflectionOnly上下文中無效。 另外,它不是相當低效實例化每個服務兩次嗎? (通過創建ServiceHost對象,我相信創建了一個新的服務實例) – Sim

+0

我剛剛發現了這個問題。您只在僅反射的上下文中加載程序集。用Assembly.Load或Assembly.LoadFrom加載你的程序集,你的原始代碼應該可以工作。 – RobH

回答

4

這裏是我落得這樣做:

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

注意:此方法需要通過組件的「主機」項目引用。注2:爲了加速程序集解析,我硬編碼了哪些程序集要加載到「mAssemblies」中。