2017-10-12 80 views
0

我正在使用AngularJS來處理一個相當複雜的父對象,這些對象需要與服務器端的行爲完全不同。基於this answer,這看起來非常穩固,我在下面創建了測試用例。我遇到的問題是,只要我輸入CreateModel函數,任何對bindingContext.ValueProvider.GetValue(key)的調用都將返回null。我已經在調試器中檢查了所有的值。對象類型似乎已加載,但尚未綁定任何值。ModelBindingContext.ValueProvider.GetValue(key)總是返回null

我的模型:

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

})(); 

事情我已經我已經試過只使用一個字符串來表示類名試圖

,如在this answerthis one。但是,bindingContext.ValueProvider.GetValue(key)的調用仍然返回空值,導致NullReferenceException

我也檢查過,以確保模型正確綁定。當我將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." 

回答

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

最大的缺點我能看到對象中的每個屬性都必須手動映射到新實例,這會變得很麻煩並且會產生難以跟蹤的錯誤。

這就是我現在使用的東西,儘管如果任何人都可以提供更優雅的解決方案,我會接受這些建議。

編輯

我發現,隨着應用程序/ JSON發佈時,模型數據可以訪問使用此: