17

Jak mogę poinformować kontrolera/model, jakiego rodzaju kultury powinien oczekiwać podczas analizowania datetime?Problem związany z kulturą datetime kultury MVC w programie ASP.NET podczas przekazywania wartości do kontrolera

Wykorzystałem niektóre z this post do wdrożenia datepicker jquery w mojej aplikacji mvc.

Po zgłoszeniu daty "zagubienia w tłumaczeniu" nie używam formatowania US dla daty, więc kiedy zostanie wysłany do kontrolera, staje się on pusty.

Mam formularz, w którym użytkownik wybiera datę:

@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> 
} 

zrobiłem szablon edycji do tego, aby realizować datepicker jquery:

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

I następnie utworzyć datepicker takie widżety.

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

Wszystko to działa dobrze.

Tu zaczynają się problemy, to jest mój kontroler:

[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. 
} 

Dlatego chciałbym jakoś powiedzieć mój kontroler co kultura powinna się spodziewać? Czy mój model jest niewłaściwy? czy mogę jakoś powiedzieć, jakiej kultury użyć, np. z atrybutami adnotacji danych?

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

Edit: this link wyjaśnia mój problem i bardzo dobre rozwiązanie dla niego również. Dzięki gdoronowi

+1

Zastosowanie jednego formatu dla wszystkich upraszanie. http://stackoverflow.com/a/28219557/960997 – rnofenko

+0

@fomaa Używam teraz datepicker z [altField] (http://api.jqueryui.com/datepicker/#option-altField) i [altFormat] (http://api.jqueryui.com/datepicker/#option-altFormat) opcje dostarczenia ukrytego pola z niezmienną wersją daty kultury (jak wspominasz o ISO8601). Następnie przesyłając to pole, uważam, że jest to lepsze rozwiązanie. – FRoZeN

Odpowiedz

11

Możesz utworzyć rozszerzenie Binder, aby obsłużyć datę w formacie kultury.

To jest przykład napisałem obsługiwać ten sam problem z dziesiętny typu, nadzieję masz pomysł

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

Aktualizacja

go użyć po prostu zadeklarować jak spoiwo w Global.asax to

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

Następnie, gdy modelator musi wykonać pewną pracę, automatycznie rozpozna, co należy zrobić. Na przykład jest to akcja z modelem zawierającym pewne właściwości typu dziesiętnego. Po prostu nie robię nic, aby uzyskać więcej informacji:

[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

możesz pokazać przykład ze swojego kodu, gdzie go używasz? – FRoZeN

+0

Zaktualizowano odpowiedź na przykładzie. Mam nadzieję, że pomoże ci to lepiej – Iridio

+0

dziękuję, to mi bardzo pomaga :) – FRoZeN

3

Podczas przesyłania daty zawsze należy spróbować przesłać go w formacie "rrrr-MM-dd". Pozwoli to na uniezależnienie się od kultury.

Zwykle mam ukryte pole, które zachowuje datę w tym formacie. Jest to stosunkowo proste przy użyciu datepika jQuery UI.

+0

@Dibbyswift: Myślałem o ukrytym polu, ale nie byłem pewien, czy to jest droga, ponieważ nie chcę niepotrzebnych ukrytych pól. Ale teraz z drugą opinią mogę iść w tym kierunku. – FRoZeN

+0

Zaletą ukrytego pola jest to, że możesz mieć widoczne pole "wyświetlania", które umożliwia użytkownikom podawanie daty w przyjaznym dla użytkownika formacie, podczas gdy ukryte pole po prostu zachowuje wartość w wymaganym formacie. – Digbyswift

10

Ten problem powstaje, ponieważ używasz metody GET w formularzu. Dostawca wartości QueryString w MVC zawsze używa formatu daty niezmienniczej/amerykańskiej.Zobacz: MVC DateTime binding with incorrect date format

Istnieją trzy rozwiązania:

  1. zmienić metodę aby móc pisać.
  2. Jak mówi ktoś inny, przed wysłaniem zmień format daty na ISO 8601 "rrrr-mm-dd".
  3. Użyj niestandardowego spinacza, aby zawsze traktować daty Ciągów zapytania jako GB. Jeśli to zrobisz, musisz upewnić się, że wszystkie terminy są w tej formie:

    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 To też było pomocne – FRoZeN

+0

+1, przełączyłem się na post i wartości zostały zaakceptowane. – Malkin

+0

Format daty ISO 8601 "yyyy-mm-dd" zadziałał dla mnie. Walczyłem z formą dwujęzyczną i jest to dobry kompromis. Dziękuję Ci. – vbocan

18

można zmienić domyślne spinacza modelu korzystania z kultury obsługi za pomocą 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;  
     } 
    } 

I global.asax zapisu:

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

Czytaj więcej na this excellent blog które opisują dlaczego zespół ramy MVC wdrożył def ault Kultura dla wszystkich użytkowników.

+3

+1 bardzo ładny wpis na blogu dokładnie wyjaśniający mój problem. – FRoZeN

+0

Wyrzuci wyjątek, jeśli jest to niepoprawna data. – mikek3332002

+0

Próbowałem tego i nie działało dla GET, np. z ActionLinks. –

0

że wystarczyły dla mnie

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

Nie jestem pewien, czy moja pamięć służy, ale to działa, jeśli używasz postu, ale nie otrzymujesz. skorzystaj z linku w jednej z odpowiedzi o nazwie: MVC powiązanie DateTime z nieprawidłowym formatem daty – FRoZeN

1

Dlaczego nie po prostu zbadać kulturę danych i przekształcić go jako takiego? To proste podejście pozwoliło mi używać silnie wpisane daty modeli, linki pokaz działania i pola edycyjne w żądanej lokalizacji i nie trzeba zamieszanie w ogóle wiążące go z powrotem do silnie wpisany DateTime:

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); 
    } 
} 
Powiązane problemy