0
votes

The common way in WCF is to have a service with several operation contracts. Once an operation contract is defined you better not change it anymore because there are a lot of cases in which an operation change will break an existing client. But how about having just one general or universal operation contract (One single instead of many operation contracts. In fact I know you won’t be able to combine all in one but most of them). Here one example. That’s not the way I would finally realize it … just a simple illustration.

 
public enum Operation
{
    Add,
    Sub,
    Mul,
    Div
 }


[DataContract]
public class Info
{

    [DataMember]
    public Operation Operation { get; set; }

    [DataMember]
    public object Data { get; set; }
}

[ServiceContract]
public interface IService
 {

    [OperationContract]
    Info Do(Info info);
}


public class Service : IService
{
    public Info Do(Info info)
    {
        var result = -1;
        switch (info.Operation)
        {
            case Operation.Add:
                // cast info.Data and calculate result
                break;
            case Operation.Sub:
                // cast info.Data and calculate result
                break; 
        }
        return new Info { Operation = info.Operation, Data = result };
    }
}

The main disadvantage on a universal contract is that you have to know how to pass data to the service and how to interpret the result. But this can be solved by documentation.

Currently I’m working on a project with a big client-server-application combined in one solution. It’s a big hassle to add one operation, update service references…

What speaks against to combine operation contracts? Do I have something more to consider?

1

1 Answers

0
votes

Once an operation contract is defined you better not change it anymore

But how about having just one general or universal operation contract

[ServiceContract]
public interface IService
 {

    [OperationContract]
    Info Do(Info info);
}

Though you have made an attempt to make it "universal" or generic you really haven't. That's because your service interface only accepts Info as an argument with no room for expansion.

Info has members of type Operation - an enumeration which you cannot change as you rightly stated. What happens if I want to do a square root operation down the track? Or maybe a factorial?

One approach for single method service contracts that allow for future request types is the request/response pattern. Essentially you define the WCF service contract once and it never changes even when you add new operations. There's actually no need to. The object that is passed in the Request parameter is essentially an a standard envelope in which the actual specific operation is serialised.

e.g. here I have renamed your Request to RequestEnvelope

[ServiceContract]
public interface IService
 {

    [OperationContract]
    ResponseEnvelope Do(RequestEnvelope request);
}

[DataContract]
public class RequestEnvelope 
{
    [DataMember]
    public IRequest Request { get; set; }
}

[DataContract]
public class ResponseEnvelope 
{
    [DataMember]
    public IResponse Response { get; set; }
}

For example, I might want to calculate prime numbers so I would serialise an instance of CalculatePrimes into the Request member of RequestEnvelope.

public interface IRequest { }

public interface IResponse { }

[Serializable]
public class CalculatePrimes : IRequest
{
    public int StartAt { get; set; }
    public int CountToCalculate { get; set; }
    public TimeSpan Timeout { get; set; }
}

[Serializable]
public class CalculatePrimesResponse : IResponse
{ 
    public List<int> Primes { get; set; }
}

This approach works very well when refactoring large monolithic services with many many operations on a single WCF interface into something more manageable and significantly less long-term maintenance. Note how the actual requests and responses don't have to be actual WCF types but POCOs.