2013-12-19 46 views
9

我遇到了以下問題:控制器參數不成爲空

我有一個asp.net的MVC 5控制器引用類型作爲參數:

[Route("")] 
    [HttpGet] 
    public ActionResult GetFeeds(Location location) 
    { 
     if (location == null) 
     { 
      // init location or smth 
     } 

     // Do smth with location 

     return new EmptyResult(); 
    } 

你會發現,我正在使用AttributeRouting。此操作的名稱沒有其他方法。

但是 - 這是我的位置類:

public class Location : ILocation 
    { 
     public DbGeography Coordinates { get; set; } 

     public double Latitude { get; set; } 

     public double Longitude { get; set; } 
    } 

這裏沒有什麼特別(接口定義了所有這些屬性)。

如果我(實際使用PowerShell),並通過類似於訪問控制器動作:

http://localhost:2000/Feed?latitude=23.1&longitude=37 

寄託都工作正常,但如果我使用

http://localhost:2000/Feed 

位置參數不null(這是一個新的位置,默認值),這是我想要的行爲:(。

有沒有人有一個想法,爲什麼發生這種情況?

在此先感謝

+2

活頁夾按設計工作 - 它實例化一個位置對象,並使用默認值填充其值,除了提供覆蓋值的位置(您不在第二個示例中)。它不會僅僅因爲你的路由沒有提供值而創建你的'Location'對象的null實例。 – 48klocs

+0

@ChristopherKellner - 由於「模型聯編程序的工作原理」而不可能出現。看看我的答案中的鏈接以獲得更多解釋。 – Tommy

回答

7

MVC模型聯編程序已接管。我最初發布的內容適用於不通過模型聯編程序的情況。然而,based on other answers on SO和我自己的快速測試,似乎viewmodel參數永遠不會爲空,因爲活頁夾的工作方式和綁定屬性來形成值。

在你的實例中,我將檢查經緯度是否爲空,以查看是否沒有任何內容被傳遞。這意味着你需要讓他們可空你的視圖模型

public class Location : ILocation 
    { 
     public DbGeography Coordinates { get; set; } 

     public double? Latitude { get; set; } 

     public double? Longitude { get; set; } 
    } 

更新控制器代碼

if (location.Latitude == null && location.Longitude == null) 
    { 
     // init location or smth 
    } 
+0

我嘗試過,但位置仍然是位置的新實例。還有什麼建議? – CKE

+0

@ChristopherKellner - 是的,你說得對,爲我更新了我的答案 – Tommy

+0

location.Latitude.HasValue看起來更好:D –

4

ModelBinder的創建對象的新實例,所以你必須在這裏兩種選擇:

有無'[Required] DataAnnotation on'required properties'並將它們標記爲空,然後檢查ModelState.IsValid(推薦)

mak Ë緯度和經度可空double?,你可以檢查Latitude.HasValue && Longitude.HasValue

UPDATE:

public class Location : ILocation 
    { 
     public DbGeography Coordinates { get; set; } 

     public double? Latitude { get; set; } 

     public double? Longitude { get; set; } 
    } 

public class LocationGetFeedsViewModel : LocationGetFeedsBinderModel { 
     // change coordinates to string because maybe that's easier to handle on the view. 
     public string Coordinates { get; set; } 
     // added to sum to the example 
     public IEnumerable<SelectListItem> Zones { get; set; } 
} 

public class LocationGetFeedsBinderModel { 
     [Required] 
     public double? Latitude { get; set; } 
     [Required] 
     public double? Longitude { get; set; } 
} 

控制器:

public ActionResult GetFeeds(LocationGetFeedsBinderModel location) { 
    if (!ModelState.IsValid) 
     // redirect or display some error 
    return new EmptyResult(); 
} 
+0

可悲的是我無法將這些屬性更改爲可空值,因爲它們是由ILocation繼承的。我看到3個可能性來實現這個目標:創建一個具有可空屬性的新VM類並將它們解析爲一個新的Location,或者檢查它們是否爲0,因爲這是非常罕見的。兩者都感覺錯誤:/第三個參數看起來會更好,因此可能爲空。或者玉有另一個想法? – CKE

+1

這就是我所說的「BinderModel」而不是ViewModel。這是非常有用的,因爲viewModel通常獲得比只接受2或3的表單發佈更多的屬性。這兩個世界都是最好的。其實我認爲讓一個控制器接受一個完整的ViewModel是很少見的,甚至是不好的。 –

+0

所以你會推薦使用「BinderModel」的第一次接觸? – CKE

1

默認的model binder總會實例化一個複雜的對象,因此不幸的是永遠不會爲空,任何可選的賦值都會被忽略。

這種行爲的最終結果,因爲它適用於您的系統將是你必須檢測使用默認的構造函數。沒有辦法通過反思或隱含檢查來做到這一點。

它必須明確地完成。

它可以通過在默認構造函數中設置類中的標誌,使用接受答案中建議的可空屬性,通過使用Bart建議的數據註釋,通過使用屬性的自定義get和set方法,或各種其他方式。

+0

這是我的原始答案。當您進入ActionResult時,該變量仍然是非空的。在VS2013上測試自己。猜測在MVC中的某個時刻對DefaultModelBinder進行了更新。 – Tommy

+0

@Tommy - 我刪除了使用可選屬性設置爲null的建議。正如你準確地聲明它將被實例化,並且可選值永遠不會被分配。請參閱修改。 –

0

我一直在測試發佈數據與angularjs。

我發現,如果你強制值,而不是不確定,ModelBinder的不實例化一個複雜的對象。

if (!location) 
    location = null; 

$http({ method: "POST", url: "/Location/Create", data: { location } })