`

wcf - Inheritance in callback contract

阅读更多

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 

msdn 写道
you don't need the [ServiceContract] attribute on the callback interface. The callback interface is just "an extra bad of [OperationContract] methods", and not really a contract entity itself. Think of it as merely an auxiliary part of the [ServiceContract] 'forward' interface. Second, all of the inheritance is through the [ServiceContract] ('forward') interfaces. When reflecting on the type, WCF will look through the contract's type hierarchy for parent interfaces that have [ServiceContract], and also pick up those parents' CallbackContracts.

 

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

msdn 写道
Yes, [ServiceContract] will be ignored when being referenced from another [SC]'s CallbackContract. But you can indeed still put it there if the CC is also an SC. Thus is it possible to model two servers that talk to each other:

 

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?

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics