In WCF 3.x, if any one try to host a service without configuring any endpoints, the ServiceHost
instance throws an System.InvalidOperationException
informing that there should be at least one endpoint configured.
But with the WCF 4.0, the runtime automatically adds one or more “default endpoints”, thereby making the service usable without any configuration. Actually when the service host application invokes Open on the ServiceHost
instance, it builds the internal service description from the configuration file and service assembly respectively along with any explicit configuration the host application has and if the number of configured endpoints is found zero, it calls AddDefaultEndpoints
method (a new public method of the ServiceHost
class) to add one or more endpoints to the service description based on the service’s base addresses configuration. In case of IIS scenarios, it would be the .svc address. The rule can be stated as below-
The AddDefaultEndpoints
method adds one default endpoint per base address for each service contract implemented by the service.
If the service implements one service contract (say IA) and the host is configured with two base addresses
(say one for TCP and another for HTTP), then the AddDefaultEndpoints
method will add two default endpoints to the service (one per base address with the service contract. combination would be TCP with IA and HTTP with IA).
If the service implements two service contracts (say IA and IB) and the host is configured with a single base address (say for TCP in this case), then the AddDefaultEndpoints
method will add two default endpoints to the service (one per base address with each service contract. combination would be TCP with IA and TCP with IB).
If the service implements two service contracts (say IA and IB) and the host is configured with two base addresses (say one for TCP and another for HTTP), then the AddDefaultEndpoints
method will add four default endpoints to the service (one per base address with each service contract. combination would be TCP with IA, TCP with IB, HTTP with IA and HTTP with IB).
So in general for a service with zero endpoint configurations, if the total no of configured base address are n and the total no of implemented service contract are m, then the AddDefaultEndpoints
method will add n x m default endpoints to the service.
Let’s try to grasp this concept through an example. For the demo I've created a simple service to demonstrate the default endpoints creation. I've defined IHusband
, IWife
& IKids
service contracts and after that I've created a service FamilyService
by implementing these service contracts as per below-
[ServiceContract]
public interface IHusband
{
[OperationContract]
void WelcomeHusband(string name);
}
[ServiceContract]
public interface IWife
{
[OperationContract]
void WelcomeWife(string name);
}
[ServiceContract]
public interface IKids
{
[OperationContract]
void WelcomeKids();
}
public class FamilyService : IHusband, IWife, IKids
{
public void WelcomeHusband(string name)
{
Console.WriteLine("Hello Mr. {0}", name);
}
public void WelcomeWife(string name)
{
Console.WriteLine("Hello Mrs. {0}", name);
}
public void WelcomeKids()
{
Console.WriteLine("Hi Kids!");
}
}
Then I've created a console application to host the service using ServiceHost
class and added a base address for HTTP programmatically to it without any configuration file associated with the host application. See the code below-
var host = new ServiceHost(typeof(FamilyService), new Uri("http://localhost:8080/familyservice"));
try
{
host.Open();
PrintServiceInfo(host);
Console.ReadLine();
host.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
host.Abort();
}
Now run the host and you will find there are three endpoints in the console window added to the service by default, one for each service contract for the HTTP base address.
Now add one more base address for TCP programmatically to it like-
var host = new ServiceHost(typeof(FamilyService),
new Uri("http://localhost:8080/familyservice"),
new Uri("net.tcp://localhost:8085/familyservice"));
and run the host application again. Now you will see there are six endpoints in the host console window, three for the TCP base address one per service contract and three for the HTTP base address one per service contract.
So you have seen in both cases how ServiceHost instance adds default endpoints to the service behind the scene for the provided base address(s). Actually ServiceHost
instance invokes the AddDefaultEndpoints
method implicitly to add default endpoints. So with WCF 4, you can use ServiceHost
class to host any service without any application configuration file.
Now it is important to note that at this point if any service has been configured with a single endpoint, then this default endpoints behavior will no longer work. To check this, just add one endpoint to the FamilyService
service like
host.AddServiceEndpoint(typeof(IHusband),new NetTcpBinding(), "pipe");
and run the host application, you'll observe that there is only single endpoint displaying in the host console window. No default endpoints.
But if you still like to have default endpoints to the service along with your own, just invoke the AddDefaultEndpoints
method explicitly like-
host.AddServiceEndpoint(typeof(IHusband),new NetTcpBinding(), "pipe");
host.AddDefaultEndpoints();
and run the host application again, you'll now find all possible default endpoints are displaying on the console host windows along with your own endpoint.
The default endpoint behavior will also work if you configure the base address(es) through the configuration file. However in this case you'll need to configure the service with desire base address(es) through the configuration file. To see this, just create a ServiceHost
instance like-
var host = new ServiceHost(typeof(FamilyService));
and add configuration file (in this case app.config
file) to the console host application and configure the service with a single base address like-
<system.serviceModel>
<services>
<service name="FamilyServiceLibrary.FamilyService">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/familyservice" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
and run the console host application, you'll see all default endpoints on the console window.
Default endpoint behavior can also be observed in case of service is hosted in IIS. With WCF 4.0, if a service is going to be hosted in IIS with default configuration, then the WCF activation logic will create the ServiceHost
instance behind the scene and configure the service with default HTTP endpoint(s). If a service implements only one service contract, then a single default HTTP endpoint would be added, and if a service implements more than one service contract the resulting ServiceHost instance will be configured with one HTTP endpoint per contract.
Let’s host our FamilyService
in IIS to understand this concept. For the demo, I've created a WCF Service Web Site and added the reference of our FamilyService
project (FamilyServiceLibrary) to it and renamed the service.svc
file to Family.svc
(not necessary). Then opened the FamilyService.svc
and updated the ServiceHost
directive as below-
<%@ ServiceHost Service="FamilyServiceLibrary.FamilyService" %>
As we know, when a service is hosted in IIS/WAS, then the base address is determined by the IIS virtual directory hosting the service along with the name of the .svc file, so there is no need to worry about the base address(es) configuration. Now I've just ensured that whether service metadata
is enabled or not through the web.config
file like-
<serviceMetadata httpGetEnabled="true" />
Finally I've hosted our FamilyService
in the IIS. Now when the first time any one browse to Family.svc
, the WCF activation logic will create the ServiceHost
instance and it will add three default HTTP endpoints for the FamilyService
type (one for each service contract). You can verify these default endpoints by browsing to the WSDL definition and you’ll find three <port> elements within the <service> element.
Let’s realise this behavior for other protocols too in case of IIS (version > 7.0)/Windows Process Activation Services (WAS) hosting. As we know WAS allows WCF service activation over any transport (TCP/MSMQ/NamedPipe etc) in IIS (version > 7.0). So I've enabled TCP and NamedPipe protocols to our FamilyService
through Interned Information Seerver (IIS) Manager as below-
Now you can verify default endpoints by browsing to the WSDL definition for TCP and NamePipe protocols too. Just see the nine <port> elements within the <service> element. Here when the first time Family.svc
browsed, the WCF activation logic has created the ServiceHost
instance and it added nine default endpoints (three for HTTP, three for TCP and three for NamedPipe for each service contract) for the FamilyService
type.
If you don't want default endpoints behavior in your service, and to want to configure your own endpoint(s), just configure your service with a relative address in web.config
file
<services>
<service name="FamilyServiceLibrary.FamilyService">
<endpoint address="ws" binding="wsHttpBinding" bindingConfiguration=""
contract="FamilyServiceLibrary.IHusband" />
</service>
</services>
and browse the Family.svc
; you'll find there is only one <port> element within the <service> element in the WSDL definition.
Now what if you still want default endpoints behavior to the service along with your own endpoint(s)? Well this is bit tricky in this case. You'll need to use ServiceHostFactory
class. You can intercept ServiceHost
creation using a ServiceHostFactory
. So in order to achieve this, first I've created a FamilyServiceHost
class by deriving from the ServiceHost
class and overridden its OnOpening
method to configure the service with a desire endpoint (relative) along with default endpoints as below-
public class FamilyServiceHost : ServiceHost
{
public FamilyServiceHost(Type serviceType, params Uri[] baseAddresses) :
base(serviceType, baseAddresses) { }
protected override void OnOpening()
{
base.OnOpening();
this.AddServiceEndpoint(typeof(IHusband),
new WSHttpBinding(), "ws");
this.AddDefaultEndpoints();
}
}
Then I've created FamilyServiceHostFactory
class by deriving from the ServiceHostFactory
class and overridden its CreateServiceHost
method in order to create instance of our custom service host class- FamilyServiceHost
as below-
public class FamilyServiceHostFactory : ServiceHostFactory
{
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
return new FamilyServiceHost(serviceType, baseAddresses);
}
}
And finally I've specified our custom host factory class in Family.svc
file using the Factory
attribute as below-
<%@ ServiceHost Service="FamilyServiceLibrary.FamilyService"
Factory="FamilyServiceLibrary.FamilyServiceHostFactory" %>
Now when you browse Family.svc
first time, the WCF activation logic will create the custom ServiceHost
instance and it will add your own endpoint along with default HTTP endpoints for the FamilyService
type (one for each service contract). You can verify these default endpoints and your own endpoint by browsing to the WSDL definition and you’ll see there are four <port> elements within the <service> element.
WCF 4.5 shipped with a nice feature of HTTPS default endpoints. So if your IIS is SSL configured, and you are going to host a WCF 4.5 service in IIS with default configuration, then the WCF 4.5 activation logic will create the ServiceHost instance behind the scene and configure the service for both default HTTP and HTTPS endpoint(s).
So if our FamilyService
is going to be hosted in SSL configured IIS, we'll get six default endpoints- three for HTTP and three for HTTPS. See the screen shot below-
If you have SSL configured IIS, but don't want to use HTTPS default endpoint(s), you can get rid of it by using the HTTPS scheme mapping from the configuration file as shown below-
<system.serviceModel>
<protocolMapping>
<remove scheme="https"/>
</protocolMapping>
</system.serviceModel>
Have you noticed in above examples how WCF chosen to use the BasicHttpBinding
for the default HTTP endpoints, the NetTcpBinding
for the default TCP endpoints and the NetNamedPipeBinding
for the default NamedPipe endpoints? How did WCF decide which binding to use for a particular based address? The answer to this question is quiet simple. WCF defines a default protocol mapping between transport protocol schemes (e.g., http, net.tcp, net.pipe, etc) and the built-in WCF bindings. The default protocol mapping is defined in the .NET 4 machine.config.comments file and it looks like as below-
<system.serviceModel>
<protocolMapping>
<add scheme="http" binding="basicHttpBinding" bindingConfiguration="" />
<add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration=""/>
<add scheme="net.pipe" binding="netNamedPipeBinding" bindingConfiguration=""/>
<add scheme="net.msmq" binding="netMsmqBinding" bindingConfiguration=""/>
</protocolMapping>
...
</system.serviceModel>
One can override these mappings at the machine level by modifying the mapping for each protocol scheme. Or you can also override mapping(s) within the scope of an application by overriding this section within your application/web configuration file.
Let’s realise this behavior. I've overridden the default binding mapping for the HTTP protocol scheme from BasicHttpBinding
to WSHttpBinding
in the app.config
for our console service host application like below-
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="" />
</protocolMapping>
and run the host application. You can now observe that there are three default endpoints (one for each contract) displaying on the console window using WSHttpBinding
in stead of BasicHttpBinding
for the HTTP protocol scheme.
Similarly you can realise this behavior for our IIS hosted service too by overriding the default binding mapping for the HTTP protocol scheme from BasicHttpBinding
to WSHttpBinding
in the web.config
. You can verify that HTTP default endpoints now using WSHttpBinding
in stead of BasicHttpBinding
by browsing to the WSDL definition for <port> elements within the <service> element.
Once WCF determines which binding to use with the help of the protocol mapping table, it uses the default binding configuration when configuring the default endpoint.
If you want to override the default binding mapping for the protocol scheme, you can override the same for a particular binding via configuration file.