2012-10-23 5 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"이며 두 개의 서비스가 포함되어 있습니다. 하나는 콜백과 기타 기본 서비스 만 노출하는 것입니다.

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

그래서, 명확하게하기 위해, 여기에 내가 무엇을 찾고 있어요 : 그것은
3. 만약에 어떤 계약도 구현이있는 경우 현재 응용 프로그램 디렉토리에
1.로드 어셈블리
2. 외모가 그 순간에 app.config를 사용하여 해당 서비스를 인스턴스화하십시오.

우선 무엇이 가능합니까? (내 생각 엔 wcfstorm이라는 응용 프로그램이 이걸로 보이는 것 같습니다.
분명히 어떻게 위의 코드를 만들 수 있습니까?

감사합니다.

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

참고 :

+0

끔찍한 해결책이지만 ...액티베이터를 사용하여 구현의 인스턴스를 만든 다음 해당 인스턴스에서 GetType을 호출하여 ServiceHost에 유형을 전달할 수 있습니까? – RobH

+0

@Munchies 다음을 추가하면됩니다 :'var test = Activator.CreateInstance (implementation);'요청 된 작업은 ReflectionOnly 컨텍스트에서 유효하지 않습니다. "라는 오류 메시지가 나타납니다. 또한 각 서비스를 두 번 인스턴스화하는 것이 비효율적이지 않습니까? (ServiceHost 객체를 생성함으로써 서비스의 새로운 인스턴스가 생성된다고 생각합니다) – Sim

+0

방금 ​​문제를 발견했습니다. 리플렉션 전용 컨텍스트에서 어셈블리를로드하고 있습니다. Assembly.Load 또는 Assembly.LoadFrom을 사용하여 어셈블리를로드하면 원래 코드가 작동합니다. – RobH

답변

4

여기에 내가하고 결국 무엇 이러한 접근 방식은 어셈블리가 "호스트"프로젝트가 참조해야합니다.

주 2 : 어셈블리 구문 분석을 가속화하기 위해 "mAssemblies"에로드 할 어셈블리를 하드 코드했습니다.