簡體   English   中英

復雜類型枚舉模型綁定

[英]Complex Type Enum Model Binding

背景

在.NET Core中,如果模型的層次結構中的任何地方都包含enum ,並且提供的值與該enum的名稱不完全匹配,則默認控制器模型綁定將失敗(為您的action參數生成null值)。 空格或奇數大寫會破壞綁定,這對我的API端點的使用者而言似乎不友好。

我的解決方案

我創建了一個模型綁定器提供程序,該提供程序使用反射來確定目標綁定類型中是否存在enum 如果此檢查為true,則返回一個自定義模型綁定程序(通過傳遞enum類型構造),該綁定程序使用正則表達式/字符串操作(粗略)掃描請求正文以獲取enum值,並努力將其解析為名稱。該enum類型,然后再傳遞JsonConvert進行反序列化。

我認為,這種解決方案對於我要實現的目標而言過於復雜和丑陋。

我想要的是類似JsonConvert屬性(用於我的enum字段)的東西,該屬性會在綁定/反序列化期間進行此工作。 Newtonsoft開箱即用的解決方案( StringEnumConverter )不會嘗試調整字符串以適合enum類型(我想是公平的),但是我不能在這里擴展Newtonsoft的功能,因為它依賴於很多內部類(無需復制)並粘貼大量的代碼)。

我想念的地方是否有管道可以更好地滿足這一需求?

PS我把它放在這里而不是代碼審查(太理論化)或軟件工程(太具體); 請告知該地點是否不合適。

我為此使用了類型安全的枚舉模式,我認為它將對您有用。 使用TypeSafeEnum,您可以使用Newtonsoft的JsonConverter屬性控制映射到JSON的內容。 由於您沒有要發布的代碼,因此我建立了一個示例。

應用程序的TypeSafeEnums使用的基類:

public abstract class TypeSafeEnumBase
{
    protected readonly string Name;
    protected readonly int Value;

    protected TypeSafeEnumBase(int value, string name)
    {
        this.Name = name;
        this.Value = value;
    }

    public override string ToString()
    {
        return Name;
    }
}

實現為TypeSafeEnum的示例類型,通常應該是普通的Enum,包括Parse和TryParse方法:

public sealed class BirdType : TypeSafeEnumBase
{
    private const int BlueBirdId = 1;
    private const int RedBirdId = 2;
    private const int GreenBirdId = 3;
    public static readonly BirdType BlueBird = 
        new BirdType(BlueBirdId, nameof(BlueBird), "Blue Bird");
    public static readonly BirdType RedBird = 
        new BirdType(RedBirdId, nameof(RedBird), "Red Bird");
    public static readonly BirdType GreenBird = 
        new BirdType(GreenBirdId, nameof(GreenBird), "Green Bird");

    private BirdType(int value, string name, string displayName) :
        base(value, name)
    {
        DisplayName = displayName;
    }

    public string DisplayName { get; }

    public static BirdType Parse(int value)
    {
        switch (value)
        {
            case BlueBirdId:
                return BlueBird;
            case RedBirdId:
                return RedBird;
            case GreenBirdId:
                return GreenBird;
            default:
                throw new ArgumentOutOfRangeException(nameof(value), $"Unable to parse for value, '{value}'. Not found.");
        }
    }

    public static BirdType Parse(string value)
    {
        switch (value)
        {
            case "Blue Bird":
            case nameof(BlueBird):
                return BlueBird;
            case "Red Bird":
            case nameof(RedBird):
                return RedBird;
            case "Green Bird":
            case nameof(GreenBird):
                return GreenBird;
            default:
                throw new ArgumentOutOfRangeException(nameof(value), $"Unable to parse for value, '{value}'. Not found.");
        }
    }

    public static bool TryParse(int value, out BirdType type)
    {
        try
        {
            type = Parse(value);
            return true;
        }
        catch
        {
            type = null;
            return false;
        }
    }

    public static bool TryParse(string value, out BirdType type)
    {
        try
        {
            type = Parse(value);
            return true;
        }
        catch
        {
            type = null;
            return false;
        }
    }
}

用於處理類型安全轉換的容器,因此您無需為實現的每個類型安全創建一個轉換器,並在實現新類型安全枚舉時防止在TypeSafeEnumJsonConverter中進行更改:

public class TypeSafeEnumConverter
{
    public static object ConvertToTypeSafeEnum(string typeName, string value)
    {
        switch (typeName)
        {
            case "BirdType":
                return BirdType.Parse(value);
            //case "SomeOtherType": // other type safe enums
            //    return // some other type safe parse call
            default:
                return null;
        }
    }
}

實現Newtonsoft的JsonConverter,它依次調用我們的TypeSafeEnumConverter

public class TypeSafeEnumJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        var types = new[] { typeof(TypeSafeEnumBase) };
        return types.Any(t => t == objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        string name = objectType.Name;
        string value = serializer.Deserialize(reader).ToString();
        return TypeSafeEnumConversion.ConvertToTypeSafeEnum(name, value); // call to our type safe converter
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null && serializer.NullValueHandling == NullValueHandling.Ignore)
        {
            return;
        }
        writer.WriteValue(value?.ToString());
    }
}

使用我們的BirdType並將轉換器設置為使用的示例對象:

public class BirdCoup
{
    [JsonProperty("bird-a")]
    [JsonConverter(typeof(TypeSafeEnumJsonConverter))] // sets the converter used for this type
    public BirdType BirdA { get; set; }

    [JsonProperty("bird-b")]
    [JsonConverter(typeof(TypeSafeEnumJsonConverter))] // sets the converter for this type
    public BirdType BirdB { get; set; }
}

用法示例:

// sample #1, converts value with spaces to BirdTyp
string sampleJson_1 = "{\"bird-a\":\"Red Bird\",\"bird-b\":\"Blue Bird\"}";
BirdCoup resultSample_1 = 
JsonConvert.DeserializeObject<BirdCoup>(sampleJson_1, new JsonConverter[]{new TypeSafeEnumJsonConverter()});

// sample #2, converts value with no spaces in name to BirdType
string sampleJson_2 = "{\"bird-a\":\"RedBird\",\"bird-b\":\"BlueBird\"}";
BirdCoup resultSample_2 = 
JsonConvert.DeserializeObject<BirdCoup>(sampleJson_2, new JsonConverter[] { new TypeSafeEnumJsonConverter() });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM