【问题标题】:Model binding and inheritance ASP MVC 5模型绑定和继承 ASP MVC 5
【发布时间】:2016-12-20 11:11:15
【问题描述】:

基类:AbstractBuildBlock,派生:TextBlock,ImageBlock,EmptyBlock,...。

此处的块:站点 -> 页面[someIndex] -> 行[someIndex] -> 构建块

Fields BuildBlocks 属于 AbstractBuildBlock 类型,因此当我在 BuildBlocks 中将站点保存到 DB 时,每条记录都有标识符 AbstractBuildBlock。我尝试在 BuildBlockRepository 中执行下一个操作:

    switch(obj.ContentType)
            {
                case "text":
                    obj = obj as TextBlock;
                    context.BuildBlocks.Add(obj);
                    break;
            }

obj = obj as TextBlock obj 之后是null。原因是 obj 是 AbstractBuildBlock 类型。我在 msdn 发现这段代码应该可以工作:

BaseClass a = new DerivedClass()
DerivedClass b = a as DerivedClass

所以我需要在模型绑定时重现此代码。这是 ajax 请求:

$('.saveSite').click(function () {
    $.ajax({
        url: '/Site/Update',
        data: { site: getSite() },
        method: 'POST',
        success: function (data) {
            console.log('Success save');
        },
        error: function (data) {
            debugBox(data);
        }
    });
});

这个请求的 mvc 动作

    public string Update(Site site)
    {
        siteRepository.Add(site);
        return "Success";
    }

所以我以 json 形式发送站点,该站点中的 BuildBlocks 也以 json 形式发送,但是它们的(块)类型当然不是 AbstractBuildBlock,它们都是 TextBlock、ImageBlock 等,并且具有带值的字段.

问题:站点具有类型为 AbstractBuildBlock 的字段 BuildBlocks,模型绑定器执行以下操作:

buildBlock = new AbstractBuildBlock(); //loose derived classes fields and posibility to convert it in DerivedClass
buildBlocks.push(buildBlock)

但我需要这样的东西

switch(buildBlock.contenType) {
    case "text" : buildBlock = new TextBlock();buidlBlocks.push(buildBlock);
}

【问题讨论】:

标签: c# asp.net json asp.net-mvc inheritance


【解决方案1】:

JSON NET Custom deserializer not work at all

ASP MVC 5 How to read object from request with the help of JSON NET

查看上面两个链接中的答案,描述了正确的ajax调用

以及下面的服务器代码

mvc 动作

public string Update(Site site)
    {
        TextBlock block = site.Pages[0].Rows[0].BuildBlocks[0] as TextBlock;
        siteRepository.Add(site);
        return "Success";
    }

AbstractJsonCreationConverter 我已经在 Infrastructure 文件夹中创建了它

public abstract class AbstractJsonCreationConverter<T> : JsonConverter
{
    protected abstract T Create(Type objectType, JObject jsonObject);

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

    public override object ReadJson(JsonReader reader, Type objectType,
      object existingValue, JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var target = Create(objectType, jsonObject);
        serializer.Populate(jsonObject.CreateReader(), target);
        return target;
    }

    public override void WriteJson(JsonWriter writer, object value,
   JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

和在同一个文件夹中的具体类

public class JsonBuildBlockConverter : AbstractJsonCreationConverter<AbstractBuildBlock>
{
    protected override AbstractBuildBlock Create(Type objectType, JObject jsonObject)
    {
        var type = jsonObject["contentType"].ToString();
        switch(type)
        {
            case "text":
                return new TextBlock();
            default:
                return null;
        }
    }
}

还有一个基础设施类

internal class SiteModelBinder : System.Web.Mvc.IModelBinder
{
    public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
    {
        // use Json.NET to deserialize the incoming Position
        controllerContext.HttpContext.Request.InputStream.Position = 0; // see: https://stackoverflow.com/a/3468653/331281
        Stream stream = controllerContext.RequestContext.HttpContext.Request.InputStream;
        var readStream = new StreamReader(stream, Encoding.UTF8);
        string json = readStream.ReadToEnd();
        return JsonConvert.DeserializeObject<Site>(json, new JsonBuildBlockConverter());
    }
}

最后一个类是 ModelBinder,它将被调用以解析 Site 类型的变量,要使其工作,您需要在 ApplicationStart() 中的 Global.asax.cs 中注册它

 protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        ModelBinders.Binders.Add(typeof(Site), new SiteModelBinder()); //RegisterModelBinder for Site
    }

就这些了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-21
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多