gpt4 book ai didi

C# WCF插件设计与实现

转载 作者:太空狗 更新时间:2023-10-29 20:08:34 25 4
gpt4 key购买 nike

我想得到一些建议。我正在开发一个系统,它将在运行时加载插件并要求它们通过 WCF 端点可用。

我将拥有一个仅真正用于配置的 MVC 3 Web 应用程序,以及一个将加载不同插件的类库(核心)。

我将不胜感激有关如何进行此操作的指导。我想加载插件,然后能够创建一个在 IIS 7 中注册的 WCF 端点以访问该插件。

提前致谢:)

最佳答案

使用 Darko's Dynamic IIS hosted WCF Service 的导数工作,你可以实现你想要的东西。让我们从我们可能想要托管的示例服务开始,我们将其称为 IMessageBroker,它的契约很简单:

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

我们将此契约(Contract)用于服务和 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;
}
}
}

为了能够动态生成 W​​CF 服务,我们需要创建一个 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());

关于C# WCF插件设计与实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5801406/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com