I am trying to play around with WCF and I think I've hit a block. My issue is that I am able to call Add(double,double) and getPerson() from the "Client". However, I am not able to call any methods of Person object. I've stripped down the classes with bare methods. Here are my code snippets, please let me know what I am doing wrong..
Server Code
namespace Test.WebSvc{
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Sample")]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
Person getPerson();
}
public class CalculatorService : ICalculator
{
public double Add(double n1, double n2) { return n1+n2 ; }
public Person getPerson(){
Person tempPerson = new Person();
return tempPerson;
}
}
[DataContract]
public class Person{
[OperationContractAttribute]
public string toString(){
return "This is a Person Object";
}
Client Code
ServiceRef1.CalculatorClient client = ServiceRef1.CalculatorClient();//works
Console.WriteLine(client.Add(1.0,2.0)); //this too works
ServiceRef1.Person p = client.getPerson(); // this is OK., but is not doing what I wanted it to do
Console.WriteLine(p.toString()); // I do not get "This is Person Object"
I am guessing something's wrong with my Person class' declaration.. but dint get a clue what should I do or what I am missing..
Thanks!
Yes, you've hit a barrier - WCF is a message-based system which only exchanges serialized data, either as XML or JSON, in text or binary format. It does not however pass around "full" .NET objects with their full capabilities like methods and all that (how should it?? It's designed to be interoperable, and I don't see how a Ruby or PHP client could call a method on a .NET object).
WCF is not designed to make it possible to remotely access objects - it just passes messages between client and server. Therefore, anything you can express in XML schema (atomic types, anything like inheritance and composition) can be serialized and sent between the two parties.
Anything that cannot be modelled in XML schema - like generics, interfaces, methods/code - cannot be passed between client and server.
There are ways and tricks to get around this if you control both ends of the communication and both of them are .NET based. Basically, you would have to put your service contract and all your data contracts (all your classes) into a separate assembly, which you then reference from both the server (implementing the service contract), and from the client calling the contract. You can tell WCF to re-use types that already exist in referenced assemblies, and in this case, the client will re-use the ready-made Person class (with all its .NET goodness) from your common shared assembly instead of re-creating a client-side data proxy. With this trick, you can have WCF send serialized messages across the wire, yet on the client side, you're re-creating a full-fledged .NET object with all its methods and all.
Again: this works great as long as you control both ends of the communication, and both ends are using .NET. Any interoperability is out the window with this approach.
You are mixing up two concepts with your Person type -- what you're doing will not work.
You've put a DataContract attribute on the Person type. This is correct, because you have a service that is returning a Person. The Person object will be serialized and returned to your service client (CalculatorClient in this case).
You should define Person like this:
[DataContract]
public class Person
{
[DataMember]
public string Description { get; set; }
}
And in your calculator service:
public Person getPerson()
{
Person tempPerson = new Person();
tempPerson.Description = "This is a Person Object";
return tempPerson;
}
This is because your Person object's job is to hold data, and carry it from server to client. It is not its job to define methods / operations, which should instead be done in your Service classes (eg CalculatorService). Adding the OperationContract attribute does not magically turn the Data Transfer Object into a Service.
Related
I have some POCO objects that are set up for use with Entity Framework Code First.
I want to return one of those objects from an ApiController in my ASP.NET MVC 4 website, and then consume it in a client application.
I originally had problems with the serialization of the object at the server end, because the Entity Framework was getting in the way (see Can an ApiController return an object with a collection of other objects?), and it was trying to serialize the EF proxy objects rather than the plain POCO objects. So, I turned off proxy generation in my DbContext to avoid this - and now my serialized objects look OK (to my eye).
The objects in question are "tags" - here's my POCO class:
public class Tag
{
public int Id { get; set; }
public int ClientId { get; set; }
public virtual Client Client { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool IsActive { get; set; }
}
Pretty standard stuff, but note the ClientId and Client members. Those are EF Code First "navigation" properties. (Every tag belongs to exactly one client).
Here's what I get from my ApiController:
<Tag xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Foo">
<Client i:nil="true"/>
<ClientId>1</ClientId>
<Id>1</Id>
<IsActive>true</IsActive>
<Name>Example</Name>
</Tag>
The Client member is nil because having disabled proxy generation I don't get automatic loading of the referenced objects. Which is fine, in this case - I don't need that data at the client end.
So now I'm trying to de-serialize those objects at the client end. I had hoped that I would be able to re-use the same POCO classes in the client application, rather than create new classes. DRY and all that. So, I'm trying:
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Tag));
var tag = xmlSerializer.Deserialize(stream);
But I've run into two problems, both of which are due to EF Code First conventions:
Problem 1: Because my Tag class has a Client member, the XmlSerializer is complaining that it doesn't know how to de-serialize that. I guess that's fair enough (though I had hoped that because the member was Nil in the XML it wouldn't care). I could pass in extra types in the XmlSerializer constructor, when I tried that, it then complained about other classes that Client uses. Since Client references all sorts of other objects, I'd end up having to pass in them all!
I tried using the [DataContract] and [DataMember] attributes to remove the Client member from the XML (by not marking it as a DataMember). That did remove it from the XML, but didn't stop the XmlSerializer from whining about it. So I guess it's not the fact that it's in the XML that's the problem, but that it's in the class definition.
Problem 2: When I did try passing in typeof(Client) as an extra type, it also complained that it couldn't de-serialize that class because it contains an interface member. That's because - again due to EF Code First conventions - it has a Tags member as follows:
`public virtual ICollection<Tag> Tags { get; set; }`
So it looks like even if I get over the referenced-types problem, I'm still not going to be able to use my POCO classes.
Is there a solution to this, or do I have to create new DTO classes purely for use at the client side, and return those from my ApiController?
I just tried using DataContractSerializer instead of XmlSerializer, and for the Tag class that seems to work. I've yet to try it with a class that has a virtual ICollection<T> member...
Update: tried it, and it "works". It still manages to reconstruct the object, and leaves the ICollection member at null.
Update 2: OK, that turned out to be a dead end. Yes, it meant that I could correctly serialize and de-serialize the classes, but as everyone kept telling me, DTO classes were a better way to go. (DTO = Data Transfer Objects - classes created specifically for transferring the data across the wire, probably with a subset of the fields of the original).
I'm now using AutoMapper (thanks Cuong Le) so that I can easily transform my POCO entities into simpler DTO classes for serialization, and that's what I'd recommend to anyone faced with the same problem.
Following advices from people on the internet about service references, I got rid of them now and split the service/data contracts into a common assembly accesible by both the server and the client. Overall this seems to work really well.
However I’m running into problems when trying to use custom objects, or rather custom subtypes, in the service. Initially I wanted to define only interfaces in the common assembly as the contract for the data. I quickly learned that this won’t work though because the client needs a concrete class to instantiate objects when receiving objects from the service. So instead I used a simple class instead, basically like this:
// (defined in the common assembly)
public class TestObject
{
public string Value { get; set; }
}
Then in the service contract (interface), I have a method that returns such an object.
Now if I simply create such an object in the service implementation and return it, it works just fine. However I want to define a subtype of it in the service (or the underlying business logic), that defines a few more things (for example methods for database access, or just some methods that work on the objects).
So for simplicity, the subtype looks like this:
// (defined on the server)
public class DbTestObject : TestObject
{
public string Value { get; set; }
public DbTestObject(string val)
{
Value = val;
}
}
And in the service, instead of creating a TestObject, I create the subtype and return it:
public TestObject GetTestObject()
{
return new DbTestObject("foobar");
}
If I run this now, and make the client call GetTestObject, then I immediately get a CommunicationException with the following error text: “The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:09:59.9380000'.”
I already found out, that the reason for this is that the client does not know how to deserialize the DbTestObject. One solution would be to declare the base type with the KnownTypeAttribute to make it know about the subtype. But that would require the subtype to be moved into the common assembly, which is of course something I want to avoid, as I want the logic separated from the client.
Is there a way to tell the client to only use the TestObject type for deserialization; or would the solution for this be to use data transfer objects anyway?
As #Sixto Saez has pointed out, inheritance and WCF don't tend to go together very well. The reason is that inheritance belongs very much to the OO world and not the messaging passing world.
Having said that, if you are in control of both ends of the service, KnownType permits you to escape the constraints of message passing and leverage the benefits of inheritance. To avoid taking the dependency you can utilise the ability of the KnownTypeAttribute to take a method name, rather than a type parameter. This allows you to dynamically specify the known types at run time.
E.g.
[KnownType("GetKnownTestObjects")]
[DataContract]
public class TestObject
{
[DataMember]
public string Value { get; set; }
public static IEnumerable<Type> GetKnownTestObjects()
{
return Registry.GetKnown<TestObject>();
}
}
Using this technique, you can effectively invert the dependency.
Registry is a simple class that allows other assemblies to register types at run-time as being subtypes of the specified base class. This task can be performed when the application bootstraps itself and if you wish can be done, for instance, by reflecting across the types in the assembly(ies) containing your subtypes.
This achieves your goal of allowing subtypes to be handled correctly without the TestObject assembly needing to take a reference on the subtype assembly(ies).
I have used this technique successfully in 'closed loop' applications where both the client and server are controlled. You should note that this technique is a little slower because calls to your GetKnownTestObjects method have to be made repeatedly at both ends while serialising/deserialising. However, if you're prepared to live with this slight downside it is a fairly clean way of providing generic web services using WCF. It also eliminates the need for all those 'KnownTypeAttributes' specifying actual types.
Im am not new to WCF web services but there has been a couple of years since the last time I used one. I am certain that last time I used a WCF service you could determine the type of object returned from a service call when developing the code. EG;
MyService.Models.ServiceSideObjects.User user = myServiceClient.GetUser();
You were then free to use the 'user' object client-side. However now it seems as if the WCF service will not return anything more than objects containing basic value types (string, int ect). So far I have remedied this by defining transfer objects which contain only these basic value types and having the service map the complex 'User' objects properties to simple strings and int's in the transfer object.
This becomes a real pain when, for example you have custom type objects containing more complex objects such as my Ticket object.
public class Ticket
{
public Agent TicketAgent {get;set;}
public Client Owner {get;set;}
public PendingReason TicketPendingReason {get;set;}
}
As simply mapping this object graph to a single transfer class with a huge list of inter-related system-typed properties gives a very 'dirty' client-side business model. Am I wrong in thinking that I SHOULD be able to just receive my Ticket object from a service method call and deal with it client side in the same state it was server-side ?
I realise this is probably a violation of some SoA principal or similar but my desktop app currently consuming this service is the ONLY thing that will consume ever consume it. So i do not care if many other clients will be able to manage the data types coming back from the service and therefore require some hugely normalised return object. I just want my service to get an object of type Ticket from its repository, return this object to the client with all its properties intact. Currently all I get is an object with a single property 'ExtentionData' which is unusable client-side.
Hope this makes sense, thank you for your time.
I might've missed a memo, but I think you need to decorate your model classes with DataContractAttribute and your properties with DataMemberAttribute, like so:
[DataContract( Namespace = "http://example.com" )]
public class Ticket
{
[DataMember]
public Agent TicketAgent { get; set; }
[DataMember]
public Client Owner { get; set; }
[DataMember]
public PendingReason TicketPendingReason { get; set; }
}
This is why you probably want to set up a DTO layer, to avoid polluting your model classes.
As for ExtensionData, it's used for forward-compatibility: http://msdn.microsoft.com/en-us/library/ms731083.aspx
I have marked Niklas's response as an answer as it has solved my issue.
While it seems you do not NEED to use [DataContract] and [DataMember], in some cases, I believe it could cause the issues I was experiencing. When simply transferring custom typed objects which, in themselves, only have simply typed properties, no attributes needed. However, when I attempted to transfer a custom typed object which itself had collections / fields of more custom typed objects there attributes were needed.
Thank you for your time.
I'm working on a Windows Phone 7 app using WCF for communications with my server.
I've created a service and am able to communicate well, but I'm having trouble accessing an interface in the client code.
For instance, in my server code I have something like this:
[OperationContract]
[ServiceKnownType(typeof(IField))]
[ServiceKnownType(typeof(TextField))]
[ServiceKnownType(typeof(NumberField))]
FieldForm GetForm();
Now my FieldForm contains the following declaration:
[DataContract]
class FieldForm
{
public List<IField> Fields { get; set; }
}
And finally, this IField interface has a few implementations:
interface IField
{
string Name { get; set; }
}
[DataContract]
class TextField : IField
{
}
[DataContract]
class NumberField : IField
{
}
(this isn't my code, but describes what I'm trying to accomplish)
Now on my client, I receive a FieldForm object via WCF and want to iterate through the Fields list to determine what UI elements to create. Problem is, the service did not provide the IField interface on the client, but I do have the implementations available (TextField and NumberField).
This leads to some crappy code in my client code like:
foreach ( object field in Fields )
{
if ( field is TextField )
// do textfieldy stuff
else if (field is NumberField)
// do numberfieldy stuff
}
when I'd really prefer to just use:
foreach ( IField field in Fields )
{
field.Name;
}
Am I missing a simple annotation on the interface in order to make the interface type available on the client, or does WCF simply not provide the ability to serialize interfaces?
Is there a way I can access my interface in my client code?
No you cannot serialize an interface across a WCF connection.
WCF uses serialized (XML) messages to communicate between client and server - all you can express in XML schema (XSD) can be moved across. This basically means any concrete type, composed of atomic types like int, string, etc.. XSD also supports inheritance and enums, but that's about the end of the flagpole.
What you might be able to do if you control both ends of the communications link (e.g. both server and client) is to put those interfaces (service interfaces and your data contracts and interfaces) into a common, shared assembly which both your client project and server project would then reference. Use any class implementing your interface in the concrete service implementation, but with this assembly sharing, you can use the signature you like on the client side and program against an interface. Mind you: this "kills" all interoperability of your WCF service - no Ruby or Java clients will be able to call your service anymore.
I have an application where client and server share types, and interoperability is not one of our concerns. I am planning to have a single repository for all web enabled objects, and i was thinking of a generic interface for my exposed service.
something like T GetObject(int id)
but wcf doesnt like it since its trying to expose its schema (which i dont really care about)
is it possible to do such a thing with WCF ?, i can use any type of binding doesnt have to be httpbinding or wsbinding...
No, you can't. Whether or not you want or need interoperability, the most basic foundation of WCF is message exchange.
The client send the server a message and gets back a response. That message is all that passes between client and server, and needs to be serializable into a XML or binary format. That's why any data being passed around must be atomic (like int, string) or a DataContract - a description for the WCF service stack about how to serialize and deserialize such objects.
You cannot pass any interfaces, or other "trickery" - all that goes between client and server must be expressable in XML schema, basically.
So I'm afraid what you're trying to achieve is quite contrary to what WCF offers. The world and paradigms of SOA (Service-Oriented Apps) are quite different and not always 100% in sync with the idea and mechanisms of OOP.
Marc
I suppose this is possible, though I'm not sure you'd want this. I'd take the following approach (untested, not sure if it works). First create the following project structure in your solution:
ServiceInterfaces
ServiceImplementations (references ServiceInterfaces and ModelClasses)
ModelClasses
Host (references ServiceInterfaces and ServiceImplementations)
Client (references ServiceInterfaces and ModelClasses)
In ServiceInterfaces you have an interface like this (I skipped the namespaces, etc to make the example shorter):
[ServiceContract]
public interface IMyService<T>
{
T GetObject(int id);
}
In ServiceImplementations you have a class that implements IMyService<T>:
public class MyService<T> : IMyService<T>
{
T GetObject(int id)
{
// Create something of type T and return it. Rather difficult
// since you only know the type at runtime.
}
}
In Host you have the correct configuration for your service in an App.config (or Web.config) file and the following code to host your service (given that it is a stand-alone app):
ServiceHost host = new ServiceHost(typeof(MessageManager.MessageManagerService))
host.Open();
And finally in Client you use a ChannelFactory<TChannel> class to define a proxy:
Binding binding = new BasicHttpBinding(); // For the example, could be another binding.
EndpointAddress address = new EndpointAddress("http://localhost:8000/......");
IMyService<string> myService =
ChannelFactory<IMyService<string>>.CreateChannel(binding, address);
string myObject = myService.GetObject(42);
Again, I'm not sure if this works. The trick is to share your service interfaces (in ServiceInterfaces) and domain model objects (in ModelClasses) between the host and the client. In my example I use a string to return from the service method but it could be any data contract type from the ModelClasses project.
You CAN DO that if you use ServiceKnownTypesDiscovery.
For example:
[ServiceKnownType("GetKnownTypes", typeof(ServiceKnownTypesDiscovery))]
public interface ISomeService
{
[OperationContract]
object Request(IRequestBase parameters);
}
where GetKnownTypes could be declared like so:
public static class ServiceKnownTypesDiscovery
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
var types = new List<Type>();
foreach (var asmFile in Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory, "*.dll"))
{
Assembly asm = Assembly.LoadFrom(asmFile);
types.AddRange(asm.GetTypes().Where(p=> Attribute.IsDefined(p,typeof(DataContractAttribute))));
}
return types;
}
}
In this case everything declared with [DataContract] (as long as they are discoverable on the server AND the client side) can be serialized.
I hope this helped!
Following the previous example, you could declare a DataContract with an object as DataMember. Then you could add an extension method to get and set a generic type on the object data member. You could also make this internal, this way you would be obliged to use the extension methods to get and set the value.
Of course, it only works if you generate the client using svcutil (or Visual Studio) and you reference the assembly containing the data contract and the class with the extensions methods.
Hope this helps...