KnownType Exception with WCF - c#

I have following Service Interface
[ServiceContract]
public interface ICacheService
{
[OperationContract]
IEnumerable<CacheResponse> GetCache(IEnumerable<CacheRequest> requests);
}
[DataContract]
[KnownType(typeof(CacheItem))]
[KnownType(typeof(TreeItem))]
[KnownType(typeof(TreeTopGroup))]
[KnownType(typeof(TreeGroup))]
[KnownType(typeof(TreeView))]
[KnownType(typeof(ITreeItem))]
public class CacheResponse
{
[DataMember]
public IDictionary<string, CacheItem> CacheItems { get; set; }
[DataMember]
public IEnumerable<KCSLuceneDocument> LuceneDocuments { get; set; }
}
The request works so i won't post it. The Response only contains a DateTime, a String and a List of TreeItem.
Important part of the TreeItem Class
public class TreeItem : ITreeItem
{
public ITreeItem Parent { get; set; }
.
. more stuff
}
As soon as the parent Property is set to something other then null, the client gets a System.ServiceModel.Dispatcher.NetDispatcherFaultException.
Stating that the elment
\"http://schemas.datacontract.org/2004/07/Core.Base:_x003C_Parent_x003E_k__BackingField\" contains Data of the Type \"http://schemas.datacontract.org/2004/07/Core.Base:TreeItem\" and that the deserializere doesn't know any Type with that name.
I tried using the KnownType Attribute as well as the ServiceKnownType Attribute.
Both didn't help.
The only thing that worked is changing the type of Parent to TreeItem which i really don't want to do. Especially as it can contain ITreeItems in some other Locations which most likely also break the Service.
Any idea how to solve the problem?

Do you have the [DataContract] attribute on TreeItem? That would be required. if the TreeItem does have a [DataContract] attribute set, I have a few other ideas, but it may require a bit more of the code to be posted. So give it a try and let me know if I can be of more help.
A hint as to what the other issues may be can be found on the MSDN for Data Contract Known Type.
The KnownType is needed when the class marked with the Data Contract attribute meets one of the following:
The sent data contract is derived from the expected data contract. For more information, see the section about inheritance in Data Contract Equivalence). In that case, the transmitted data does not have the same data contract as expected by the receiving endpoint.
The declared type for the information to be transmitted is an interface, as opposed to a class, structure, or enumeration. Therefore, it cannot be known in advance which type that implements the interface is actually sent and therefore, the receiving endpoint cannot determine in advance the data contract for the transmitted data.
The declared type for the information to be transmitted is Object. Because every type inherits from Object, and it cannot be known in advance which type is actually sent, the receiving endpoint cannot determine in advance the data contract for the transmitted data. This is a special case of the first item: Every data contract derives from the default, a blank data contract that is generated for Object.
Some types, which include .NET Framework types, have members that are in one of the preceding three categories. For example, Hashtable uses Object to store the actual objects in the hash table. When serializing these types, the receiving side cannot determine in advance the data contract for these members.
source
If it is marked with [DataContract] you will likely need to add a [ServiceKnownType] attribute on the TreeItem class itself to show possibilities for the TreeItem.Parent property. That is just a conjecture, I would need to see a bit more code first.

Related

ServiceRemoting V2_1 still throwing serialization exception when working with interface return types

