クライアントとサービス間の共有サービス コントラクトとの通信に WCF を使用するアプリケーションがあります。この種の serviceContract の場合:
[ServiceContract]
public interface IMyContract
{
[OperationContract]
IEnumerable<string> MyOperation();
}
IEnumerable のデフォルトの実装は配列です。実装を別のものにカスタマイズしたいと思います: たとえば、リスト、ハッシュセット...
私のクライアント プロキシは自動生成されず (svcmap ファイルはありません)、コントラクト内の別の型の IEnumerable を変更したくありません (WCF の逆シリアル化を理解している限り、この場合は既知の型が機能せず、CollectionDataContractAttribute も機能しません。 . そうですか?)。
RealProxy ラッピング チャネル生成 (リサイクル管理用) を作成したので、今のところ結果をインターセプトして、その場でコレクション タイプを変更できます...
しかし、独自のシリアライザーを最初から作成することなく、クライアント側で IEnumerable の実装を選択するために、何らかの形で逆シリアル化に影響を与える方法があるかもしれません。
私の問題がどこかの悪いアプローチによるものであるかどうかにかかわらず、私の質問が十分に明確でない場合は、躊躇しないでください。
前もって感謝します!
更新: Jury Soldatenkov が良い解決策を教えてくれました!
IEnumerable 実装を選択できるようにする私の POC は次のとおりです。
private class CustomDataContractResolver<TCollection> : DataContractResolver where TCollection : class, IEnumerable<object>
{
/// <summary>
/// Maps a data contract type to an xsi:type name and namespace during serialization.
/// </summary>
/// <param name="type">The type to map.</param>
/// <param name="declaredType">The type declared in the data contract.</param>
/// <param name="knownTypeResolver">The known type resolver.</param>
/// <param name="typeName">The xsi:type name.</param>
/// <param name="typeNamespace">The xsi:type namespace.</param>
/// <returns>
/// true if mapping succeeded; otherwise, false.
/// </returns>
public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
{
if (declaredType != null
&& declaredType.IsGenericType
&& declaredType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
typeName = new XmlDictionary().Add(typeof(IEnumerable<>).Name);
typeNamespace = new XmlDictionary().Add("custom");
return true;
}
return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
}
/// <summary>
/// Maps the specified xsi:type name and namespace to a data contract type during deserialization.
/// </summary>
/// <param name="typeName">The xsi:type name to map.</param>
/// <param name="typeNamespace">The xsi:type namespace to map.</param>
/// <param name="declaredType">The type declared in the data contract.</param>
/// <param name="knownTypeResolver">The known type resolver.</param>
/// <returns>
/// The type the xsi:type name and namespace is mapped to.
/// </returns>
public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
{
if (typeNamespace == "custom"
&& typeName == typeof(IEnumerable<>).Name)
{
var argumentType = declaredType.GetGenericArguments()[0];
var collectionType = typeof(TCollection).GetGenericTypeDefinition().MakeGenericType(argumentType);
return collectionType;
}
return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
}
}
IEnumerable のシリアル化方法を変更したくありませんでしたが、IEnumerable のシリアル化を変更しないと、列挙に対して逆シリアル化メソッドが呼び出されません。そのため、すべてのカスタム内容をメッセージに入れる必要がある名前空間として「カスタム」を選択しました...これがそれほど悪い方法ではないことを願っています...また、IEnumerableのフルネームを使用する必要があったと思います名前ですが、私の文脈では帯域幅についていくつか懸念があります。
私は CustomDataContractResolver を次のように使用します。
private class CustomDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
[...]
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractSerializer(type, [...], new CustomDataContractResolver<HashSet<object>>());
}
}