Combine wcf operation contracts - wcf

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?

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.

Related

Common WCF Response Handler?

I have a WCF Client, and the Endpoint has just been upgraded with a new method (OperationContract). I want to write a common method to handle the response from this new method as well as from the existing method at the endpoint.
I am trying to create a "Base" response class and adding common properties to it provided by the WCF endpoint, but I notice in my handler method, the properties are not being retained.
The code for the class I want all responses to inherit from looks like this :
public class ResponseBase
{
public string[] ItemsReturned;
public bool ItemsWereAvailable;
}
So I add partial declarations to get this onto the objects in the endpoint.
public partial class RetrieveResponse :ResponseBase
{
}
public partial class RetrieveResponse2 :ResponseBase
{
}
This way I have a handler method that just accepts "ResponseBase" as its input.
Am I doing this all wrong?
Any class whose instances will be return values and/or parameters of an operation contract should be decorated with the DataContract attribute, and the properties, as DataMembers:
[DataContract]
public class ResponseBase
{
[DataMember]
public string[] ItemsReturned { get; set; }
[DataMember]
public bool ItemsWereAvailable { get; set; }
}
http://msdn.microsoft.com/en-us/library/ms733127.aspx
If they are not, the DataContractSerializer doesn't serialize them.

Passing List<T> as parameter to WCF service operation

I have a WCF operation contract which looks like this:
public void SavePersons(List<Person> list, bool IsSelected)
{
}
I am passing it a strongly typed list of Person objects (List<Person>) in my client. However, I am getting a bad request 400 message when calling the service. What am I doing wrong?
May I suggest you create you create a contract to encapsulate the parameters like so:
public void SavePersons(PersonCollectionContract Request)
{
...
}
[DataContract]
public class PersonCollectionContract
{
[DataContract]
public List<Person> People { get; set; }
[DataContract]
public bool IsSelected { get; set; }
}
[DataContract]
public class Person
{
...
}
I was facing a similar problem in passing a List<Health> of class Health type as a parameter to a wcf service method. I created a data contract in wcf service as below:
[DataContract]
public class Health
{
...
}
Defined a method in wcf service class such as:
public string GetData(List<Health> healthValues)
In my client application, while configuring/updating the service, I followed these steps:
Add/Update URL
Under Data Type (in Advanced), selected option, Collection type: System.Collection.Generic.List
And finally, I created a list and added the code in client as follows:
List<WcfService.Health> listHealth = new List<WcfService.Health>();
WcfService.Health h = new WcfService.Health();
.
.
listHealth.Add(h);
WcfService.Service1Client s = new WcfService.Service1Client();
string str = s.GetData(listHealth);
This solved my purpose and I was able to send the data as a list through wcf service.

What's the practical way of serializing an instance of subclass by using DataContractSerializer?

