【问题标题】:EditorFor on nullable DateTime - "Nullable object must have a value."EditorFor on nullable DateTime - “Nullable 对象必须有一个值。”
【发布时间】:2011-12-20 19:20:51
【问题描述】:

我正在尝试显示一个允许用户为某人输入新任务的表单。我正在使用 DateTime.cshtml EditorTemplate 来处理分配的 DateTime 值。不可为空的 DateTime 工作正常。可为空的 DateTime 会导致“InvalidOperationException:可为空的对象必须具有值”。

我有一个看起来像这样的简单视图模型:

AssignmentViewModel.cs:

public Person Person { get; set; }
public Assignment NewAssignment { get; set; }

Assignment.cs 包含:

public DateTime AssignmentStartDate { get; set; }
public DateTime? AssignmentEndDate { get; set; }

我的 AssignmentController Create() 方法如下所示:

public ViewResult Create(int personId)
{
    Person person = personRepository.GetPersonById(personId);
    var newAssignment = new AssignmentViewModel { Person = person, NewAssignment = new Assignment() };
    return View(newAssignment);
}

我的 Create.cshtml 视图如下所示:

@model AssignmentViewModel

@using (Html.BeginForm("Create", "Assignment"))
{
    @Html.Hidden("NewAssignment.PersonId", Model.Person.PersonId)
    @Html.LabelFor(x => x.NewAssignment.AssignmentStartDate):
    @Html.EditorFor(x => x.NewAssignment.AssignmentStartDate.Date, new { cssClass = "datePicker" })
    <br />
    @Html.LabelFor(x => x.NewAssignment.AssignmentEndDate):
    @Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value.Date, new { cssClass = "datePicker" })
    <br />
    <input type="submit" value="Send />
}

我的 DateTime.cshtml EditorTemplate 看起来像:

@model DateTime?

@{
    String modelValue = "";
    if (Model.HasValue)
    {
        if (Model.Value != DateTime.MinValue)
        {
            modelValue = Model.Value.ToShortDateString();
        }
    }
}

@Html.TextBox("", modelValue, new { @class = "datePicker" })

当我尝试加载“创建”视图时,我在“@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value)”行中遇到了上述异常。

您可能想知道为什么我要传入 AssignmentEndDate.Value.Date 而不是只传入 AssignmentEndDate;原因是因为我试图达到将 DateTime 拆分为 Date 和 TimeOfDay 字段并将它们与 DateTimeModelBinder 重新组合的地步。我正在使用与shown herehere 类似的技术。

我可以绕过错误,通过更改我的控制器 Create() 方法来实例化 ViewModel,并将 AssignmentEndDate 设置为 DateTime.MinValue,但这对于可为空的 DateTime 似乎完全错误:

var newAssignment = new AssignmentViewModel 
                        { 
                            Person = person, 
                            NewAssignment = new Assignment { AssignmentEndDate = DateTime.MinValue } 
                        };

在我通过为可为空的 DateTime 提供一个值来“绕过”错误之后,发生了一些奇怪的事情;不需要的可为空的 DateTime 属性 (AssignmentEndDate.Date) 未能通过客户端验证。尝试提交表单会以红色突出显示该字段。

我该如何正确处理?

【问题讨论】:

    标签: asp.net-mvc-3


    【解决方案1】:

    问题是您试图检索AssignmentEndDate.Value.Date,但AssignmentEndDatenull,这会导致此错误。

    由于您的编辑器模板接受DateTime?,您应该只传递AssignmentEndDate。换句话说,从视图中删除.Value.Date

    @Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, new { cssClass = "datePicker" })
    

    由于您的编辑器模板使用ToShortDateString(),因此根本不需要从日期“截断”时间。

    更新

    关于您希望拥有单独的“日期”和“时间”编辑器:

    您可以通过两种方式做到这一点。

    1 - 您当前的DateTime? 编辑器为Model.Value.Date 呈现一个字段,因此您可以简单地将其扩展为也为Model.Value.TimeOfDay 呈现一个字段。示例:

    @{
      DateTime? modelDate = (Model == null) ? (DateTime?)null : Model.Value.Date;
      TimeSpan? modelTime = (Model == null) ? (TimeSpan?)null : Model.Value.TimeOfDay;
    }
    @Html.TextBox(..., modelDate, new{@class="datePicker"})
    @Html.TextBox(..., modelTime, new{@class="timePicker"})
    

    2 - 您可以将上述功能拆分为 2 个单独的编辑器,“DateOnly”和“TimeOnly”。然后,更新您的视图以调用两个编辑器:

    @Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "DateOnly")
    @Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "TimeOnly")
    

    选择权取决于您,以及是否要将日期和时间部分分开或放在一起,但这就是我解决此问题的方法。

    【讨论】:

    • 嗨,斯科特,我实际上已经尝试过第一个修复。我得到“模板只能用于字段访问、属性访问、单维数组索引或单参数自定义索引器表达式。”第二个建议将解决这个直接错误,但不允许我有两个日期和时间字段;请参阅我的问题中以“您可能想知道...”开头的文字。
    • 第一部分是有道理的......这是一个 MVC 的东西,它不支持空检查逻辑。我会相应地更新我的答案。
    • 如果您想要 2 个单独的日期和时间字段,我相应地更新了我的答案。
    • 我在您的回答中没有看到任何关于为 DateTime 属性的日期和时间组件使用单独字段的内容?仅删除 .Value.Date 是行不通的。我现在放弃这个,我会接受你的回答,因为它回答了主要问题,但我找不到解决我遇到的所有问题的解决方案。 :|
    • 它可能来自 WPF 背景,但组合/拆分我认为属于 ViewModel 的数据,我建议添加一个镜像 assignment.cs 但包含 2 个 DateTimes 的“AssignmentViewModel”(对于开始/结束日期)和 2 TimeSpans(用于开始/结束时间)。我选择从 IViewModel 接口继承我的 ViewModels,并使用 public T CreateModel(); 函数将逻辑 return new Assignment(){ AssignmentStartDate=StartDate.Date.Add( StartTime ), AssignmentEndDate=EndDate.Date.Add( EndTime ) };
    【解决方案2】:

    在您的 Shared/DisplayTemplate 文件夹中创建一个 DateTime.cshtml

    @model Nullable<DateTime>
    @(Model != null ? string.Format(ViewData.ModelMetadata.DisplayFormatString ?? "{0:d}", Model) : string.Empty)
    

    这支持数据注释中的元数据(如果找到的话)。

    【讨论】:

      【解决方案3】:

      更新:GetValueOrDefault 将其视为 DateTime,因此附加了所需的字段验证器,因为原始表达式是针对日期时间而不是可为空的日期时间。

      因此下面的解决方案不起作用。

      像提问者一样,我也从这里使用了 DateTimeModelBinder: Here's the Link

      这就是我解决类似情况的方法:

      @Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.GetValueOrDefault().Date)
      

      这就是我的 DateTime EditorTemplate 的样子:

      @model DateTime
      
      @Html.TextBox("", Model != default(DateTime) ? Model.ToShortDateString() : String.Empty, new { @class = "datepicker", @maxlength = "10" })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-15
        相关资源
        最近更新 更多