WCF, Interface return type and KnownTypes

2019-01-11 00:01发布

问题:

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.

回答1:

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();

    }