简体   繁体   English

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

[英]Model binding with inheritance in Asp.Core

I need to accept list of objects from user: 我需要接受来自用户的对象列表:

public async Task<IActionResult> CreateArticle(List<InformationBlockModel> informationBlocks)
    {
        ...
    }

ModelBinder should determine concrete types, but when I trying to cast InformationBlock to TextInformationBlock, exception throws. ModelBinder应该确定具体的类型,但是当我尝试将InformationBlock转换为TextInformationBlock时,异常抛出。

Hierarchy: 层次:

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

Finally, I found a solution: 最后,我找到了一个解决方案:

Startup.cs Startup.cs

services.AddMvc()
    .AddJsonOptions(options => options.SerializerSettings.Converters.Add(new InformationBlockConverter()));

JsonCreationConverter.cs 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;
    }
}

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

InformationBlockType InformationBlockType

public enum InformationBlockType
{
    Text,
    Image,
    Video
}

Asp.Net binding does not work like this by default. 默认情况下,Asp.Net绑定不能像这样工作。 If you want to this sort of behaviour you will have to write your own custom model binding , which isn't too difficult. 如果你想要这种行为,你将不得不编写自己的自定义模型绑定 ,这不是太困难。

Or, use a view model: 或者,使用视图模型:

public class InformationBlockViewModel
{
    public string Type { get; set; }
    public string Text { get; set; }
    public string Name { get; set; }
}

Then handle the block type in the controller: 然后处理控制器中的块类型:

public async Task<IActionResult> CreateArticle(List<InformationBlockViewModel> informationBlocks)
{
    foreach (var block in informationBlocks) {        
        switch (block.Type)
        {
            case "Text":
                // Handle text
                break;
            case "Image":
                // Handle image
                break;
            case default:
                throw new Exception("Unknown information block type.");
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM