17

我如何告訴我的控制器/模型它應該期望什麼樣的文化解析日期時間?ASP.NET MVC日期時間文化問題當傳遞價值回控制器

我正在使用一些this post實現jQuery的日期選擇器到我的mvc應用程序。

當我提交日期時,它得到「在翻譯中丟失」我沒有使用美國格式的日期,所以當它被髮送到我的控制器它簡直成爲空。

我已經在用戶選擇日期的形式:

@using (Html.BeginForm("List", "Meter", FormMethod.Get)) 
{ 
    @Html.LabelFor(m => m.StartDate, "From:") 
    <div>@Html.EditorFor(m => m.StartDate)</div> 

    @Html.LabelFor(m => m.EndDate, "To:") 
    <div>@Html.EditorFor(m => m.EndDate)</div> 
} 

我做了一個編輯模板此,實現了jQuery日期選擇器:

@model DateTime 
@Html.TextBox("", Model.ToString("dd-MM-yyyy"), new { @class = "date" }) 

我再創建日期選擇器這樣的小部件。

$(document).ready(function() { 
    $('.date').datepicker({ dateFormat: "dd-mm-yy" }); 
}); 

這一切工作正常。

這裏就是問題的開始,這是我的控制器:

[HttpGet] 
public ActionResult List(DateTime? startDate = null, DateTime? endDate = null) 
{ 
    //This is where startDate and endDate becomes null if the dates dont have the expected formatting. 
} 

這就是爲什麼我想以某種方式告訴我的控制器,它應該期待什麼文化? 我的模型錯了嗎?我能以某種方式告訴它使用哪種文化,就像數據註釋屬性一樣?

public class MeterViewModel { 
    [Required] 
    public DateTime StartDate { get; set; } 
    [Required] 
    public DateTime EndDate { get; set; } 
} 

編輯:this link解釋了我的問題,也是一個很好的解決它以及。感謝gdoron

+1

使用一種格式要求。 http://stackoverflow.com/a/28219557/960997 – rnofenko

+0

@fomaa我現在使用datepicker與[altField](http://api.jqueryui.com/datepicker/#option-altField)和[altFormat]( http://api.jqueryui.com/datepicker/#option-altFormat)選項來提供一個帶有文化不變版本日期的隱藏字段(就像您提到的ISO8601一樣)。然後提交該字段,我覺得這是一個更好的解決方案。 – FRoZeN

回答

11

您可以創建一個活頁夾擴展來處理文化格式的日期。

這是我寫的處理與十進制類型相同的問題的樣本,希望你的想法

public class DecimalModelBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
    ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
    ModelState modelState = new ModelState { Value = valueResult }; 
    object actualValue = null; 
    try 
    { 
     actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture); 
    } 
    catch (FormatException e) 
    { 
     modelState.Errors.Add(e); 
    } 

    bindingContext.ModelState.Add(bindingContext.ModelName, modelState); 
    return actualValue; 
    } 
} 

更新

要使用它,只需聲明粘結劑在Global.asax中像這

protected void Application_Start() 
{ 
    AreaRegistration.RegisterAllAreas(); 
    RegisterGlobalFilters(GlobalFilters.Filters); 
    RegisterRoutes(RouteTable.Routes); 

    //HERE you tell the framework how to handle decimal values 
    ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder()); 

    DependencyResolver.SetResolver(new ETAutofacDependencyResolver()); 
} 

然後,當模型綁定器必須做一些工作,它會自動知道該怎麼做。 例如,這是一個包含某些類型爲decimal的屬性的模型。我乾脆什麼也不做

[HttpPost] 
public ActionResult Edit(int id, MyViewModel viewModel) 
{ 
    if (ModelState.IsValid) 
    { 
    try 
    { 
     var model = new MyDomainModelEntity(); 
     model.DecimalValue = viewModel.DecimalValue; 
     repository.Save(model); 
     return RedirectToAction("Index"); 
    } 
    catch (RulesException ex) 
    { 
     ex.CopyTo(ModelState); 
    } 
    catch 
    { 
     ModelState.AddModelError("", "My generic error message"); 
    } 
    } 
    return View(model); 
} 
+0

你可以在你使用它的地方顯示一個例子嗎? – FRoZeN

+0

以示例更新了答案。我希望它可以幫助你更好 – Iridio

+0

謝謝,這可以幫助我很多:) – FRoZeN

3

提交日期時,您應該始終嘗試以「yyyy-MM-dd」格式提交。這將使它成爲獨立於文化的國家。

我通常有一個隱藏字段,以這種格式保存日期。這是使用jQuery UI的日期選擇器相對簡單。

