2011-04-27 57 views
7

我想獲得一些建議。我正在開發一個系統,它將在運行時加載插件,並要求它們通過WCF端點可用。C#WCF插件設計與實現

我將有一個MVC 3 Web應用程序僅用於配置,而一個類庫(核心)將加載不同的插件。

我將不勝感激關於如何去做這個的一些指導。我想加載插件,然後能夠創建一個WCF端點,該端點通過IIS 7註冊以訪問該插件。

感謝提前:)

+0

所以你發現MEF的插件是否正確?每個插件實現一個特定的接口?通過「創建一個註冊到IIS 7以訪問該插件的WCF端點」,你到底意味着什麼? – BrandonZeider 2011-04-27 15:33:14

+0

那麼我最終做的是在實際的插件中,定義一個WCF聯繫人和所有的作品,併爲插件啓動一個端點。我們理想的做法是嘗試將該端點註冊到IIS 7中。但看起來我會按照目前的方式進行操作。 – 2011-04-28 14:09:57

+0

你可以自己主持,這會讓你最終控制發現的端點。 – BrandonZeider 2011-04-28 14:23:26

回答

11

使用Darko's Dynamic IIS hosted WCF Service工作的衍生物,可以達到你想要的東西。讓我們先從我們可能要舉辦一個示例服務,我們把它叫做一個IMessageBroker,它的合同很簡單:

[ServiceContract] 
public interface IMessageBroker 
{ 
    [OperationContract] 
    string Send(string message); 
} 

我們使用這個合同無論是服務,而MEF出口/進口。我們還將定義了一些額外的元數據與它一起去:

public interface IMessageBrokerMetadata 
{ 
    public string Name { get; } 
    public string Channel { get; } 
} 

由於這是一個簡單的項目,我會欺騙和使用一個簡單的靜態類,用於管理組成部分的MEF CompositionContainer

public static class MEF 
{ 
    private static CompositionContainer container; 
    private static bool initialised; 

    public static void Initialise() 
    { 
     var catalog = new DirectoryCatalog("bin"); 
     container = new CompositionContainer(catalog); 
     initialised = true; 
    } 

    public static CompositionContainer Container 
    { 
     get 
     { 
      if (!initialised) Initialise(); 
      return container; 
     } 
    } 
} 

爲了能夠動態生成WCF服務,我們需要創建一個可以訪問我們的組合容器來訪問我們的A型ServiceHostFactory,所以你可以這樣做:

public class MEFServiceHostFactory : ServiceHostFactory 
{ 
    public override ServiceHostBase CreateServiceHost(string constructorString, System.Uri[] baseAddresses) 
    { 
     var serviceType = MEF.Container 
      .GetExports<IMessageBroker, IMessageBrokerMetadata>() 
      .Where(l => l.Metadata.Name == constructorString) 
      .Select(l => l.Value.GetType()) 
      .Single(); 

     var host = new ServiceHost(serviceType, baseAddresses); 

     foreach (var contract in serviceType.GetInterfaces()) 
     { 
      var attr = contract.GetCustomAttributes(typeof(ServiceContractAttribute), true).FirstOrDefault(); 
      if (attr != null) 
       host.AddServiceEndpoint(contract, new BasicHttpBinding(), ""); 
     } 

     var metadata = host.Description.Behaviors 
      .OfType<ServiceMetadataBehavior>() 
      .FirstOrDefault(); 

     if (metadata == null) 
     { 
      metadata = new ServiceMetadataBehavior(); 
      metadata.HttpGetEnabled = true; 
      host.Description.Behaviors.Add(metadata); 
     } 
     else 
     { 
      metadata.HttpGetEnabled = true; 
     } 

     return host; 
    } 
} 

實質上,constructorString參數用於傳遞特定服務所需的元數據名稱。接下來,我們需要處理這些服務的定位。我們現在需要的是VirtualPathProvider,我們可以通過VirtualFile來動態創建實例。該供應商將是這樣的:

public class ServiceVirtualPathProvider : VirtualPathProvider 
{ 
    private bool IsServiceCall(string virtualPath) 
    { 
     virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); 
     return (virtualPath.ToLower().StartsWith("~/services/")); 
    } 

    public override VirtualFile GetFile(string virtualPath) 
    { 
     return IsServiceCall(virtualPath) 
        ? new ServiceFile(virtualPath) 
        : Previous.GetFile(virtualPath); 
    } 

    public override bool FileExists(string virtualPath) 
    { 
     if (IsServiceCall(virtualPath)) 
      return true; 

     return Previous.FileExists(virtualPath); 
    } 

    public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart) 
    { 
     return IsServiceCall(virtualPath) 
        ? null 
        : Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); 
    } 
} 

我們正在做的,被映射到/Services/我們MEF來源的端點的呼叫。該服務需要一個虛擬文件,而這正是我們將其結合在一起:

public class ServiceFile : VirtualFile 
{ 
    public ServiceFile(string virtualPath) : base(virtualPath) 
    { 

    } 

    public string GetName(string virtualPath) 
    { 
     string filename = virtualPath.Substring(virtualPath.LastIndexOf("/") + 1); 
     filename = filename.Substring(0, filename.LastIndexOf(".")); 

     return filename; 
    } 

    public override Stream Open() 
    { 
     var stream = new MemoryStream(); 
     var writer = new StreamWriter(stream); 

     writer.Write("<%@ ServiceHost Language=\"C#\" Debug=\"true\" Service=\"" + GetName(VirtualPath) + 
        "\" Factory=\"Core.MEFServiceHostFactory, Core\" %>"); 
     writer.Flush(); 

     stream.Position = 0; 
     return stream; 
    } 
} 

虛擬文件會從虛擬路徑,其中/Services/SampleMessageBroker.svc打出來的元數據的名稱 - >SampleMessageBroker。然後,我們生成一些標記,它代表Service="SampleMessageBroker"代表.svc文件的標記。這個參數將被傳遞給MEFServiceHostFactory,我們可以選擇端點。所以,對於一個樣本端點:

[Export(typeof(IMessageBroker)), 
ExportMetadata("Name", "SampleMessageBroker"), 
ExportMetadata("Channel", "Greetings")] 
public class SampleMessageBroker : IMessagerBroker 
{ 
    public string Send(string message) 
    { 
    return "Hello! " + message; 
    } 
} 

我們現在可以訪問動態的/Services/SampleMessageBroker.svc。你可能想要做的是提供一個靜態服務,它允許你將可用的端點進行集成,並將其反饋給消費客戶端。

哦,別忘了要連接的虛擬路徑提供:

HostingEnvironment.RegisterVirtualPathProvider(new ServiceVirtualPathProvider()); 
+0

感謝馬修,我會放棄這一切。我的解決方案最終有點類似,但這是一個更好的實現 – 2011-04-29 06:05:42