首页 > 解决方案 > 在 WebAPI 中实现一个 TypeConverter 来对 Enum 进行描述

问题描述

DescriptionAttribute在 Enums 上使用来提供包含空格的描述。我创建了一些扩展方法,它们将返回枚举的字符串描述和/或从字符串描述返回枚举值。

现在我希望我的 WebAPI 使用这些扩展方法来处理枚举类型转换而不是默认的 TypeConverter,以便我可以传递一个类似的值"Car Wash"并将其映射到一个枚举。

有没有办法将默认字符串覆盖为 Enum TypeConverter?

环境 .NetCore 2.x

更新 - 我当前的代码

当控制器将对象序列化为 JSON 以发送到客户端时,我当前的代码效果很好。鉴于以下Enum情况,枚举值为 0 将导致客户端获得字符串“心理健康”——完美。但是,现在当客户端将“心理健康”发送回服务器时——我需要将其转换回AgencyTpes.MentalHealth. 现在,绑定引擎抛出一个错误。

//Example Enum
public enum AgencyTypes {
   [Description("Mental Health")]
   MentalHealth,
   Corrections,
   [Description("Drug & Alcohol")]
   DrugAndAlcohol,
   Probation
}

我使用的枚举扩展DescriptionAttribute

public static class EnumExtensions
{
    public static string ToDisplayString(this Enum values)
    {
        var attribute = value.GetType().GetMember(value.ToString())
           .FirstOrDefault()?.GetCustomAttribute<DescriptionAttribute>();
        return attribute ?.Description ?? value.ToString();
     }

     public static object GetValueFromDescription(string description, Type enumType)
     {
         return Convert.ChangeType(LookupDescription(description,enumType),enumType);
     }

     public static T GetValueFromDescription<T>(string description) where T: struct
     {
        return (T)LookupDescription(description, typeof(T));
     }

     private static object LookupDescription(string description, Type enumType)
     {
        if(!enumType.IsEnum)
           throw new ArgumentException("Type provided must be an Enum", enumType.Name);

        foreach(var field in enumType.GetFields())
        {
           var attribute = Attribute.GetCustomAttribute(field, tyepof(DescriptionAttribute)) as DescriptionAttribute;
           if((attribute != null && attribute.Description == description)
               || field.Name == description)
           {
              return field.GetValue(null);
           }
         }
         throw new ArgumentException($"Requested value for '{description}' in enum {enumType.Name} was not found", nameof(description));
     }
}

我的 JSON 覆盖以使控制器能够将枚举转换为字符串

//Startup.cs
services.AddMvc().SetCompatibilityVersion(Compatibility.Version_2_2)
  .AddJsonOptions(options => 
  {
     options.SerializerSettings.Converters.Add(new StringAnnotationEnumConverter());
  });


public class StringAnnotationEnumConverter : StringEnumConverter
{
   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
   {
      var token = JToken.Load(reader);
      var value = token.ToString();
      if(reader.TokenType == JsonToken.String)
          return EnumExtensions.GetValueFromDescription(value, objectType);
      else
          return base.ReadJson(reader, objectType, existingValue, serializer);
   }

   public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
   {
       if(value == null)
       {
          writer.WriteNull();
          return;
       }

       Enum e = (Enum)value;
       string enumName = e.ToDisplayString();
       if(string.IsNullOrWhiteSpace(enumName))
          throw new JsonSerializationException(String.Format("Integer value {0} is not allowed.",e.ToString("D")));

       writer.WriteValue(enumName);
    }
}

更新 2 - WebAPI

这是控制器和域对象的示例代码

public class Agency 
{
   public int Id {get; set;}
   public string Name {get; set;}
   public AgencyTypes AgencyType {get; set;}
   ...
 }


 [ApiController]
 public class AgencyController : ControllerBase
 {
    [HttpPost]
    public async Task<IActionResult> Agency([FromForm] Agency agency)
    {
       ...
    }
  }

标签: c#asp.net-core-webapi

解决方案


您可以尝试覆盖默认值EnumConverter来进行自定义属性检查

public class MyEnumConverter: EnumConveter {
    public MyEnumConverter(Type type) : base(type) {
    }


    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
        if (value is string) {
            var enumString = (string)value;
            return EnumExtensions.GetValueFromDescription(enumString, EnumType);
        }
        return base.ConvertFrom(context, culture, value);
    }
}

TypeConverter并用属性装饰你的枚举

[TypeConverter(typeof(MyEnumConverter))]
public enum AgencyTypes {
    [System.ComponentModel.Description("Mental Health")]
    MentalHealth,
    Corrections,
    [System.ComponentModel.Description("Drug & Alcohol")]
    DrugAndAlcohol,
    Probation
}

推荐阅读