私はこの問題について新しい質問を開くことにしました。インターネット上のどこにもこの問題についての正確な答えが見つからなかったので、おそらくこの質問を拡張しました。
protobuf-netを使用して、WCFクライアントとサービス間で交換されるメッセージをシリアル化/逆シリアル化したい。このサービスは、Windowsサービスで自己ホストされます。クライアントとサービスの両方がプログラムで構成され、に非常によく似たカスタムバインディングを使用しますwsHttpBinding
。サービス参照コードは、VisualStudioの[サービス参照の追加]オプションを使用して生成されます。WCFサービスで使用されるORMはEntityFramework4であり、そのコードはEF 4.xPOCOGeneratorを使用して生成されます。私のサービス構成の詳細については、ここで始めた質問を参照してください(ここで、現在のシリアライザーについて説明しましたDataContractSerialzizer
)。
カスタムDTOのリストを返す1つのサービス操作で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とメンバーを認識させるために、2つの回避策のいずれかを使用する必要がありました。
- DTOの共有アセンブリを使用する(EFで生成されたPOCOをクライアントに渡すため、カスタムDTOを除いて、私の場合は理想的なソリューションではありません)
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によって生成されたサービス参照を削除し、独自に作成してサービスコントラクト全体を共有しましたが、何も変更されていません。