1

我决定就这个问题提出一个新问题,也许扩展这个问题,因为在互联网上的任何地方都没有找到关于这个问题的准确答案。

我想使用protobuf-net序列化/反序列化我的 WCF 客户端和服务之间交换的消息。该服务在 Windows 服务中自托管。客户端和服务都以编程方式配置,使用非常类似于wsHttpBinding. 服务参考代码是使用 Visual Studio 中的“添加服务参考”选项生成的。WCF 服务上使用的 ORM 是EntityFramework 4,它的代码是使用EF 4.x POCO Generator 生成的。有关我的服务配置的更多信息可以在我从这里开始的问题中找到(这就是我描述我当前的序列化程序的地方DataContractSerialzizer)。

我只用一个返回自定义 DTO 列表的服务操作测试了protobuf-net 。这是操作(请注意,我只是将我的代码复制粘贴到这里,可能有一些字段以我的母语命名,而不是英语):

    public static List<OsobaView> GetListOsobas()
    {
        Database DB = new Database(); // EF object context
        var retValue = DB.Baza.Osoba
                       .Select(x => new OsobaView
                       {
                           ID = x.ID,
                           Prezime = x.Prezime,
                           Ime = x.Ime,
                           Adresa = x.Adresa,
                           DatumRodjenja = x.DatumRodjenja,
                           JMBG = x.JMBG
                       });
        return retValue.ToList();
    }

这是OsobaView类的定义:

    [ProtoContract]
    public class OsobaView
    {
        [ProtoMember(1)]
        public int ID;
        [ProtoMember(2)]
        public string Prezime;
        [ProtoMember(3)]
        public string Ime;
        [ProtoMember(4)]
        public string Adresa;
        [ProtoMember(5)]
        public DateTime DatumRodjenja;
        [ProtoMember(6)]
        public string JMBG;
    }

当我使用“添加服务参考”来生成参考代码时,我必须使用两种变通方法之一才能让我的客户识别ProtoContracts 和成员:

  • 为 DTO 使用共享程序集(在我的情况下,除了自定义 DTO,这不是一个理想的解决方案,因为我将 EF 生成的 POCO 传递给客户端)
  • 使用ProtoPartialMember方法

我使用了它们,并且同时使用了protobuf -net的v1v2,所有解决方案都产生了相似的结果,这让我相信我的客户根本没有反序列化。继续阅读。

让我们考虑一下我使用这种ProtoPartialMember方法的情况。起初我使用v2。我喜欢ProtoOperationBehavior可以使用的方式。这是要调用的服务操作:

    [ProtoBuf.ServiceModel.ProtoBehavior]
    public List<OsobaView> GetListOsobas()
    {
        return OsobaQueries.GetListOsobas();
    }

以下是我如何替换DataContractSerializerOperationBehavior客户ProtoOperationBehavior所需的服务操作:

    OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
    if (op != null)
    {
        DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
        if (dcsBehavior != null)
            op.Behaviors.Remove(dcsBehavior);
        op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
    }

当然,这里是上面提到的 DTO 的变通实现:

    [ProtoPartialMember(1, "ID")]
    [ProtoPartialMember(2, "Prezime")]
    [ProtoPartialMember(3, "Ime")]
    [ProtoPartialMember(4, "Adresa")]
    [ProtoPartialMember(5, "DatumRodjenja")]
    [ProtoPartialMember(6, "JMBG")]
    [ProtoContract]
    public partial class OsobaView
    {
    }

现在,当我从客户端调用此服务操作时,我得到null. 但提琴手不同意。它清楚地表明,在响应标题中:

    Content-Length: 1301963
    Content-Type: application/soap+xml; charset=utf-8

...在消息正文中:

    <s:Body>
      <GetListOsobasResponse xmlns="http://tempuri.org/">
        <proto>CkMIpHES .../* REALLY LONG RESPONSE */... IyMDAxOA==</proto>
      </GetListOsobasResponse>
    </s:Body>

然后我想,让我们试试v1。在服务方面,我没有太大变化。我刚刚删除了对v2 .DLL 的引用并将其替换为对v1 .DLL 的引用。在客户端,我不得不删除添加ProtoOperationBehavior到我的服务操作行为的代码,并添加了以下行:

    Service.Proxy.Endpoint.Behaviors
        .Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());

