模型与Asp.Core中的继承绑定

Vla*_*dka 2 asp.net .net-core asp.net-core

我需要接受来自用户的对象列表:

public async Task<IActionResult> CreateArticle(List<InformationBlockModel> informationBlocks)
    {
        ...
    }
Run Code Online (Sandbox Code Playgroud)

ModelBinder应该确定具体的类型,但是当我尝试将InformationBlock转换为TextInformationBlock时,抛出异常.

层次:

public class InformationBlockModel
{
    public virtual InformationBlockType Type { get; set; }
}

public class TextInformationBlockModel : InformationBlockModel
{
    public string Text { get; set; }

    public override InformationBlockType Type { get; set; } = InformationBlockType.Text;
}

public class ImageInformationBlockModel : InformationBlockModel
{
    public override InformationBlockType Type { get; set; } = InformationBlockType.Image;
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Vla*_*dka 7

最后,我找到了一个解决方案:

Startup.cs

services.AddMvc()
    .AddJsonOptions(options => options.SerializerSettings.Converters.Add(new InformationBlockConverter()));
Run Code Online (Sandbox Code Playgroud)

JsonCreationConverter.cs

public abstract class JsonCreationConverter<T> : JsonConverter
{
    public override bool CanWrite { get; } = false;

    public override bool CanRead { get; } = true;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T) == objectType;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);

        var target = Create(objectType, jObject);

        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}
Run Code Online (Sandbox Code Playgroud)

InformationBlockConverter

public class InformationBlockConverter : JsonCreationConverter<InformationBlockModel>
{
    private readonly Dictionary<InformationBlockType, Type> _types = new Dictionary<InformationBlockType, Type>
    {
        {InformationBlockType.Text, typeof(TextInformationBlockModel)},
        {InformationBlockType.Image, typeof(ImageInformationBlockModel)},
        {InformationBlockType.Video, typeof(VideoInformationBlockModel)}
    };

    protected override InformationBlockModel Create(Type objectType, JObject jObject)
    {
        return (InformationBlockModel) jObject.ToObject(_types[Enum.Parse<InformationBlockType>(
            jObject.GetValue("type", StringComparison.InvariantCultureIgnoreCase).Value<string>(), true)]);
    }
}
Run Code Online (Sandbox Code Playgroud)

InformationBlockType

public enum InformationBlockType
{
    Text,
    Image,
    Video
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢分享这个。我确实遇到了列表中显示重复项的问题,为了防止这种情况,`Create` 覆盖应该返回一个新的、任何类型的空实例要返回,而不是将 `jObject` 转换为所需的类型。 (3认同)