【问题标题】:Configuring WCF client and service for use with protobuf-net配置 WCF 客户端和服务以与 protobuf-net 一起使用
【发布时间】:2012-04-06 09:12:28
【问题描述】:

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

我想使用 protobuf-net 序列化/反序列化在我的 WCF 客户端和服务之间交换的消息。该服务在 Windows 服务中自托管。客户端和服务都以编程方式配置,使用与wsHttpBinding 非常相似的自定义绑定。服务参考代码是使用 Visual Studio 中的“添加服务参考”选项生成的。 WCF 服务上使用的 ORM 是 EntityFramework 4,它的代码是使用 EF 4.x POCO Generator 生成的。有关我的服务配置的更多信息可以在我开始的问题here 中找到(我在那里描述了我当前的序列化程序是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-netv1v2,所有解决方案都产生了相似的结果,这导致我相信我的客户根本没有反序列化。继续阅读。

让我们考虑一下我使用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 不能同意,因为它又说和之前说的一样。相同的内容长度和相同的消息正文。

这是怎么回事?

附:如果值得的话,这里是 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();

编辑

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

【问题讨论】:

  • 我请假几天(家庭假期)。会尽快看。
  • @MarcGravell 家族先行!慢慢来,这个问题不会跑掉 :-)

标签: wcf protobuf-net datacontractserializer custom-binding


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    相关资源
    最近更新 更多