我想获得一些建议。我正在开发一个系统,它将在运行时加载插件,并要求它们通过WCF端点可用。C#WCF插件设计与实现
我将有一个MVC 3 Web应用程序仅用于配置,而一个类库(核心)将加载不同的插件。
我将不胜感激关于如何去做这个的一些指导。我想加载插件,然后能够创建一个WCF端点,该端点通过IIS 7注册以访问该插件。
感谢提前:)
我想获得一些建议。我正在开发一个系统,它将在运行时加载插件,并要求它们通过WCF端点可用。C#WCF插件设计与实现
我将有一个MVC 3 Web应用程序仅用于配置,而一个类库(核心)将加载不同的插件。
我将不胜感激关于如何去做这个的一些指导。我想加载插件,然后能够创建一个WCF端点,该端点通过IIS 7注册以访问该插件。
感谢提前:)
使用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());
感谢马修,我会放弃这一切。我的解决方案最终有点类似,但这是一个更好的实现 – 2011-04-29 06:05:42
所以你发现MEF的插件是否正确?每个插件实现一个特定的接口?通过“创建一个注册到IIS 7以访问该插件的WCF端点”,你到底意味着什么? – BrandonZeider 2011-04-27 15:33:14
那么我最终做的是在实际的插件中,定义一个WCF联系人和所有的作品,并为插件启动一个端点。我们理想的做法是尝试将该端点注册到IIS 7中。但看起来我会按照目前的方式进行操作。 – 2011-04-28 14:09:57
你可以自己主持,这会让你最终控制发现的端点。 – BrandonZeider 2011-04-28 14:23:26