Using KnowTypeAttribute in WCF Service - wcf

I have a situation where I need to pass a list of objects to my service. The objects have to be of type ELEMENT. I have my Element interface defined like so
public interface IElement{ }
Then I have my DataContracts inheriting this IElement class Like so . . . .
[KnownType(typeof(Common.IElement))]
[DataContract]
public abstract class IPet : IElement
{.....}
I also have a KnownType attribute on my Service interface like so
[ServiceContract(Name="Pets", SessionMode = SessionMode.Allowed)]
[ServiceKnownType(typeof(Memberships.PetServiceUser))]
[ServiceKnownType(typeof(.Common.IElement))]
[DeliveryRequirements(RequireOrderedDelivery=true)]
public interface IPetService {.....}
Problem is on the client side, the IElement type is not available on deserialization of service types on client. Any idea what I may be doing wrong here and how I can go about correcting this please?
None

I'm not 100 percent sure I understand everything you're trying to do here, but it seems upside-down to me. The usual way to use the KnownType attribute is to decorate the base type with the derived types. Something along the lines of:
[DataContract]
[KnownType(typeof(Pet))]
[KnownType(typeof(...
...
public class Element: IElement
{
....

Related

using custom types in a WCF service

I'm new in WCF and now stuck in something about using custom types in WCF service.
I have two classes Class1 and Class2 in TestClass project
public Class1: ArrayList{
public string street;
}
public Class2{
public string name;
public string address;
}
My WCF service TestService include function DoSomething using two above classes
public string DoSomething(Class1 c1){
return c1.street;
}
And when try to call this function
Class1 c1 = new Class1();
Class2 c2 = new Class2();
c1.Add(c2);
ServiceClient1.Dosomething(c1);
I get the Exception
There was an error while trying to serialize parameter http://tempuri.org/:c1. The
InnerException message was 'Type 'WebApplication1.Class2' with data contract name
'Class2:http://schemas.datacontract.org/2004/07/WebApplication1' is not expected. Add
any types not known statically to the list of known types - for example, by using the
KnownTypeAttribute attribute or by adding them to the list of known types passed to
DataContractSerializer.'. Please see InnerException for more details.
Can anyone tell me how to add DataContract for a class defined outside the WCF service, and how to solve this problem.
Thanks a lot!
Add the lines below to your service interface declaration (add them just below the ServiceContract attribute):
[ServiceKnownType(typeof(Class1))]
[ServiceKnownType(typeof(Class2))]
alternatively, and this is the recommended approach, define your set of DTO objects exported by the service and decorate them with [DataContract] and [DataMember] attributes.

Do WCF and DataContractSerializer serialize CollectionDataContract-decorated collection types differently?

I have a really simple customized collection type that inherits from List<> and uses a CollectionDataContract.
When I use DataContractSerializer.WriteObject to serialize it, it respects the CollectionDataContract attribute the way I'd expect; however, when I use it as a return type for a WCF method, I get the default ArrayOfFoo.
I'm wondering if there is some decoration I'm missing in the service contract.
Details:
[DataContract(Namespace = "")]
public class Foo
{
[DataMember]
public string BarString { get; set; }
}
[CollectionDataContract(Namespace = "")]
[Serializable]
public class FooList : List<Foo> {}
If I just instantiate a Foo and then use DataContractSerializer.WriteObject to serialize it, I get what you'd expect:
<FooList>
<Foo>
<BarString>myString1</BarString>
</Foo>
</FooList>
However, if I have a service with a method like this...
[ServiceContract Name = "MyService"]
public interface IMyService
{
[OperationContract, WebGet(UriTemplate = "foos/")]
FooList GetAllFoos();
}
and then do a GET for http://www.someEndpoint.com/foos/, I get this:
<ArrayOfFoo>
<Foo>
<BarString>myString1</BarString>
</Foo>
</ArrayOfFoo>
I've also tried specifying Name="MyFooListName" in the CollectionDataContract attribute. Same results: DataContractSerializer gets the memo; WCF doesn't.
Saeed sent me in the right direction: I inadvertently ended up with XmlSerializer, when I had been hoping for DataContractSerializer.
I had ended up with XmlSerializer... well... by asking for it.
In particular, I had decorated methods in my service with the XmlSerializerFormat like this:
[ServiceContract Name = "MyService"]
public interface IMyService
{
// ... other stuff ...
[OperationContract, WebInvoke(UriTemplate = "foos/", Method = "POST")]
[XmlSerializerFormat]
Foo PostAFoo(Foo yourNewFoo);
}
I had done this in the hopes of forgiving member order in hand-rolled Foo XML blobs. Of course, when one does this one ends up with XmlSerializer, not DataContractSerializer.
When I take away the XmlSerializerFormat attribute, problem solved: WCF is now serializing my FooList collection the way I want.
See MSDN for detail:
The DataContractSerializer does not
support the programming model used by
the XmlSerializer and ASP.NET Web
services. In particular, it does not
support attributes like
XmlElementAttribute and
XmlAttributeAttribute. To enable
support for this programming model,
WCF must be switched to use the
XmlSerializer instead of the
DataContractSerializer.
So the serialization going to be done by XMLSerializer, and you can't change it.
Have you selected Generic types while configuring your WCF service? if not then,
right click and go to configuration, then select Generic Type, by default it is arraylist type.

WCF with shared objects and derived classes on client

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

WCF client proxy exception - "Type cannot be added to list of known types"

I am having problems creating WCF client proxy for service code like in this example:
// data classes
[KnownType(typeof(ClassA))]
[KnownType(typeof(ClassB))]
public abstract class BaseClass : Dictionary<string, ITest>
{
}
public class ClassA : BaseClass
{
}
public class ClassB : BaseClass
{
}
public interface ITest
{
}
// service
[ServiceContract]
public interface IService1
{
[OperationContract]
BaseClass Method();
}
public class Service1 : IService1
{
public BaseClass Method()
{
...
}
}
Whenever I try to create a WCF proxy using "Add Service Reference" in VS it fails and trace log says
Type 'WcfProxyTest.ClassA' cannot be added to list of known types since another type 'WcfProxyTest.ClassB' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfKeyValueOfstringanyType' is already present. If there are different collections of a particular type - for example, List<Test> and Test[], they cannot both be added as known types. Consider specifying only one of these types for addition to the known types list.
I can see what the error message is saying, but is there any other way around this (other than refactoring the classes). I am dealing with a legacy system which has classes written in the same manner as in my example and rewriting them is not an option as this stuff sits in the very core of the system :S
Any ideas? Thanks!
I decided to refactor the code in such a way that I don't have to provide two KnownTypes which gets me around the problem. About 300 syntax errors later that worked. I would be interested in any other ways of doing it though...
Try adding:
[KnownType(typeof(Dictionary<string, ITest>))]

WCF - serializing inherited types

I have these classes:
[DataContract]
public class ErrorBase {}
[DataContract]
public class FileMissingError: ErrorBase {}
[DataContract]
public class ResponseFileInquiry
{
[DataMember]
public List<ErrorBase> errors {get;set;};
}
An instance of the class ResponseFileInquiry is what my service method returns to the client. Now, if I fill ResponseFileInquiry.errors with instances of ErrorBase, everything works fine, but if I add an instance of inherited type FileMissingError, I get a service side exception during serialization:
Type 'MyNamespace.FileMissingError' with data contract name 'FileMissingError'
is not expected. Add any types not known statically to the list of known types -
for example, by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.'
So serializer is getting confused because it's expecting the List to contain the declared type objects (ErrorBase) but it's getting inherited type (FileMissingError) objects.
I have the whole bunch of error types and the List will contain combinations of them, so what can I do to make it work?
You should add KnownType attribute to your base class
[DataContract]
[KnownType(typeof(FileMissingError))]
public class ErrorBase {}
Read more about KnownType attribute in this blog
Try this:
[DataContract]
[KnownType(typeof(FileMissingError))]
public class ErrorBase {}
As the error message states, any information that cannot be know statically (like the polymorphic relationship you have expressed here) must be supplied via attributes. In this case you need to specify that your FileMissingError data contract is a known type of its base class, ErrorBase.
A tad bit late, but maybe for future generations. =)
If you don't want to add an attribute for every child class to your parent class, you could construct a list of known types in the parent classes static constructor using
IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => !a.GlobalAssemblyCache);
IEnumerable<Type> serializableTypes = assemblies.SelectMany(a => a.GetTypes())
.Where(t => IsSerializable(t));
// ...
private static bool IsSerializable(Type type)
{
return type.GetCustomAttributes(true).Any(a => a is DataContractAttribute);
}
and pass this list to the de/serializers constructor. I don't know how robust this solution is, but that's what I am doing and so far it works. It is a little slow, so make sure to cache the result.