1

我正在将服务的绑定从 Restful http 更改为 netTcp,用于 IIS 7 上托管的一组应用程序服务。restful 配置使用路由表,并根据命名约定和依赖注入动态创建端点. 由于我们现在需要在事务块 (DTS) 中调用这些服务,因此我们将这些服务转换为 Net Tcp 绑定。

我希望能够保持使用约定模型的路由和依赖注入,而不是有很多凌乱的 .svc 文件。我知道 ASP 路由表不适用于其他绑定。有什么替代方案的建议吗?自定义 HttpModule 可以做到吗?

  • 将对任何有用的信息进行投票或勾选答案或答案链接。

谢谢,亚伦

4

2 回答 2

4

不,没有直接等效于 net.tcp 绑定的 ASP.NET RouteTable。并且自定义 HttpModule 无济于事,因为这仅适用于 HTTP ;)

但是,您仍然可以做一些事情。首先,如果您不想为每个服务创建一个 .SVC 文件,您可以利用新的 (WCF 4) 基于配置的服务激活,它允许您在应用程序的 Web.config 中声明所有 WCF 服务(在 system.serviceModel 内部),如下所示:

<serviceHostingEnvironment>
  <serviceActivations>
    <add relativeAddress="Service1.svc" service="MyNamespace.MyService1"/>
    <add relativeAddress="Foo/Service2.svc" service="MyNamespace.MyService2"/>
    <add relativeAddress="Bar/a/Service3.svc" service="MyNamespace.MyService3" factory="System.ServiceModel.Activation.ServiceHostFactory"/>
  </serviceActivations>
</serviceHostingEnvironment>

笔记:

  • @factory 属性默认为ServiceHostFactory,即如果您使用另一种工厂类型(如 WebServiceHostFactory 或自定义),则只需指定它
  • 主要的问题是,@relativeAddress 必须包含扩展名!
  • 扩展必须在 system.web/compilation/buildProviders 中声明,并且在那里指定的类型必须具有 [ServiceActivationBuildProvider] 属性(System.ServiceModel.Activation)
  • 这意味着实际上您最终将使用该.svc扩展,因为该扩展已默认在根 Web.config 中声明 -

但是,这种方法至少允许您在一个地方定义所有服务(而不是多个 .svc文件) - 在这方面它与RouteTable.

现在,如果您对 .svc 扩展名感到困扰,您甚至可以更进一步。WCF4 中的另一个新项目是路由服务(请参阅http://msdn.microsoft.com/en-us/library/ee517423.aspx),它是一个通用 WCF 服务,它接收消息以便将它们转发到基于其他 WCF 服务根据各种标准(如 SOAP 标头、消息内容、端点地址等)。

使用路由服务,您可以定义各种 EndpointAddress 过滤器(例如,net.tcp://{routing-service-base}/service1、net.tcp://{routing-service-base}/service2 等) -这使您更接近 ASP.NET 路由。

但是,如果您将路由服务本身托管在 IIS 中,显然,该服务最终仍会以.SVC基地址结束!

绕过最后一个限制的唯一方法(即,如何在一个不错的 URI 上托管路由服务本身,例如net.tcp://servername/XYZServices)是在一个例如,Windows 服务 - 它使您可以完全控制其基地址。然后,您可以继续在 IIS 中托管实际的目标服务(带有 .svc 扩展名),并且您的路由服务会将“不错的”公共 URI 转换为 IIS .svc 的。

然而,放弃 IIS/AppFabric 托管只是为了获得好的 URI 是一个艰难的决定。我可能不会这样做。

希望您至少可以使用其中的一些:)

于 2011-05-07T11:39:07.117 回答
2

这个问题及其答案已经有将近一年的历史了。尽管如此,我也面临同样的问题。

简短的答案是您已经发现的。它不起作用。然而这并没有阻止我们。后来的一些研究和对激活机制的一点“潜水”提供了以下工作解决方案:

public class CustomRouting
{
    public static void Route(string routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
    {
        var serviceHostingEnvironment = new StaticReflectionWrapper(typeof (ServiceHostingEnvironment));
        serviceHostingEnvironment.StaticMethod("EnsureInitialized");
        var hostManager = serviceHostingEnvironment.GetStaticField("hostingManager");

        var hostingManager = new InstanceReflectionWrapper(hostManager);
        var normalizedAddress = hostingManager.Method<string>("NormalizedRelativeAddress", routePrefix);
        var serviceActivation = new KeyValuePair<string, string>(normalizedAddress, string.Format("{0}|{1}|{2}", normalizedAddress, serviceHostFactory.GetType().FullName, serviceType.FullName));
        hostingManager.GetField<Hashtable>("serviceActivations").Add(serviceActivation.Key, serviceActivation.Value);

        var tD = StaticReflectionWrapper.Resolve(typeof (ServiceHostingEnvironment).Assembly, "TD");
        if (tD.StaticMethod<bool>("CBAEntryReadIsEnabled"))
        {
            tD.StaticMethod("CBAEntryRead", routePrefix, serviceActivation.Key);
        }

        ServiceHostingEnvironment.EnsureServiceAvailable(serviceActivation.Key);
    }
}

帮助类 StaticReflectionWrapper 和 InstanceReflectionWrapper 只是帮助类保持反射的可读性。

您使用 Route 方法如下:

var fact = new CustomServiceHostFactory();
CustomRouting.Route("DynamicServices/" + service.GetType().Name + ".svc", fact, service.GetType());

服务变量是 ServiceContract 的实现。

使用这个需要自担风险,因为这真的会进入我们不应该来的地区。尽管如此,它仍然有效。

希望这可以减少很多人的搜索时间并打开封闭的大门。

于 2012-06-06T14:34:30.757 回答