What's the practical way of serializing an instance of subclass by using DataContractSerializer?
For example, here are the data types:
[DataContract]
public class Car
{
public Car()
{
Wheels = new Collection<Wheel>();
}
[DataMember]
public Collection<Wheel> Wheels { get; set; }
}
[DataContract]
public abstract class Wheel
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class MichelinWheel : Wheel
{
[DataMember]
public string Wheel1Test { get; set; }
}
[DataContract]
public class BridgeStoneWheel : Wheel
{
[DataMember]
public string Wheel2Test { get; set; }
}
Then here is the code that creates a car with two differen wheels:
Car car = new Car();
MichelinWheel w1 = new MichelinWheel { Name = "o1", Wheel1Test = "o1 test" };
BridgeStoneWheel w2 = new BridgeStoneWheel { Name = "o2", Wheel2Test = "o2 test" };
car.Wheels.Add(w1);
car.Wheels.Add(w2);
Now if I try to serialize the car by using DataContractSerializer, I will get an exception that says MichelinWheel is not expected. And I have to modify the Wheel class like this to make it work:
[DataContract]
[KnownType(typeof(MichelinWheel))]
[KnownType(typeof(BridgeStoneWheel))]
public abstract class Wheel
{
[DataMember]
public string Name { get; set; }
}
But this approach is not practical, because I am not able to list all kinds of wheels before they are created. And changing the Wheel class every time after a new brand of wheel is created is also not practical, because they might by created in third-party code.
So, what is the practical approach of serializing an instance of a subclass when using DataContractSerializer?
Thanks
Check this article using DataContractResolver from WCF 4. You can also use KnownTypeAttribute with passing name of a method that will use reflection to get all types. Anyway service requires that all types are known before it starts.
There are several ways to make known types available to the service.
The simplest you have outlined above, but obviously this requires you to recompile when new types are added, and depending on your configuration can make it awkward to avoid circular dependencies.
You can also configure the KnownTypes:
through the service configuration file (service restart only required),
add them as service known types provided through a static method on the service interface which you could get through reflection as Ladislav Mrnka has indicated (you could probably reflect over all loaded assemblies and return all types that have the DataContact attribute on them as known types, but I couldn't find an example of that.)
implement your own way of getting them (perhaps through some bespoke configuration elements in the config file or just through a text file)

How can I pass a List<Interface> over WCF?

I have a WCF service where I am trying to return a List (where IWatchable is a custom interface I have built) in one of my operation contracts. When I test the service on the client the method returns an object[] instead of List<IWatchable>. Is it possible to return a List of IWatchable, since IWatchable is an interface with WCF?
Method:
public List<IWatchable> GetWorkload( Guid nodeId, int maximum )
IWatchable:
public interface IWatchable
{
string ActionName { get; set; }
Guid ActionReference { get; set; }
}
Hopefully a bit more info will be helpful...
I have a derived interface:
public interface IAMRAWatchable: IWatchable
And three concrete implementations from IAMRAWatchable:
public class InstrumentationWatch: IAMRAWatchable
public class OutputWatch: IAMRAWatchable
etc...
In my WCF method that returns List<IWatchable> I want to send an InstrumentationWatch and an OutputWatch to the client... Is this possible or am I going about this the wrong way?
Resolved
Thanks to John I found my solution. KnownType wasn't working since I was using List<IWatchable> - So I wrapped my list into a new class and added the attributes to it. I'll need to re-factor my code but for others who are interested here is the class:
[DataContract]
[KnownType( typeof( InstrumentationWatch ) )]
[KnownType( typeof( OutputWatch ) )]
public class WorkInfo
{
[DataMember]
public List<IWatchable> WorkQueue { get; set; }
}
and my WCF method:
public WorkInfo GetWorkload( Guid nodeId, int maximum )
An interface can never be serialized. It is only a description of behavior.
You can serialize objects which implement the interface, but you must tell WCF what their types are. See Data Contract Known Types.

WCF - Sending data to server outside of contract

I have a WCF service with a client application. I have complete control over both the client and server implementation. I have hundreds of methods in the WCF contract which need a piece of information supplied by the client. Instead of modifying hundreds of methods, is there a way I can send specific data from the client with every call to the service, possibly somewhere in the channel?
Maybe when the client is setting up the proxy before making the call, it can store this data somewhere in an internal property of the proxy... the data would then get sent to the server and from within the service method I could inspect the OperationContext or some other piece of memory to get this data back and use it?
Any ideas?
It sounds like you are wanting something like headers like with SOAP webservices. I'm not a WCF expert, but this looks like the WCF equivalent.
It shouldn't actually be that hard. The best way I can think of is to write an IClientMessageInspector that adds a SOAP header into the Message.Headers in its BeforeSendRequest method.
See e.g. http://weblogs.asp.net/paolopia/archive/2007/08/23/writing-a-wcf-message-inspector.aspx
You can't do this trivially. It will take some work.
It's true that SOAP Headers are the perfect way to pass out-of-band data to and/or from a service. But you already have your contract defined, and adding headers will change the contract.
I believe you'll have to start using message contracts.
Original:
[DataContract]
public class ComplexObject
{
[DataMember(Name = "Id")]
public int Id;
[DataMember]
public string Name;
}
[ServiceContract()]
public interface IMyContract
{
void MyOperation(ComplexObject co);
}
public class MyService : IMyContract
{
#region Implementation of IMyContract
public void MyOperation(ComplexObject co)
{
// use co.*
}
#endregion
}
Using Message Contracts:
[DataContract]
public class ComplexObject
{
[DataMember(Name = "Id")]
public int Id;
[DataMember]
public string Name;
}
[DataContract]
public class MyHeader
{
[DataMember]
public string UserName;
[DataMember]
public string Password;
}
[DataContract]
public class OutputHeader
{
[DataMember]
public string Token;
}
[MessageContract]
public class MyOperationRequest
{
[MessageHeader]
public MyHeader Authentication;
[MessageBodyMember]
public ComplexObject TheObject;
}
[MessageContract]
public class MyOperationResponse
{
[MessageHeader]
public OutputHeader OutputHeader;
}
[ServiceContract()]
public interface IMyContract
{
MyOperationResponse MyOperation(MyOperationRequest request);
}
public class MyService : IMyContract
{
public MyOperationResponse MyOperation(MyOperationRequest request)
{
// use request.TheObject.*
// Can also read request.Authentication.*
return new MyOperationResponse
{ OutputHeader = new OutputHeader { Token = "someToken" } };
}
}