over the last few days, I tried to equip my application with the service remoting IPC stack. I initially implemented the V2 version, but just noticed thanks to this post (https://github.com/Azure/service-fabric-issues/issues/735) that that does not support returning interfaces.
So, I just now made the switch to V2_1.
However, I still face this problem:
One or more errors occurred. (Type 'MooMed.Core.DataTypes.Session.SessionContext' with data contract name 'SessionContext:http://schemas.datacontract.org/2004/07/MooMed.Core.DataTypes.Session' 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.)'
This is the service method that is being called on the endpoint:
[CanBeNull]
public Task<ISessionContext> GetSessionContext(int accountId):
The involved classes/intefaces look like this:
public interface ISessionContext
{
Account Account { get; set; }
}
[DataContract]
public class SessionContext : ISessionContext
{
[DataMember]
public Account Account { get; set; }
}
Also, as I mentioned, I changed my remoting version from V2 to V2_1 just now, so this is added to the Service class which contains the GetSessionContext method:
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1, RemotingClientVersion = RemotingClientVersion.V2_1)]
So, according to the docs (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting#use-the-remoting-v2-interface-compatible-stack) I should now be fully equipped to have everything working properly.
The only thing I see myself doing differently than the tutorial is how I declare the endpoint listeners. I'm doing this via FabricTransportServiceRemotingListener like this:
public static ServiceReplicaListener CreateTypedListener([NotNull] IService service)
{
return new ServiceReplicaListener(context => new FabricTransportServiceRemotingListener(context, service),
$"I{service.GetType().Name}");
}
However, I don't see how this could be the culprit, as the request comes in properly and I don't think declaring endpoints does necessarily interfere with how responses are serialized.
So, what am I doing wrong here?
The DataContractSerializer can't serialize an interface. (see how to mark an interface as DataContract in WCF).
However, you can write your own custom serializer to achieve this. From that article you posted at the bottom are instructions on how to do this. You can also look at my example of implementing protobuf-net here:
https://github.com/mikeruhl/Frenetik.Fabric.Remoting.Protobuf
My example should not be used in production. It's a work in progress and there are zero tests written for it at the moment.

Generic argument property causes error on WebService

Service:
Error:
The error is with a type that has ChainedListNode<T>. Thing is, when I remove the DataMemberAttribute from Value, the service works.
[DataContract]
public class ChainedListNode<T>
{
[DataMember]
public T Value { get; set; }
}
Any ideas to what's causing it and/or how to solve it?
The problem is that the type parameter in the open type ChainedListNode<T> means that ChainedListNode<T>.Value could contain anything at all. WCF can't create a contract which describes all the possible values which could be placed in the Value property, so it rejects the whole type. When there is no Value property, the type parameter T is irrelevant and is ignored, and everything works OK.
In similar situations I've created a closed type derived from my generic type and used that type as my data contract:
[DataContract]
public class ChainedListNodeOfString : ChainedListNode<string>
{
[DataMember]
public string Value { get; set; }
}
If you need to, you can create a derived type (and a related OperationContract) for each different kind of value you need to return. This makes your API more verbose, but it works.
I do no think that generic is a good idea to use in the WCF, because I do not see a good serialization going in this case, even if you can achive it, although I am not sure it possible, I would think that you may still get error eventually.
The reason why it work when you remove DataMember is because it not getting serialized theoretically not used in the service only on the backed end.

WCF service contract; calling a class method from client

Working with WCF service I met the following problem: calling the service from a client (simple console application), I can not access the method of a class marked with [DataContract] and [ServiceContract], even if this method is marked with [OperationContract] (actually, I've tried all the possible combinations of attributes so far :D ). Is there a way to resolve it? I'm missing some points here, I guess, but still can't handle it, need one's help ^^
Here is the code of a class:
[ServiceContract]
[DataContract]
public class AmountSpecification : IOrderSpecification
{
[DataMember]
public int Amount {get ; set;}
public AmountSpecification(int amount)
{
Amount = amount;
}
public bool IsSatisfiedBy(Order o)
{
return o.Amount >= Amount;
}
[OperationContract]
public IOrderSpecification And(IOrderSpecification specification)
{
return new AndSpecification(this, specification);
}
}
Your method returns IOrderSpecification.
So, on the client side, there is no indication on which implementation is used, (AmountSpecification , AndSpecification, etc) and so the client will not create the correct instance type.
You need to add Well Known Types attribute on your interface. This will allow the wsdl to contains definitions for the implementation, and so the client will have the information.
You're mixing up service contract and data contract in one class. You only need the DataContract and DataMember attributes to create a data contract, and those aren't even required.
However, when you generate a service reference from a service using this data contract, and the project you're adding the service reference to doesn't have a reference to the assembly where AmountSpecification is defined, it will generate a data contract with only the given name and properties.
If you want to use the class in its enitrety, you have to reference the assembly that class is defined in in your client appliaction, and check "Reuse types in referenced assemblies" in your service reference configuration. See Service reference complex types.

WCF, Interface return type and KnownTypes

I'm creating a WCF service, and I'm having a lot of trouble with some Serialization issues. Perhaps there's just 1 way to do it, but i'd like to confirm it
Here's my sample code :
Contracts
public interface IAtm
{
[DataMember]
double Latitude { get; set; }
[DataMember]
double Longitude { get; set; }
}
[ServiceContract]
public interface IAtmFinderService
{
[OperationContract]
ICollection<IAtm> GetAtms();
}
Service Implementation :
[KnownType(typeof(Atm))]
[KnownType(typeof(List<Atm>))]
[ServiceKnownType(typeof(Atm))]
[ServiceKnownType(typeof(List<Atm>))]
public class AtmFinderService : IAtmFinderService
{
public ICollection<IAtm> GetAtms()
{
return new List<IAtm>()
{
new Atm() { Latitude = 1, Longitude = 1 },
new Atm() { Latitude = 2, Longitude = 2 }
};
}
}
I added all of the KnownType and ServiceKnownType attributes because i thought that there was something missing there..
So now, i've been doing some tests. I tried creating a console app, using the "add service reference" method to make VS create automatically the proxy. This way, I get a function like
object[] GetAtms();
When trying to call it, i get this error :
The InnerException message was 'Type
'WCFTest.Atm' with data contract name
'Atm:http://schemas.datacontract.org/2004/07/WCFTest'
is not expected. Consider using a
DataContractResolver or 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.'.
Very nice... So then, I think that VS's autogenerated code is crap. I did the following change in my service (and all the related classes and implementations) :
[OperationContract]
ICollection<Atm> GetAtms();
So now, i'm returning a concrete type. After updating the service reference, it creates a copy of the Atm class, with its members and stuff.
After calling the service, the call succeeds.
I thought that this was some bad behaviour related to the autogenerated code, so i tried creating a very simple host/client app. I started a console host listening on some port, then created a client that uses the ClientBase class to make a call to the service. Same behaviour... if the service is implemented returning an interface type, it fails. If i change it to return the concrete type, it works. I think that i have some problem with the KnownType attributes, i must be missing something that the serializer can't process. but i don't know what.
Ok, i managed to fix it
The problem, as I see it, was this
Since I'm returning an interface and not a concrete class, WCF doesn't know what to expect on the other end. So, it can be anything. When he gets a List, he's confused. The correct way to do it was to add the KnownType attributes where needed.
Who needs to know those types? the service implementation, to serialize and deserialize them correctly. However, the client talks with the interface of the service, not with the implementation itself. That's why adding theKnownType attribute in the service implementation didn't work
The problem here is that, interfaces don't allow KnownType attributes, but they do allow ServiceKnownType attributes. The solution to the problem was to add the expected type in the service interface contract, and voila, everything works ok and using interfaces
[ServiceContract]
[ServiceKnownType(typeof(Atm))]
[ServiceKnownType(typeof(List<Atm>))]
public interface IAtmFinderService
{
[OperationContract]
ICollection<IAtm> GetAtms();
}

WCF - multiple service contracts using pretty same data contracts

I have a new question for WCF gurus.
So, I have a class User which is close to the 'User' representation from the DB which I use for database operations. Now, I would like to have 2 different service contracts that use this class as data contract, but each in their own way... I mean,
public class DBLayer
{
void InsertUsers(List<User> userList)
{
// both 'PropertyVisibleForService1' and 'PropertyVisibleForService2'
// are used HERE to be inserted into their columns
}
}
[DataContract]
public class User
{
[DataMember] public string PropertyVisibleOnlyForService1{...}
[DataMember] public string PropertyVisibleOnlyForService2{...}
}
[ServiceContract]
public interface IService1
{
List<User> GetUsers(); // user with 'PropertyVisibleOnlyForService1' inside
}
[ServiceContract]
public interface IService2
{
List<User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside
}
So, the idea is that each service will get a different kind of user, subset of 'User'. Keeping in mind that I want to use the 'User' as is for DB operations, what would be my options to achieve this? Do I really need to create different data contracts or is there another smarter way?
Best would be to not only give me the solution, but also to explain me some best practices and alternatives.
Thank you in advance.
EDIT1:
I added a dummy DBLayer class here for a better overview and why I think the inheritance may not be good in this case.
A solution would be of having another 'UserForService1' and 'UserForService2' as data contracts which would map at the end from/into an 'User' but I wanted some other points of view.
EDIT2: Very good article which helped me in this case: http://bloggingabout.net/blogs/vagif/archive/2009/03/29/iextensibledataobject-is-not-only-for-backward-compatibility.aspx
You could create separate DTO's for each service but your case would actually be ideal for a Decorator pattern:
[DataContract]
public class UserForService1 : User
{
private User mUser;
public UserForService1(User u)
{
mUser = u;
}
//expose only properties you'd like the user of this data contract to see
[DataMember]
public string SomeProperty
{
get
{
//always call into the 'wrapped' object
return mUser.SomeProperty;
}
set
{
mUser.SomeProperty = value;
}
}
// etc...
}
and for Service2 similar code, that exposes only what you care for there...
If they are designed to represent different types of users, they should be different classes. I agree with phoog in the comments, you should derive the type you want from the shared User class and add the specific service properties to the derived classes.
Why don't you think inheritance would be good in this case? If you give us some more details, we could try to revise the suggestions to suit your actual problem.
As suggested in the comment, you can have two classes deriving from a base User then using Data Contract Known Types, you can accomplish your desired goal. See the following links for more examples.
http://www.freddes.se/2010/05/19/wcf-knowntype-attribute-example/
http://footheory.com/blogs/bennie/archive/2007/07/28/handling-data-contract-object-hierarchies-in-wcf.aspx
If you don't want to use inheritance, something like:
[DataContract]
public class User
{
}
[DataContract]
public class Service1User : User
{
[DataMember] public string PropertyVisibleOnlyForService1{...}
}
[DataContract]
public class Service2User : User
{
[DataMember] public string PropertyVisibleOnlyForService2{...}
}
[ServiceContract]
public interface IService1
{
List<Service1User> GetUsers(); // user with 'PropertyVisibleOnlyForService1' inside
}
[ServiceContract]
public interface IService2
{
List<Service2User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside
}
Then I'm not sure what you would do. Your sortof breaking the principals of an type declaration at that point. Think of it in a normal .NET way; if you define "User" in your application, then it is the same type everywhere. Some properties cant be hidden from certain other classes or methods.
WCF is also going to pack this type information into the generated WSDL, and it is only going to define the User type once, so it needs to know what properties are there.
Now, if all you care about is the actual SOAP message that is constructed, and you don't care about the WSDL or what any clients generated off the WSDL will see, then technically you can have it not emit that property into the SOAP message when it is null, by doing:
[DataMember(EmitDefaultValue=false)]
Then when that property is null, it wont be included in the serialization. That would make no real difference if the client was generated from the WSDL though, as its User type would still have to contain both properties. It would just change the serialization so that instead of sending the client something like:
<User>
<PropertyVisibleOnlyForService1 nil="true" />
<PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>
it would instead send:
<User>
<PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>

Categories