我决定就这个问题提出一个新问题,也许扩展这个问题,因为在互联网上的任何地方都没有找到关于这个问题的准确答案。
我想使用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;
}
当我使用“添加服务参考”来生成参考代码时,我必须使用两种变通方法之一才能让我的客户识别ProtoContract
s 和成员:
- 为 DTO 使用共享程序集(在我的情况下,除了自定义 DTO,这不是一个理想的解决方案,因为我将 EF 生成的 POCO 传递给客户端)
- 使用
ProtoPartialMember
方法
我使用了它们,并且同时使用了protobuf -net的v1和v2,所有解决方案都产生了相似的结果,这让我相信我的客户根本没有反序列化。继续阅读。
让我们考虑一下我使用这种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 生成的服务引用并创建了自己的服务引用,共享整个服务合同,但没有任何改变。