Protobuf.net WCF Deserialize List

2020-07-18 03:32发布

问题:

I try use WCF with protobuf-net r.282

Ok. I mark my contracts with ProtoBehavior attribute

    [OperationContract,ProtoBehavior]
    [FaultContract(typeof(ServiceFaultException))]
    Dictionary<ActivityCategoryDTO, SalesTemplateDTO> GetSalesTemplates();

    [OperationContract, ProtoBehavior]
    [FaultContract(typeof(ServiceFaultException))]
    List<ActivityCategoryDTO> GetActivities();

Next, - DTO:

    [DataContract]
    [Serializable]
    [ProtoContract]
    public class ActivityCategoryDTO
    {
        [DataMember]
        [ProtoMember(1)]
        public int Id { get; set; }
        [DataMember]
        [ProtoMember(2)]
        public string Guid { get; set; }
        [DataMember]
        [ProtoMember(3)]
        public string Name { get; set; }
    }

I try consume this servise from client. When I call GetSalesTemplates - all is OK. I've got successful deserialized dictionary, but when I call GetActivities I get null at client. Through fiddler I see that, data is transmitting succesfully, so I think it's deserializer problem.

What's wrong? How can I get List at Client?

EDIT

It seems that I have problems with all Lists :)

[DataContract]
[Serializable]
[ProtoContract]
public class SalesTemplateDTO
{
    [ProtoMember(1)]
    [DataMember]
    public string Name { get; set; }
    [ProtoMember(2)]
    public List<FieldTemplateDTO> Fields;
}

It comes to client just with Name, List of Fields is null again. Though all data is transmitted too.

回答1:

OK; I've reproduced and this actually looks to be the IDE/svcutil refusing to re-use the service-contract (interface) from your DTO assembly, even though it is re-using the data-contracts (classes). The mex-generated service-contract (interface) lacks the necessary extra attributes, so protobuf-net never gets asked to deserialize.

Two options:

Option 1

use configuration rather than code to define the protobuf usage; the advantage of this is that it can be used without any changes to either client or server.

Option 2

don't use the generated service wrapper - a generic counterpart can be written simply as:

public class Client<T> : ClientBase<T> where T : class
{
    public T Service { get { return Channel; } }
        public Client() {
    }

    public Client(string endpointConfigurationName) : 
            base(endpointConfigurationName) {
    }

    public Client(string endpointConfigurationName, string remoteAddress) : 
            base(endpointConfigurationName, remoteAddress) {
    }

    public Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(endpointConfigurationName, remoteAddress) {
    }

    public Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress) {
    }
}

Then consume as:

using (var svc = new Client<IService1>())
{
    var data = svc.Service.GetActivities();
}


回答2:

I don't know off the top of my head; I can investigate, but not right at this moment. As a pragmatic workaround (until I can find why), perhaps return something like a ActivityResult class that contains a List<ActivityCategoryDTO> as a member?