+0

@Dibbyswift:我在想隱藏的領域,但不知道這是要走的路,因爲我不想要不尋常的隱藏領域。但現在有了第二個意見,我可能會朝這個方向發展。 – FRoZeN

+0

隱藏字段的優點是,您可以有一個可見的「顯示」字段,允許用戶以用戶友好的格式提供日期,而隱藏字段只需保留您所需格式的值。 – Digbyswift

10

這個問題的產生是因爲您使用的是表上的GET方法。 MVC中的QueryString值提供程序始終使用Invariant/US日期格式。請參閱:MVC DateTime binding with incorrect date format

有三種解決方案:

  1. 更改方法POST。
  2. 正如其他人所說,在提交之前將日期格式更改爲ISO 8601「yyyy-mm-dd」。
  3. 使用自定義聯編程序始終將查詢字符串日期視爲GB。如果你這樣做,你必須確保所有日期都以這樣的形式:使用IModelBinder

    public class DateTimeBinder : IModelBinder 
        { 
         public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
         { 
          var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
          var date = value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture); 
    
          return date;  
         } 
        } 
    

    而且在

    public class UKDateTimeModelBinder : IModelBinder 
    { 
    private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
    
    /// <summary> 
    /// Fixes date parsing issue when using GET method. Modified from the answer given here: 
    /// https://stackoverflow.com/questions/528545/mvc-datetime-binding-with-incorrect-date-format 
    /// </summary> 
    /// <param name="controllerContext">The controller context.</param> 
    /// <param name="bindingContext">The binding context.</param> 
    /// <returns> 
    /// The converted bound value or null if the raw value is null or empty or cannot be parsed. 
    /// </returns> 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
        var vpr = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
    
        if (vpr == null) 
        { 
         return null; 
    
        } 
    
        var date = vpr.AttemptedValue; 
    
        if (String.IsNullOrEmpty(date)) 
        { 
         return null; 
        } 
    
        logger.DebugFormat("Parsing bound date '{0}' as UK format.", date); 
    
        // Set the ModelState to the first attempted value before we have converted the date. This is to ensure that the ModelState has 
        // a value. When we have converted it, we will override it with a full universal date. 
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, bindingContext.ValueProvider.GetValue(bindingContext.ModelName)); 
    
        try 
        { 
         var realDate = DateTime.Parse(date, System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB")); 
    
         // Now set the ModelState value to a full value so that it can always be parsed using InvarianCulture, which is the 
         // default for QueryStringValueProvider. 
         bindingContext.ModelState.SetModelValue(bindingContext.ModelName, new ValueProviderResult(date, realDate.ToString("yyyy-MM-dd hh:mm:ss"), System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB"))); 
    
         return realDate; 
        } 
        catch (Exception) 
        { 
         logger.ErrorFormat("Error parsing bound date '{0}' as UK format.", date); 
    
         bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format("\"{0}\" is invalid.", bindingContext.ModelName)); 
         return null; 
        } 
    } 
    } 
    
+0

+1這也有幫助 – FRoZeN

+0

+1,我切換到帖子和值被接受。 – Malkin

+0

ISO 8601「yyyy-mm-dd」日期格式適用於我。我正在用雙語形式掙扎,這是一個很好的妥協。謝謝。 – vbocan

18

您可以更改使用的用戶區域性的默認模型綁定在Global.asax寫:

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder()); 
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder()); 

更多的this excellent blog描述爲什麼MVC框架團隊實施了DEF對所有用戶都是文化。

+3

+1非常好的博客文章,完全解釋我的問題。 – FRoZeN

+0

如果日期無效,會引發異常。 – mikek3332002

+0

我試過這個,但它不適用於GET,例如來自ActionLinks。 –

0

是爲我做的

<system.web>  
     <globalization enableClientBasedCulture="true" uiCulture="Auto" culture="Auto" /> 
    </system.web> 
+0

不確定我的記憶是否有效,但是如果您使用了文章,但是這不起作用,但沒有得到。請按照以下其中一個答案中的鏈接進行操作:使用不正確的日期格式的MVC DateTime綁定 – FRoZeN

1

的伎倆爲什麼不簡單地檢查數據的文化,並將其轉換爲這樣的?這個簡單的方法允許我使用的機型強類型的日期,演出動作鏈接和編輯字段中所需的語言環境,而不必在所有大驚小怪結合它放回一個強類型的日期時間:所有

public class DateTimeBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
     return value.ConvertTo(typeof(DateTime), value.Culture); 
    } 
} 
相關問題