I have a service decorated with a ServiceContract attribute, and the interface definition is something like this:
[ServiceContract(CallbackContract = typeof(ITabularPushCallback))] public interface ITabularPushService { [OperationContract(IsOneWay = true)] void Subscribe(int tableId); [OperationContract(IsOneWay = true)] void UnSubscribe(int tableId); [OperationContract(IsOneWay = false)] bool IsSubscribed(int tableId); }
the callback contract of the interface ITabularPushService is ITabularCallback, the definition of the interface is like this:
public interface ITabularPushCallback { // this will ensure that the Datagram transport will be used, the Callback does not rquire that the server to block // and this is far more effecient than Bidirectional connection [OperationContract(IsOneWay = true)] void NotifyMessage(object[][] messages, int tableId); }
Notice it is not necessary to decorate the callback contract with the [ServiceContract] attribute, because
This is just an side note, let's get back to our question before.
Now suppose that we have a new requirement to add a new method to the ITabularCallback, something like the Server heartbeat, we don't want to directly extend the interface because we feel this is so general that we want to make a separate interface for the heartbeat. so we go ahead to implement the code as such.
/// <summary> /// Server HeartBeat interface /// </summary> public interface IHeartbeat { /// <summary> /// Server heartbeat /// </summary> /// <param name="timestamp">heartbeat timestamp</param> [OperationContract(IsOneWay = true)] void Heartbeat(long timestamp); }
then we will make our ITabularCallback interface to inherit from the IHeartbeat, quick and easy, shouldn't it be ? however, you might be greeted with a NotSupportedException. The messgae may contains the following.
Callback method Heartbeat is not supported, this can happen if the method is not marked with OperationContractAttribute or if its interface type is not the target of the ServiceContractAttribute's CallbackContract.
Huh?
So we figured out somehow that WCF does not walk the tree of interface and do reflection to get all Operatoin members. (For details, please see the References on topic Inheritance not supported on callback contracts?)
since as we have pointed out, the ServiceContract willl forward interface to WCF system, so we can leverage this point. we can make a marker interface and decorate that with a ServiceContract attribute, which shall binds to Callback we have in mind - IHeartbeat, we caller the new interface IHeartbeatService, it is an empty interface, which serves here only for its side effect of ServiceContract attribute to forward its callback contract.
// Check // http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/ef896836-dec1-4fa6-9956-e3a4958643ce [ServiceContract(CallbackContract = typeof(IHeartbeat))] public interface IHeartbeatService { }
Now we have to refine the Service and its contract together...
[ServiceContract(CallbackContract = typeof(ITabularPushCallback))] public interface ITabularPushService : IHeartbeatService { // same as before }
and the new ITabularCallback interface.
public interface ITabularPushCallback : IHeartbeat { // ... same as before. }
now that both the IHeartbeat and ITabularCallback is forwarded you will not worry about getting the NotSupported excetion.
Explain on the Design:
Suppose that we have
[ServiceContract(Namespace="foo", CallbackContract=typeof(IBaseCB))] interface IBaseC { ... } interface IBaseCB : IBaseBaseCB { ... } interface IBaseBaseCB { [OperationContract] void F(); }
If we have reflection which goes up the tree, then it will have a F method in the namespace foo.
Now, if we add more interface, such as
[ServiceContract(Namespace="bar", CallbackContract=typeof(IBaseBaseCB))] interface IOther { ... }
then we might have another F in namespace "bar".
Now, we refine as such
interface IDerivedCB : IBaseCB { ...} [ServiceContract(CallbackContract=typeof(IDerivedCB))] interface IDerivedC : IBaseC, IOther { ... }
Then how can wcf know which namespace F now in IDerviedC is?
Some other tips:
1. putting [ServiceContract] attribute on a callback contract
To model two servers
[ServiceContract(CallbackContract=typeof(IPong)] interface IPing { ... } [ServiceContract(CallbackContract=typeof(IPing)] interface IPong { ... }
Or even make that to a single contract (Peerchannel does this a bit)
[ServiceContract(CallbackContract=typeof(ISelf)] interface ISelf { ... }
References:Inheritance not supported on callback contracts?