I'm trying to take a datacontract object that I received on the server, do some manipulation on it and then return an upcasted version of it however it doesn't seem to be working. I can get it to work by using the KnownType or ServiceKnownType attributes, but I don't want to roundtrip all of the data. Below is an example:
[DataContract]
public class MyBaseObject
{
[DataMember]
public int Id { get; set; }
}
[DataContract]
public class MyDerivedObject : MyBaseObject
{
[DataMember]
public string Name { get; set; }
}
[ServiceContract(Namespace = "http://My.Web.Service")]
public interface IServiceProvider
{
[OperationContract]
List<MyBaseObject> SaveMyObjects(List<MyDerivedObject> myDerivedObjects);
}
public class ServiceProvider : IServiceProvider
{
public List<MyBaseObject> SaveMyObjects(List<MyDerivedObject> myDerivedObjects)
{
... do some work ...
myDerivedObjects[0].Id = 123;
myDerivedObjects[1].Id = 456;
myDerivedObjects[2].Id = 789;
... do some work ...
return myDerivedObjects.Cast<MyBaseObject>().ToList();
}
}
Anybody have any ideas how to get this to work without having to recreate new objects or using the KnownType attributes?
I think that your problem is that you are trying to send over a generic list.
It will work if you encapsulate the list in an object. That is create an object with a single public property which is the generic list.
You also need to make sure that all classes that are not used directly in the contract are marked as serializable.
If you want to return the derived objects then there will always be a round trip because the client and the service are separate. In order for the client to update its own list of MyBaseObjects it has to deserialize the list of MyDerivedObjects that came from the server.
The use of KnownType and/or ServiceKnownType is needed because this leads to the addition of that type information into WSDL, which is in turn used by the client to deserialize the messages to the correct type.
For starters, a useful tool for testing the scenario you've described: http://www.wcfstorm.com
You might try creating a DataContractSurrogate (IDataContractSurrogate) and returning your base type for the call to GetDataContractType. I'm not really sure that's how it was intended to be used so you still may be better of with "the extra work", but maybe I don't understand the scope of that extra work.
One of the problems with WCF (and .net remoting) is that it they tries to make “message passing” look like method calls.
This fall down when you try to use too many “oop” type designs.
The fact that the messages are
represented by .net classes, does not
make all of their behaviour like .net
class.
See this, and this, for more on the problem of Leaking Abstraction.
So you need to start thinking about message passing not object when designing your WCF interfaces, or you will hit lots of problems like this.
Related
Can anyone give me an example or point me to a resource on how to use Protobuf-Net to serialize/deserialize some of the bulit-in system classes?
Specifically I'm just trying to serialize/deserialize the base Exception class and all other exception classes that inherit from it. Will I have to create a new RunTypeModel that specifies every possible exception class that I will ever need to serialize, or can I somehow tell Protobuf-Net to serialize them all the same way without listing every single one?
Any help is very appreciated since I am brand new to Protobuf-Net and I'm still trying to understand it all.
protobuf-net is designed to serialize DTO models, but not exceptions - very similar to XmlSerializer etc (but binary rather than xml, obviously). Serialising exceptions is not currently built in. It may be possible to hack some things, but this isn't really a designed feature.
You really can´t serialize a class like
public class MyTest
{
[ProtoMember(1)]
public Exception MyException { get; set; }
}
But doing a small change will be possible to serialize
public class MyTest
{
[ProtoMember(1, DynamicType = true)]
public Object MyException { get; set; }
}
This was the only way that I found to serialize an exception.
So I have some class in a business logic .dll. It is not wrapped in a datacontract, I would like to expose it to anything calling the service by doing so in the Service and IService classes (for example). But the only examples I have seen have been to expose classes that are defined in the service, I do not wish to do this and I do not wish to use [Datacontract] in my business logic layer if that makes sense?
Ask if any clarification is required. Help is as always most appreciated.
Thanks :)
edit: I am slightly confused by many of these solutions, what I would like to do is provide the caller of the service a range of classes to instance and then pass back to the service through a method. So:
public Class ServiceConsumer{
addPerson(){
theService.addPerson(new theService.Person("Thomas", 22, "Male");
}
}
Does that make sense? That's a bit pseudo-codish as I can't remember the consumer side of WCF calls off the top of my head. All the solutions seem to require either knowledge of what classes are available or the classes mashed together in one class?
The only other solution I can see so far is to have a method for every class, but let me tell you there will be potentially a hundred classes!
Many thanks.
For starters, don't annotate the business object with [DataContract]. It's considered bad practice.
About 35 minutes into this video Miguel talks about data contracts.
What you need to use is a Data Transfer Object. It will make sure that there is proper separation between your Business Layer and the Service Layer. Also check this link.
While you should layer it properly, there are some cases where you dont really need the seperation of UI, Service, and Business Logic. Generally this happens when you are developing a smaller project, and its really not going to grow.
If you choose you still want to do this, see the example below. You are basically going to wrap your types in a Proxy like "RequestContract" In my case my BL types would be MyType and ByMyType. Those two classes are not annotated and they are brought in using DataContracts defined in the service.
public class ExampleService : IExampleService
{
public ExampleService() { }
public GetMyTypeResponseContract GetMyType(GetMyTypeRequestContract theType)
{
return new GetMyTypeResponseContract()
{
MyType = new MyType()
{
Response = theType.ByMyType.Request
}
};
}
}
[DataContract]
public class GetMyTypeRequestContract
{
[DataMember]
public ByMyType ByMyType { get; set; }
public GetMyTypeRequestContract() { }
}
[DataContract]
public class GetMyTypeResponseContract
{
[DataMember]
public MyType MyType { get; set; }
public GetMyTypeResponseContract() { }
}
Have you considered using POCO - http://msdn.microsoft.com/en-us/library/ee705457.aspx
From a technology point of view, you can use a surrogate.
I have a WCF service and I'm sharing types with a client in a shared assembly.
If the client create a derived class will it be possible to pass back the derived type to the service so that I can read the added properties through reflection ?
I tried but having issues with KnownTypes since the service don't know how to deserialize the derived type.
[Serializable]
public abstract class Car : ICar
{........
//on the client :
[Serializable]
public class MyCar : Car
{......
when passing myCar to Service I get the exception complaining about knownType but I cant add this on the server since I wont know what the client will be sending through and I want to handle extra properties through reflection.
Possible to register client types as knowntypes at runtime ?
Is this maybe the solution ?
http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx
This is not possible. Both service and client has to know what types will be sent in messages. If you want to use known type you have to define that relation to parent type on the service.
Why do you need to know added properties on the server?
I think there is a way.
I vaguely remember that when I studied WCF, I met ExtensionData which should be a mechanism to get everything that does not match the serialization of the class. for example, if you enable ExtensionData and you are in this situation
//Server
public class GenericRQ
{
public string GenericProperty {get;set;}
}
public Service GenericService
{
Public void GenericMethod(GenericRQ RQ)
{
}
}
// client
Public class MoreSpecificRQ : GenericRQ
{
public string SpecificProperty {get;set;}
}
At
Public void GenericMethod(GenericRQ RQ)
{
// the serializer adds automatically in RQ.ExtensionData everything that has come and that does not match the class GenericRQ.
}
On how to enable ExtensionData you to easily search on the web
Am I correct in thinking that if I have a WCF OperationContract takes in an object and needs to set a property on that object so the client gets the update, I need to declare it to return the object.
e.g. given a datacontract:
[DataContract]
public class CompositeType
{
[DataMember]
public int Key { get; set; }
[DataMember]
public string Something { get; set; }
}
this will not work with WCF:
public void GetDataUsingDataContract(CompositeType composite)
{
composite.Key = 42;
}
this will work:
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
composite.Key = 42;
return new CompositeType
{
Key = composite.Key,
Something = composite.Something
};
}
IMO, authoring methods that produce output via side-effects is a "bad" thing. Having said that however, are there circumstances that necessitate this model? Yes.
Certainly C# programming model permits this, is WCF broken? No. At a certain point, one must realise they are consuming WCF, and as a framework it attempts to satisfy a majority of use-cases [for instance, replicating all input parameters on all round trips to preserve implicit side effect semantics is, in a word, silly].
Of course, there are ways to work around this - C# also provides for explicit declaration of these scenarios and WCF supports these as well!
For instance
// use of "ref" indicates argument should be returned to
// caller, black-eye and all!
public void GetDataUsingDataContract (ref CompositeType composite)
{
composite.Key = 42;
}
Give it a go!
Hope this helps :)
If you use 'out of the box' WCF, you are actually using a form of webservices, that uses serialized versions of the objects that are sent from client to server.
This is the reason you cannot 'by reference' change properties on objects. You will always have to use a request / response pattern.
I know that a private parameterless constructor works but what about an object with no parameterless constructors?
I would like to expose types from a third party library so I have no control over the type definitions.
If there is a way what is the easiest? E.g. I don't what to have to create a sub type.
Edit:
What I'm looking for is something like the level of customization shown here: http://msdn.microsoft.com/en-us/magazine/cc163902.aspx
although I don't want to have to resort to streams to serialize/deserialize.
You can't really make arbitrary types serializable; in some cases (XmlSerializer, for example) the runtime exposes options to spoof the attributes. But DataContractSerializer doesn't allow this. Feasible options:
hide the classes behind your own types that are serializable (lots of work)
provide binary formatter surrogates (yeuch)
write your own serialization core (a lot of work to get right)
Essentially, if something isn't designed for serialization, very little of the framework will let you serialize it.
I just ran a little test, using a WCF Service that returns an basic object that does not have a default constructor.
//[DataContract]
//[Serializable]
public class MyObject
{
public MyObject(string _name)
{
Name = _name;
}
//[DataMember]
public string Name { get; set; }
//[DataMember]
public string Address { get; set; }
}
Here is what the service looks like:
public class MyService : IMyService
{
#region IMyService Members
public MyObject GetByName(string _name)
{
return new MyObject(_name) { Address = "Test Address" };
}
#endregion
}
This actually works, as long as MyObject is either a [DataContract] or [Serializable]. Interestingly, it doesn't seem to need the default constructor on the client side. There is a related post here:
How does WCF deserialization instantiate objects without calling a constructor?
I am not a WCF expert but it is unlikely that they support serialization on a constructor with arbitrary types. Namely because what would they pass in for values? You could pass null for reference types and empty values for structs. But what good would a type be that could be constructed with completely empty data?
I think you are stuck with 1 of 2 options
Sub class the type in question and pass appropriate default values to the non-parameterless constructor
Create a type that exists soley for serialization. Once completed it can create an instance of the original type that you are interested in. It is a bridge of sorts.
Personally I would go for #2. Make the class a data only structure and optimize it for serialization and factory purposes.