次のようなサービス コントラクト インターフェイスがあります。
[ServiceKnownType(typeof(GeometricModelDescriptor))]
[ServiceKnownType(typeof(TemplateModelDescriptor))]
[ServiceContract]
public interface IModelRepository
{
[OperationContract]
ModelDescriptor GetDescriptor(ModelHandle model);
}
次のようないくつかの単純なデータ コントラクト タイプを使用します。
[DataContract]
public class ModelDescriptor
{
//...
}
[DataContract]
public sealed class GeometricModelDescriptor : ModelDescriptor
{
//...
}
GetDescriptor メソッドを呼び出そうとすると、クライアント プロキシが型を逆シリアル化できないことを示すシリアル化例外が発生します。
要素「http://tempuri.org/:GetDescriptorResult」には、「MyNamespace:GeometricModelDescriptor」という名前にマップされるタイプのデータが含まれています。デシリアライザーは、この名前にマップされる型を認識しません。DataContractResolver の使用を検討するか、'GeometricModelDescriptor' に対応する型を既知の型のリストに追加します。たとえば、KnownTypeAttribute 属性を使用するか、DataContractSerializer に渡される既知の型のリストに追加します。
私の理解では、ServiceKnownType 属性がこの例外を防ぐ必要があります。私は何が欠けていますか?
人々はクライアントコードを求めてきました。少し複雑ですが、クライアント プロキシ ラッパーを生成するロジックの核心は次のとおりです。
var baseType = typeof(ClientBase<>).MakeGenericType(typeof(TService));
var proxyType = _module.DefineType("ProxyType" + typeof(TService).Name,
TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public,
baseType,
new[] { typeof(TService) });
var constructor = proxyType.DefineConstructor(MethodAttributes.Public,
CallingConventions.HasThis,
new[] { typeof(ServiceEndpoint)});
var il = constructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, baseType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
null,
new[] {typeof(ServiceEndpoint)},
null));
il.Emit(OpCodes.Ret);
var interfaces = FlattenInterfaces(typeof(TService)).ToList();
foreach (var interfaceType in interfaces)
{
BuildInterfaceMethods(typeof(TService), proxyType, interfaceType);
}
これにより、ClientBase<IService>
も実装する子孫が作成されIService
ます。インターフェイスの実装 (BuildInterfaceMethods によって構築された) は、ClientBase によって提供される保護された Channel プロパティを介して各メソッド呼び出しをルーティングします。発行されたクラスはこれと同等です:
public sealed class ModelRepositoryProxy : ClientBase<IModelRepository>, IModelRepository
{
//Constructor omitted for brevity...
ModelDescriptor IModelRepository.GetDescriptor(ModelHandle model)
{
return Channel.GetDescriptor(model);
}
}
この回答で概説されているソリューションを使用することにAddGenericResolver
なりました。構築後すぐに、既知のタイプのサービスごとにクライアントでIDesign を呼び出します。これにより、指定された型を解決する方法を認識している DataContractSerializerOperationBehavior が登録されます。クライアントが型を解決できるようにするためにこれが必要な理由は明らかではありませんが、機能します。