C# MVC 4 ViewModel not accepting null DateTime

2019-06-08 13:23发布

问题:

I'm in a bit of trouble here learning C# and mvc4.

The Problem occurs in the Filter part of my application. I have an ViewModel that grabs the list of "Listar_Produtos" of the database, and some fields for searching options.

What I intend to do is make the filter accept any field, even if it's null values. Because i'll make the filter based on these paramters.

I Have an Viewmodel:

using Foolproof;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace Ecommerce.Models.Repository
{
    public class Produto_Repository
    {
        public class Index_Listar_Produtos
        {
            public List<Listar_Produto> Index_List_Produto { get; set; }

            [Display(Name = "Data de Cadastro Inicial")]
            [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
            public Nullable<DateTime> CadastroInicialData { get; set; }

            [Display(Name = "Data de Cadastro Final")]
            [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
            [GreaterThanOrEqualTo("CadastroInicialData", ErrorMessage = "\"Data Inicial\", deve ser maior que \"Data Final\"")]
            public Nullable<DateTime> CadastroFinalData { get; set; }
        }
    }
}

And I have the following View:

<td>
    @Html.LabelFor(Model => Model.CadastroInicialData)<br />
    @Html.TextBoxFor(Model => Model.CadastroInicialData, "{0:dd/MM/yyyy}")
    @Html.ValidationMessageFor(Model => Model.CadastroInicialData)
</td>
<td>
    @Html.LabelFor(Model => Model.CadastroFinalData)<br />
    @Html.TextBoxFor(Model => Model.CadastroFinalData, "{0:dd/MM/yyyy}")
    @Html.ValidationMessageFor(Model => Model.CadastroFinalData)
</td>

In my Controller I have:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Filtro(Produto_Repository.Index_Listar_Produtos ViewModel)
{
    if (!ModelState.IsValid)
    {
        Produto_Repository.Index_Listar_Produtos Model_list = Produto_Repository.GetProdutoByAll();
        ViewModel.Index_List_Produto = Model_list.Index_List_Produto;
        return View("Index", ViewModel);
    }
}

Where "Produto_Repository.GetProdutoByAll();" returns the list of "Produtos" again.

The code works fine and well if I provide dates in the form. The dates are in "pt-BR" format: 23/03/2013.

But if I provide nothing in the Fields (both Datefields in my View), than the "if(!ModelState.IsValid)" returns true and enters the "if", because both "CadastroInicialData" and "CadastroFinalData" comes with null values

The desired behavior is that the ViewModel could accept null or empty values that are granted by "Nullable" or "DateTime?".

I Tryed to insert values to the nullable date fields doing the following:

if (ViewModel.CadastroInicialData == null)
    ViewModel.CadastroInicialData = Convert.ToDateTime("01/01/2013");
if (ViewModel.CadastroFinalData == null)
    ViewModel.CadastroFinalData = Convert.ToDateTime("01/01/2013");

But now the ViewModel returns the following error: "is an invalid date format"

One note is that i'm using the following 'solution' for converting datetimes for pt-BR dateformat on the following question: Format datetime in asp.net mvc 4

How I make the ViewModel accept null values when the text fields are not filled with dates? I'm kinda confused here. I apreciate any help ! Thanks !

回答1:

You can add another properties to your Model class, then use them to determine if the value is null or not. Also implement them in your view. See below code:

Model

public class Index_Listar_Produtos
{
    public List<Listar_Produto> Index_List_Produto { get; set; }

    [Display(Name = "Data de Cadastro Inicial")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public Nullable<DateTime> CadastroInicialData { get; set; }

    [Display(Name = "Data de Cadastro Final")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    [GreaterThanOrEqualTo("CadastroInicialData", ErrorMessage = "\"Data Inicial\", deve ser maior que \"Data Final\"")]
    public Nullable<DateTime> CadastroFinalData { get; set; }


    public string GetStringTypeCadastroInicialData
    {
        get { return CadastroInicialData != null ? CadastroInicialData.Value.ToShortDateString() : DateTime.MinValue.ToShortDateString()(Or empty string ); }
    }

    public string GetStringTypeCadastroFinalData
    {
        get { return CadastroInicialData != null ? CadastroFinalData.Value.ToShortDateString() : DateTime.Now.ToShortDateString(); }
    }

}

View

<td>
    @Html.LabelFor(Model => Model.GetStringTypeCadastroInicialData)<br />
    @Html.TextBoxFor(Model => Model.GetStringTypeCadastroInicialData)
    @Html.ValidationMessageFor(Model => Model.GetStringTypeCadastroInicialData)
</td>
<td>
    @Html.LabelFor(Model => Model.GetStringTypeCadastroFinalData)<br />
    @Html.TextBoxFor(Model => Model.GetStringTypeCadastroFinalData)
    @Html.ValidationMessageFor(Model => Model.GetStringTypeCadastroFinalData)
</td>


回答2:

Instead of

public Nullable<DateTime> CadastroInicialData { get; set; }

try

public DateTime? CadastroInicialData { get; set; }

and then to see if it has a value, you can do:

if (CadastroInicialData.HasValue)
{
    // do your processing here
}


回答3:

Your model state is failing because of the GreaterThan data annotation on your second nullable DateTime value. null is not greater than null, so it is failing. You will either need to modify the GreaterThan data annotation to not compare if the values if they are null or remove that data annotation and do the comparison yourself. An example could be:

if(model.CadastroInicialData.HasValue() && model.CadastroFinalData.HasValue() && model.CadastroInicialData.Value > model.CadastroFinalData.Value){
    ModelState.AddModelError("CadastroInicialData", "CadastroInicialData must be less than CadastroFinalData");
}

if(!modelState.IsValid()){
   Produto_Repository.Index_Listar_Produtos Model_list = Produto_Repository.GetProdutoByAll();
   ViewModel.Index_List_Produto = Model_list.Index_List_Produto;
   return View("Index", ViewModel);
}


回答4:

Instead of using Convert.ToDateTime use DateTime.TryParseExact, and you can specify format and culture. The below code should work and fix the errors:

if (ViewModel.CadastroInicialData == null)
    ViewModel.CadastroInicialData = DateTime.TryParseExact(text, "MM/dd/yyyy",
                           CultureInfo.InvariantCulture,
                           DateTimeStyles.None,
                           out "01/01/2013");
if (ViewModel.CadastroFinalData == null)
    ViewModel.CadastroFinalData = DateTime.TryParseExact(text, "MM/dd/yyyy",
                           CultureInfo.InvariantCulture,
                           DateTimeStyles.None,
                           out "01/01/2013");