ModelBindingContext.ValueProvider.GetValue(key)始终返回null

时间:2017-10-12 13:26:13

标签: json angular asp.net-mvc-5 model-binding

我正在使用AngularJS来处理一个相当复杂的父对象,其中包含需要在服务器端表现完全不同的子节点。基于this answer,看起来很稳固,我在下面创建了测试用例。我遇到的问题是每当我输入bindingContext.ValueProvider.GetValue(key)函数时,对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 } } 的任何调用都会返回null。我已经检查了调试器中的所有值。对象类型似乎已加载,但尚未绑定任何值。

我的模特:

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

})();

我的剧本:

bindingContext.ValueProvider.GetValue(key)

我尝试过的事情

我尝试过使用字符串来表示类名,如this answerthis one。但是,对NullReferenceException的调用仍会返回null,从而产生CreatureModelBinder

我还检查过以确保模型正确绑定。当我将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."

{{1}}

1 个答案:

答案 0 :(得分:0)

我希望存在的解决方案:

发布JSON数据时,似乎所有键都具有前缀的模型名称。只需将CreateModel的第一行更改为:

即可解决此问题
CreatureType creatureType = GetValue<CreatureType>(bindingContext, bindingContext.ModelName + ".Type");

上一个答案:

我的AngularJS功能发布的数据被Chrome标记为“请求有效负载”,在CreateModel中无法访问(据我所知)。当我逐行实施@ DarinDimitrov的解决方案时,数据被发布为“表单数据”,可在CreateModel中找到。我发现了一些关于数据类型here之间差异的更多信息,尽管AngularJS似乎无法在没有某些花哨的杂技的情况下使用除application / json之外的内容类型发送数据。

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

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."

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

这就是我现在所使用的,但如果有人能提供更优雅的解决方案,我愿意接受建议。

修改

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