WCF值枚举替代方案,支持动态枚举。

5
我正在尝试使WCF支持未命名枚举。 我创建了一个代理,当它是枚举类型时可以正常工作。 但是,当它是可空枚举类型时,在反序列化时会失败。 这是我的代理,它是从article修改而来的,我的代码不同之处在于我不想提供已知类型:
public class EnumValueDataContractSurrogate : IDataContractSurrogate
{
    #region Interface Implementation

    public Type GetDataContractType(Type type)
    {
        return type;
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        if (null == obj)
        {
            return obj;
        }

        if (targetType.IsEnum)
        {
            return EnumExtensions.ChangeToUnderlyingType(targetType, obj);
        }

        if (targetType.IsNullable() && targetType.GetUnderlyingType().IsEnum)
        {
            return (int?)obj;
        }

        return obj;
    }

    // This Method is never invoked for targetType enum/enum?
    // However all the other parameters work fine
    public object GetDeserializedObject(object obj, Type targetType)
    {

        if (targetType.IsNullable())
        {
            targetType = targetType.GetUnderlyingType();
        }

        if ((false == targetType.IsEnum) || (null == obj))
        {
            return obj;
        }

        var stringObj = obj as string;
        if (null != stringObj)
        {
            return Enum.Parse(targetType, stringObj);
        }
        return Enum.ToObject(targetType, obj);
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
        //not used
        return;
    }

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        //Not used
        return null;
    }

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
    {
        //not used
        return null;
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        //not used
        return null;
    }

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
    {
        //not used
        return typeDeclaration;
    }

    #endregion
}

public static object ChangeToUnderlyingType(Type enumType, object value)
{
    return Convert.ChangeType(value, Enum.GetUnderlyingType(enumType));
}

当枚举类型不可为空时,一切反序列化正常。
当枚举类型可为空并且有值时,WCF将无法将int反序列化为枚举类型。
编辑:
我认为这可能与WCF如何从代理中处理反序列化有关。以下是我注意到的一些行为,可能会有所帮助。
  1. 调用GetDeserializedObject时,object obj将被填充为已经反序列化的对象。例如,看起来WCF反序列化在代理之前启动。
  2. 使用基础类型调用时,实际上永远不会触发GetDeserializedObject,我认为这是因为代理反序列化仅适用于对象。
  3. WCF无法将枚举类型序列化为值,但它可以很好地从值反序列化。
资源:
这是DataContract Surrogates的MSDN

如何使可空(和非可空)枚举类型严格地从值进行序列化和反序列化?


据文档显示,在反序列化过程完成后,会调用GetDeserializedObject方法。如果此过程失败,该方法将不会被调用。如果要在反序列化之前拦截它,则唯一的方法是修改GetDataContractType方法。我不确定,但如果枚举合同传递到该方法中,您可能可以拦截它们的反序列化方式。 - Eldar
@Eldar 是的,我一直在尝试玩反序列化,我认为这是如何做的,但我还没有能够让它工作。如果您知道任何方法可以将枚举值一致地序列化为它们的值,我会接受它作为答案。 - johnny 5
2个回答

2

以下代码行不允许您处理 Nullable<Enum> 类型:

  if ((false == targetType.IsEnum) || (null == obj))
  {
      return obj;
  }

您还需要显式地检查Nullable<>类型。如下所示:
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
    targetType = targetType.GetGenericArguments()[0];   
}

这里有一个示例代码,演示了它的用法。


抱歉造成困惑,我在发布问题时忘记修复了。我的问题是反序列化方法从未针对该参数触发。WCF从未调用该方法。 - johnny 5
WCF内部具有忽略某些参数进行代理反序列化的逻辑,但它并没有完全记录,因此我无法弄清楚为什么。 - johnny 5
虽然这不是我问题的答案,但你是唯一一个尝试解决它的人,所以请享受 :) - johnny 5
@johnny5,你可以分享一些数据契约和样例,展示你如何进行序列化/反序列化。以及你的客户端是通过dll、wsdl还是mex终结点生成客户端代理的呢? - Eldar

0

我会把这个作为更新发出来。我已经让它比之前工作得更好,现在以下内容可以正确序列化:

  1. 非空枚举的命名值
  2. 非空枚举的未命名值
  3. 可为空枚举的命名值

注意:我从代理中省略了琐碎的方法,因为它们保持不变:

public class EnumValueDataContractSurrogate : IDataContractSurrogate
{
    public object GetObjectToSerialize(object obj, Type targetType)
    {
        if (targetType.IsEnum && !Enum.IsDefined(targetType, obj))
        {
            return EnumExtensions.ChangeToUnderlyingType(targetType, obj);
        }
        return obj;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        return obj;
    }
}

注意: 我观察到两件事情需要提及。

  1. GetObjectToSerialize targetType 永远不会为空,WCF 会帮我们去掉 null。
  2. 尽管 WCF 无法处理枚举作为值的序列化,但 WCF 没有问题将其转换回非空枚举,因此现在在 GetDeserializedObject 中没有代码了。

免责声明:这是基于观察的编程,我找不到任何完全支持我的 WCF 文档。 (但如果 WCF 有好的文档,我就不会来这里了)

唯一不起作用的是可空枚举的未命名值...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接