我启动了它,调用了操作,这次结果不是null. 这次是空白字段列表。再一次,Fiddler不能同意,因为它又说和之前说的一样。相同的内容长度和相同的消息正文。

这里发生了什么?

PS 如果值得的话,这里是 WCF 配置:

    CustomBinding customBinding = new CustomBinding();
    customBinding.CloseTimeout = TimeSpan.FromMinutes(10);
    customBinding.OpenTimeout = TimeSpan.FromMinutes(10);
    customBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
    customBinding.SendTimeout = TimeSpan.FromMinutes(10);
    HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
    httpsBindingElement.AllowCookies = false;
    httpsBindingElement.BypassProxyOnLocal = false;
    httpsBindingElement.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
    httpsBindingElement.MaxBufferPoolSize = 20480000;
    httpsBindingElement.MaxBufferSize = 20480000;
    httpsBindingElement.MaxReceivedMessageSize = 20480000;
    httpsBindingElement.RequireClientCertificate = true;
    httpsBindingElement.UseDefaultWebProxy = true;
    TransportSecurityBindingElement transportSecurityElement = new TransportSecurityBindingElement();
    transportSecurityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UserNameSecurityTokenParameters());
    transportSecurityElement.EndpointSupportingTokenParameters.SetKeyDerivation(false);
    TransactionFlowBindingElement transactionFlowElement = new TransactionFlowBindingElement();
    TextMessageEncodingBindingElement textMessageEncoding = new TextMessageEncodingBindingElement();
    textMessageEncoding.MaxReadPoolSize = 20480000;
    textMessageEncoding.MaxWritePoolSize = 20480000;
    textMessageEncoding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
    ReliableSessionBindingElement reliableSessionElement = new ReliableSessionBindingElement();
    reliableSessionElement.ReliableMessagingVersion = ReliableMessagingVersion.WSReliableMessagingFebruary2005;
    customBinding.Elements.Add(transportSecurityElement);
    customBinding.Elements.Add(transactionFlowElement);
    customBinding.Elements.Add(textMessageEncoding);
    customBinding.Elements.Add(reliableSessionElement);
    customBinding.Elements.Add(httpsBindingElement);

    EndpointAddress endpoint = new EndpointAddress(new Uri(ServiceAddress));
    Service.Proxy = new BazaService.BazaClient(customBinding, endpoint);
    Service.Proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, CertificateSubject);
    CustomBehavior behavior = Service.Proxy.Endpoint.Behaviors.Find<CustomBehavior>();
    if (behavior == null)
    {
        Service.Proxy.Endpoint.Behaviors.Add(new CustomBehavior()); // message inspector
    }
    Service.Proxy.Endpoint.Contract.Behaviors.Add(new CyclicReferencesAwareContractBehavior(true));
    Service.Proxy.Endpoint.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());

    /* code used for protobuf-net v2

    OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
    if (op != null)
    {
        DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
        if (dcsBehavior != null)
            op.Behaviors.Remove(dcsBehavior);
        op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
    } */

    Service.Proxy.ClientCredentials.UserName.UserName = LogOn.UserName;
    Service.Proxy.ClientCredentials.UserName.Password = LogOn.Password;
    Service.Proxy.Open();

编辑

为了提供更多信息,我已经阅读了那里写的内容,但没有帮助。我删除了 Visual Studio 生成的服务引用并创建了自己的服务引用,共享整个服务合同,但没有任何改变。

4

1 回答 1

1

在稍微集中注意力之后,我决定从头开始重新启动解决方案。我用它的 POCO 为 EDMX 创建了一个类库,一个用于ServiceContractDataContracts,一个用于实际的 WCF 服务实现。然后我与 WCF 客户端共享了这两个包含ServiceContractDataContracts 的库和 POCO 并再次尝试,产生了与以前相同的结果。在尝试了其他一些不使用protobuf-net进行序列化的操作后,结果发现它们的行为与第一个相同,导致空字段(!)。

问题是,.datasource在我决定使用程序集共享技术后,我在重构时搞砸了我的 WCF 客户端文件。所以这是一个典型的 PEBKAC,如果做得好,它当然可以正常工作。与protobuf-net的出色合作,Marc Gravell

于 2012-04-10T08:19:36.170 回答