Asp.net mvc 3 可为空的日期时间上的编辑器;可为null的对象必须有一个值;

Asp.net mvc 3 可为空的日期时间上的编辑器;可为null的对象必须有一个值;,asp.net-mvc-3,Asp.net Mvc 3,我试图显示一个表单,允许用户为一个人输入新的任务。我正在使用DateTime.cshtml EditorTemplate来处理分配的日期时间值。不可为空的DateTime工作正常。可空DateTime导致“InvalidOperationException:可空对象必须有值。” 我有一个简单的viewmodel,如下所示: @model AssignmentViewModel @using (Html.BeginForm("Create", "Assignment")) { @Html

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

我有一个简单的viewmodel,如下所示:

@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 />
}
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);
}
@model DateTime?

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

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

@Html.TextBox("", Model != default(DateTime) ? Model.ToShortDateString() : String.Empty, new { @class = "datepicker", @maxlength = "10" })
我的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 />
}
当我尝试加载Create视图时,在“@Html.EditorFor(x=>x.NewAssignment.AssignmentEndDate.Value)”行上出现了上述异常

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

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

var newAssignment = new AssignmentViewModel 
                        { 
                            Person = person, 
                            NewAssignment = new Assignment { AssignmentEndDate = DateTime.MinValue } 
                        };
在我通过为可为null的DateTime提供一个值来“绕过”错误之后,发生了一些奇怪的事情;非必需的可为Null的DateTime属性(AssignmentEndDate.Date)无法通过客户端验证。尝试提交表单时,将以红色突出显示字段


如何正确处理此问题?

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

由于编辑器模板接受一个
DateTime?
,因此只需传递
AssignmentEndDate
。换句话说,从视图中删除
.Value.Date

@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, new { cssClass = "datePicker" })
由于编辑器模板使用的是
ToSortDateString()
,因此根本不需要从日期开始“截断”时间

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

你可以用两种方法来做

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

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

选择取决于您,您希望将日期和时间部分分开还是放在一起,但这就是我解决此问题的方法。

更新:GetValueOrDefault将其视为日期时间,因此需要附加所需的字段验证器,因为原始表达式用于datetime,而不是可为空的datetime

因此,下面的解决方案不起作用。 与asker一样,我也在这里使用了DateTimeModelBinder:

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

@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.GetValueOrDefault().Date)
这就是我的DateTime EditorTemplate的外观:

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

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

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

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

在共享/显示模板文件夹中创建DateTime.cshtml

@model Nullable<DateTime>
@(Model != null ? string.Format(ViewData.ModelMetadata.DisplayFormatString ?? "{0:d}", Model) : string.Empty)
@模型可为空
@(Model!=null?string.Format(ViewData.ModelMetadata.DisplayFormatString??“{0:d}”,Model):string.Empty)

这支持数据注释中的元数据,以便在找到时使用。

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