Is there an ASP style Routing Table for NetTcp bindings in IIS 7?
I'm working on changing the bindings of a service from Restful http to netTcp, for a set of application services hosted on IIS 7. The restful configuration uses a Routing Table, and dynamically creates the end points based on a naming convention and dependency injection. As we now need to call these within a transaction b开发者_高级运维lock (DTS) we are converting these services to Net Tcp bindings.
I'd like to be able to keep the routing by convention model used, and dependency injection rather than have lots of messy .svc files. I know the ASP Routing Tables don't work with the other bindings. Any suggestions for an alternative? Could a custom HttpModule do it?
- Will vote up any useful info or tick an answer or link to an answer.
Thanks, Aaron
No, there is no direct equivalent of the ASP.NET RouteTable for net.tcp bindings. And a custom HttpModule wouldn't help as that is just for HTTP ;)
However, there are still some things you can do. First of all, if you don't want to have a .SVC file for each one of your services, you can leverage the new (WCF 4) configuration-based service activation which lets you declare all WCF services in the application's Web.config (inside system.serviceModel), like this:
<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>
Notes:
- The @factory attribute defaults to
ServiceHostFactory
, i.e. you only need to specify it if you're using another factory type (like WebServiceHostFactory, or a custom one) - The major hick-up is, @relativeAddress MUST contain an extension !!
- The extension must be declared in system.web/compilation/buildProviders, and the type specified there must have an [ServiceActivationBuildProvider] attribute (System.ServiceModel.Activation)
- Which means in reality you'll end up using the
.svc
extension, since that one is already declared by default in the root Web.config -
But, this approach at least allows you to define all your services in one place (as opposed to multiple .svc files) - and in that regard it's comparable to RouteTable
.
Now, if you're bothered by the .svc extensions, you can even go one step further. Another new item in WCF4 is the Routing Service (see http://msdn.microsoft.com/en-us/library/ee517423.aspx) which is a generic WCF service that receives messages in order to forward them to other WCF services based on various criteria (like SOAP Headers, message content, endpoint address, etc.).
Using a Routing Service, you could define various EndpointAddress filters (e.g., net.tcp://{routing-service-base}/service1, net.tcp://{routing-service-base}/service2, etc.) - which gets you even closer to the ASP.NET routing.
However, if you host the Routing Service itself in IIS, obviously, that one would still end up with a .SVC
base address!
The only way how you could get around that last limitation (i.e., how to host the Routing Service itself at a nice URI, like net.tcp://servername/XYZServices) would be to self-host just the Routing Service, in a Windows Service, for instance - which gives you full control over its base address. Then you can keep hosting the actual target services in IIS - with .svc extensions - and your Routing Service would translate the "nice" public URIs into the IIS .svc ones.
It's a tough decision, however, to give up IIS/AppFabric hosting just to get nice URIs. I probably wouldn't do it.
Hope you can use at least some of that :)
This question with its answer is almost a year old. Nevertheless, I faced the same problem.
The short answer is what you have discovered already. It doesn't work. Yet this never stopped us. Some research later and a little "diving" into the activation mechanism delivered the following working solution:
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);
}
}
The helper classes StaticReflectionWrapper and InstanceReflectionWrapper are merely helper classes to keep the reflection readable.
You use the Route method as following:
var fact = new CustomServiceHostFactory();
CustomRouting.Route("DynamicServices/" + service.GetType().Name + ".svc", fact, service.GetType());
The service variable is an implementation of a ServiceContract.
Use this at own risk because this really goes low into regions where we shouldn't come. Nevertheless, it works.
Hopefully this reduces the search time for a lot of people and opens closed doors.
精彩评论