0

我有一个应用程序使用 WCF 与客户端和服务之间的共享服务合同进行通信。对于这种服务合同:

[ServiceContract]
public interface IMyContract
{
    [OperationContract]
    IEnumerable<string> MyOperation();
}

IEnumerable 的默认实现是 Array,我想将实现自定义为另一个:例如 List、Hashset...

我的客户端代理不是自动生成的(因此没有 svcmap 文件),并且我不想将 IEnumerable 更改为合同中的另一种类型(据我了解 WCF 反序列化,在这种情况下 knowntypes 不起作用,CollectionDataContractAttribute .. 。 可以?)。

由于我创建了一个 RealProxy 包装通道生成(用于回收管理),我现在可以拦截其中的结果以动态更改集合类型......

但是可能有一种方法可以以某种方式影响反序列化,以便在客户端选择 IEnumerable 的实现,而无需从头开始创建我自己的序列化程序或类似的东西。

如果我的问题不是很清楚,请不要犹豫,我的问题是否是由于某个地方的方法不好。

提前致谢!

更新: Jury Soldatenkov 指出了一个很好的解决方案!

这是我的 POC 允许选择 IEnumerable 实现:

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>>());
    }
}
4

0 回答 0