ModelBindingContext.ValueProvider.GetValue(key)始终返回null [英] ModelBindingContext.ValueProvider.GetValue(key) always returns null

查看:98
本文介绍了ModelBindingContext.ValueProvider.GetValue(key)始终返回null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用AngularJS来操纵一个相当复杂的父对象,该对象带有需要在服务器端行为完全不同的子对象.基于此答案(看起来很可靠),我在下面创建了测试用例.我遇到的问题是,每当我输入CreateModel函数时,对bindingContext.ValueProvider.GetValue(key)的任何调用都将返回null.我已经检查了调试器中的所有值.该对象类型似乎已加载,但尚未绑定任何值.

I'm using AngularJS to manipulate a fairly complex parent object with children that need to behave quite differently server-side. Based on this answer, which appears pretty solid, I've created the test case below. The issue I'm running into is that whenever I enter the CreateModel function, any call to bindingContext.ValueProvider.GetValue(key) returns null. I've checked all values in the debugger. The object type appears to be loaded, but no values have yet been bound.

我的模特:

public class Menagerie
{
    public Menagerie()
    {
        Critters = new List<Creature>();
    }

    public string MakeNoise()
    {
        return String.Join(" ", Critters.Select(c => c.MakeNoise()));
    }

    public List<Creature> Critters { get; set; }
}

public class Tiger : Creature
{
    public Tiger() { }
    public override CreatureType Type => CreatureType.Tiger;
    public override string Sound => "ROAR";
}

public class Kitty : Creature
{
    public Kitty() { }
    public override CreatureType Type => CreatureType.Kitty;
    public override string Sound => "meow";
}

public class Creature
{
    public Creature() { }
    public string Name { get; set; }
    public virtual CreatureType Type { get; set; }
    public virtual string Sound { get; }
    public string MakeNoise()
    {
        return $"{Name} says {Sound ?? "nothing"}.";
    }

    public static Type SelectFor(CreatureType type)
    {
        switch (type)
        {
            case CreatureType.Tiger:
                return typeof(Tiger);
            case CreatureType.Kitty:
                return typeof(Kitty);
            default:
                throw new Exception();
        }
    }
}

public enum CreatureType
{
    Tiger,
    Kitty,
}

public class CreatureModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        CreatureType creatureType = GetValue<CreatureType>(bindingContext, "Type");
        Type model = Creature.SelectFor(creatureType);
        Creature instance = (Creature)base.CreateModel(controllerContext, bindingContext, model);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, model);
        return instance;
    }

    private T GetValue<T>(ModelBindingContext bindingContext, string key)
    {
        ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(key); // valueResult is null
        bindingContext.ModelState.SetModelValue(key, valueResult);
        return (T)valueResult.ConvertTo(typeof(T)); // NullReferenceException
    }
}

我的脚本:

(function () {
    'use strict';

    angular.module('app', []).controller('CritterController', ['$scope', '$http', function ($scope, $http) {

        $http.post('/a/admin/get-menagerie', { }).success(function (data) {
            $scope.menagerie = data.menagerie;
        });

        $scope.makeNoise = function () {
            $http.post('/a/admin/make-noise', { menagerie: $scope.menagerie }).success(function (data) {
                $scope.message = data.message;
            });
        }
    }]);

})();

我尝试过的事情

我尝试仅使用字符串来表示类名,如此答案

I've tried just using a string to indicate the class name, as in this answer and this one. However, the call to bindingContext.ValueProvider.GetValue(key) still returns null, resulting in a NullReferenceException.

我还检查以确保模型正确绑定.当我将CreatureModelBinder更改为以下内容时,一切都可以正常映射.但是每个生物都会失去其继承的类型,并成为Creature.

I also checked to ensure the model is binding properly. When I change my CreatureModelBinder to the following, everything maps fine. But each creature loses its inherited type and becomes a Creature.

public class CreatureModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
} // MakeNoise returns: "Shere Khan says nothing. Mr. Boots says nothing. Dr. Evil says nothing."

推荐答案

我希望存在的解决方案:

发布JSON数据时,似乎所有密钥都带有模型名称.只需将CreateModel的第一行更改为:

When posting JSON data, it appears that all keys have the model name prepended. This can be solved simply by changing the first line of CreateModel to:

CreatureType creatureType = GetValue<CreatureType>(bindingContext, bindingContext.ModelName + ".Type");

上一个答案:

Chrome浏览器将我的AngularJS函数发布的数据标记为请求有效负载",据我所知,无法在CreateModel中访问该数据.当我逐行实现@DarinDimitrov的解决方案时,数据被发布为表单数据",可在CreateModel中使用.我发现了有关数据类型之间差异的更多信息

The data my AngularJS function posts is labeled by Chrome as "Request Payload", which cannot (as far as I can tell) be accessed in CreateModel. When I implemented @DarinDimitrov's solutions line-for-line, the data was posted as "Form Data", which was available in CreateModel. I found a little more info about the difference between data types here, though AngularJS doesn't appear to be able to send data with a content-type other than application/json without some fancy acrobatics.

但是,我确实发现可以在BindModel函数中访问对象的实例(并创建具有不同类型的新实例),如下所示:

I did, however, find that I can access an instance of my object (and create a new instance with a different type) in the BindModel function as follows:

public class CreatureModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        Creature instance = (Creature)base.BindModel(controllerContext, bindingContext);
        Creature newInstance = (Creature)Activator.CreateInstance(Creature.SelectFor((instance).Type));
        newInstance.Name = instance.Name;

        return newInstance;
    }
} // MakeNoise() returns: "Shere Khan says ROAR. Mr. Boots says meow. Dr. Evil says meow."

我能看到的最大缺点是,对象中的每个属性都必须手动映射到新实例,这会变得很麻烦并且会产生难以跟踪的错误.

The greatest downside I can see is that every property in the object must be manually mapped to the new instance, which can become cumbersome and produce hard-to-track bugs.

这是我目前正在使用的方法,但是如果有人可以提供更优雅的解决方案,我欢迎提出建议.

This is what I'm using for now, though I'm open to suggestions if anyone can provide a more elegant solution.

编辑

我发现当发布为application/json时,可以使用以下方式访问模型数据:

I found that when posting as application/json, the model data can be accessed using this:

这篇关于ModelBindingContext.ValueProvider.GetValue(key)始终返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