[英]ASP.NET MVC UpdateModel() parameter conversion woes
一個警告:我對MVC及其范例以及其某些內部功能還比較陌生,但是我對此很滿意。 這是我的第二個基礎MVC應用程序,我對如何解決其中一個測試人員遇到的“問題”感到有些困惑。
用戶得到的是一個編輯屏幕,其中給出了來自國庫券的每日LIBOR利率的有效日期(百分比)。 速率始終在0到100之間,因此,我嘗試使用域對象之一的元數據中的RangeAttribute
來限制該范圍。 我已指定范圍如下:
[Required, DisplayName("Published Rate"), Range(typeof(decimal), "0", "100")]
public object PublishedRate { get; set; }
請注意,我要傳遞字符串值,因為RangeAttribute
沒有采用小數的重載構造函數。 在用戶進入並輸入與眾不同的內容之前,這似乎非常有效,例如:
“ 0.000000000000000000000000000000000000001”
這導致UpdateModel()
失敗; ModelState
顯示此錯誤(奇怪的是,對於相同的ModelState值,這是三次):
從“ System.String”類型到“ System.Decimal”類型的參數轉換失敗。
深入研究錯誤可以找出原因。 下面的第一行是該字段的驗證報告的內容。 我很好奇這沒有引起模型驗證錯誤(即未出現在模型的摘要驗證列表中):
“ 0.000000000000000000000000000000000000001不是十進制的有效值。” “對於小數,值太大或太小。”
System.Web.Mvc.ValueProviderResult.ConvertSimpleType()
和System.ComponentModel.BaseNumberConverter.ConvertFrom()
引發異常。
用戶永遠不會輸入這樣的值,但是我不介意知道MVC中是否內置有任何機制可以(或將防止這種情況)(服務器端)。 像下面這樣的數字似乎沒有問題,只是看起來很小。
“ 0.555555555555555555555555555555555555”
歸根結底,我真的只需要9位數的精度。 支持這些值的數據庫表decimal(9,6)
。 我知道我可以為我的模型實現自定義模型綁定程序並從Request中手動收集值,但是必須有一些簡單的方法,例如自定義FilterAttribute
或可以在嘗試將其綁定到該值之前更正該值的東西。該模型,我不確定是什么,並且正在尋找建議。
我似乎回想過有關嘗試使用RangeAttribute
約束十進制值的一些問題,但我不記得這個問題。 也許您在MVC專家那里可以了解情況。
您可以使用Regex屬性將精度保留為十進制的十進制數。這還允許您在Regex失敗時添加自定義消息,例如“您的值在十進制后最多可以有9位。” 或類似的東西。 另外,如果您啟用了客戶端驗證,則Regex將在客戶端驗證和服務器端驗證中均起作用。
因此,經過幾個小時的頭部撞擊,我決定使用以下自定義模型活頁夾來獲取小數。 它確保所有十進制值在綁定它們之前都是可解析的。
public class TreasuryIndexRateDecimalBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var providerResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (providerResult != null)
{
decimal value;
if (!decimal.TryParse(providerResult.AttemptedValue, NumberStyles.Float, CultureInfo.CurrentCulture, out value))
{
// TODO: Decide whether to show an error
// bindingContext.ModelState.AddModelError(bindingContext.ModelName, "error message");
return 0m;
}
return Math.Round(value, 6);
}
return base.BindModel(controllerContext, bindingContext);
}
}
綁定是在Application_Start()
設置的,以將其注冊為所有十進制值。
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(decimal), new TreasuryIndexRateDecimalBinder());
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
除非有人提出了更有趣的方法,否則我會堅持下去。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.