WCF Enum By Value Surrogates to support dynamic enums

巧了我就是萌 提交于 2021-02-19 01:37:54

问题


I'm trying to make WCF Support unnamed enums. I've created a Surrogate which works fine when its an enum. However when it's a nullable enum it fails on deserialization. This is my surrogate which was modified from this article, my code differs since I don't want to supply known types:

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

When the Enum is not nullable everything deserialized fine.
When the Enum is nullable, with a value, WCF will not deserialize the int to Enum.

EDIT:

I think this may be related to how WCF handles Deserialization from Surrogates. Here's a few behaviors I've noticed that might be helpful.

  1. When calling GetDeserializedObject the object obj will be populated with an object that has already been deserialized. e.g it looks like WCF deserialization kicks in before the surrogate does

  2. When calling with an the with the underlying type the GetDeserializedObject is actually never hit, I think this is because surrogate Deserialization only works on objects

  3. WCF Cannot Serialize Enums to the value but it handles deserializing from the value just fine.

Resources:

This is the MSDN for datacontract surrogates

How can I get nullable (and non-nullable) Enums to serialize and deserialize strictly from values?


回答1:


Below line not let you handle Nullable<Enum> types :

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

You also need to check Nullable<> type explicitly. Like below :

if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
    targetType = targetType.GetGenericArguments()[0];   
}

Fiddle that demonstrates it.




回答2:


I'll post this as an update. I have this working farther than before the following serializes properly now:

  1. named values for non-nullable enums
  2. unnamed values for non-nullable enums
  3. named values for nullable enums

Note: I've omitted the trivial methods from the surrogate as they've remained the same:

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;
    }
}

NOTE: It important to mention 2 things I've observed.

  1. GetObjectToSerialize targetType is never null, WCF handles stripping the null for us.
  2. Despite WCF not be able to handle serialization of enums as values, WCF has no problem converting back to the enum non-nullable enum, Hence forth why there now is no code in the GetDeserializedObject

Disclaimer: this is observation based programming, I could not find anything to fully support my claim in WCF Documentation. (But if WCF had good documentation I wouldn't be here in the first place)

The only thing that isn't working is unnamed values for nullable enums...



来源:https://stackoverflow.com/questions/59619094/wcf-enum-by-value-surrogates-to-support-dynamic-enums

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!