まあ、最近私が把握できないような興味深い問題に立ち向かいました。MVC3 - モデルがTimeSpanを必要とするときにヌルアイテムを渡す
私が取得エラーメッセージは次のとおりです。
{"The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type 'System.TimeSpan'."}
私は、データベースへの新しいエントリを提出しようとすると、これが発生します。したがって、提出されているものの詳細。
モデルクラス:コントローラクラスにおけるpublic class EventModel
{
[Key]
public int EventID { get; set; }
[DisplayName("Booking title")]
[Required(ErrorMessage="Please provide a title for the booking")]
public string Title { get; set; }
[DataType(DataType.Date)]
[DisplayName("Start date")]
[DisplayFormat(DataFormatString="{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime StartDateTime { get; set; }
[DisplayName("End date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
[IsDateAfter("StartDateTime", true, ErrorMessage="End date must be on or after the start date")]
public DateTime EndDateTime { get; set; }
public long StartTicks { get; set; }
public long EndTicks { get; set; }
[NotMapped]
[DisplayName("Start Time")]
public TimeSpan StartTime
{
get { return TimeSpan.FromTicks(StartTicks); }
set { StartTicks = value.Ticks; }
}
[NotMapped]
[DisplayName("End Time")]
public TimeSpan EndTime
{
get { return TimeSpan.FromTicks(EndTicks); }
set { EndTicks = value.Ticks; }
}
[DefaultValue(2)]
[DisplayName("Booking is")]
public int BookingStatus { get; set; }
[DisplayName("Set recurrence")]
[DefaultValue(false)]
public bool DoesRecur { get; set; }
[DisplayName("Set recurrence type")]
public string Pattern { get; set; }
[DisplayName("Set the day this happens on ")]
public int DayIndex { get; set; }
[DisplayName("Choose the day instance this recurs on")]
public int DayCount { get; set; }
[DisplayName("Day ")]
[NotMapped]
public string Day { get; set; }
[DisplayName("Instance")]
[NotMapped]
public string Instance { get; set; }
// links resource to a user/member
[DisplayName("Booked by")]
[NotMapped]
public string BookerName { get; set; }
public Guid MemberID { get; set; }
// links resource to a resource type
[DisplayName("Resource required:")]
public int ResourceID { get; set; }
}
アクションメソッド:ビュー項目のすぐ
[HttpGet]
public ActionResult Create(DateTime eventDate)
{
var days = from DayOfWeek d in Enum.GetValues(typeof(DayOfWeek))
select new { ID = (int) d, Name = (DayOfWeek)d };
var instance = from DayInstance i in Enum.GetValues(typeof(DayInstance))
select new { ID = (int) i, Name = (DayInstance)i };
MembershipUser mu = Membership.GetUser(HttpContext.Profile.UserName);
CreateEventViewModel model = new CreateEventViewModel()
{
Event = new EventModel()
{
StartDateTime = eventDate,
EndDateTime = eventDate,
MemberID = (Guid)mu.ProviderUserKey
},
Resources = DBContext.Resources.ToList(),
Patterns = DBContext.Patterns.ToList(),
ResourceTypes = DBContext.ResourceTypes.ToList()
};
ViewData["dayOfWeek"] = new SelectList(days, "ID", "Name", DayOfWeek.Monday);
ViewData["dayInstance"] = new SelectList(instance, "ID", "Name", DayInstance.First);
return View(model);
}
[HttpPost]
public ActionResult Create(CreateEventViewModel em)
{
if (ModelState.IsValid)
{
// get the resource turn aournd time
double turnAround = rc.GetResourceTurnAround(em.Event.ResourceID);
MembershipUser mu = Membership.GetUser(HttpContext.Profile.UserName);
em.Event.MemberID = (Guid) mu.ProviderUserKey;
em.Event.BookingStatus = 2;
// need to get the time added to the date.
DateTime actualStartPoint = new DateTime(em.Event.StartDateTime.Ticks + em.Event.StartTicks);
DateTime actualEndPoint = new DateTime(em.Event.EndDateTime.Ticks + em.Event.EndTicks);
em.Event.StartDateTime = actualStartPoint;
em.Event.EndDateTime = actualEndPoint;
// add turn around time to the end of the event
em.Event.EndDateTime = em.Event.EndDateTime.AddMinutes(turnAround);
// needed becase these are handled slighty differently to the rest of the model
em.Event.DayIndex = int.Parse(Request.Form.GetValues("dayOfWeek").GetValue(0).ToString());
em.Event.DayCount = int.Parse(Request.Form.GetValues("dayInstance").GetValue(0).ToString());
DBContext.Events.Add(em.Event);
DBContext.SaveChanges();
// get the resource owner
MembershipUser resourceOwner = Membership.GetUser(rc.GetResourceOwnerByID(em.Event.ResourceID));
// email the admin team and the user the details of this booking
// get the email address of the user making the booking
StringBuilder message = new StringBuilder();
message.AppendFormat("Thank you for your booking, this is now being reviewed by the team.\nThe details of your booking are included for confirmation.\n");
message.AppendFormat("Booking Title: {0}\nResource: {1}\n Date: {2} {3} (this includes our turn around time added on)\n", em.Event.Title, rc.GetResourceNameByID(em.Event.ResourceID), actualStartPoint, actualEndPoint);
message.AppendFormat("You can log in at any time to review your bookings.\nYou will receive an email when the team have reviewed this request\nMany thanks\n");
EmailHandler eh = new EmailHandler();
eh.SetRecipient(Membership.GetUser().Email);
eh.AddAdminEmail();
eh.AddBcc(resourceOwner.Email);
eh.SetSubject("Booking Requested");
eh.SetBody(message.ToString());
eh.sendMessage();
return RedirectToAction("Index");
}
else
{
return View();
}
}
- メインビュー:
@model AssetManager.Models.CreateEventViewModel
@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend id="bookingLegend">Place Booking</legend>
<div class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Event.Title)
@Html.ValidationMessageFor(model => model.Event.Title)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.StartDateTime)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Event.StartDateTime, new { @class = "date" })
@Html.ValidationMessageFor(model => model.Event.StartDateTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label timeSelector">
@Html.LabelFor(model => model.Event.StartTime)
</div>
<div class="editor-field timeSelector">
@Html.EditorFor(model => model.Event.StartTime)
@Html.ValidationMessageFor(model => model.Event.StartTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.EndDateTime)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Event.EndDateTime, new { @class = "date" })
@Html.ValidationMessageFor(model => model.Event.EndDateTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label timeSelector">
@Html.LabelFor(model => model.Event.EndTime)
</div>
<div class="editor-field timeSelector">
@Html.EditorFor(model => model.Event.EndTime)
@Html.ValidationMessageFor(model => model.Event.EndTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
@Html.Label("Select Resource Type")
</div>
<div class="editor-field">
@Html.DropDownList("ResourceTypes", new SelectList(Model.ResourceTypes, "ResourceTypeID", "Title"), "-- Select Resource Type --", new { @id = "ddlResourceTypes" })
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.ResourceID)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.Event.ResourceID, new SelectList(Enumerable.Empty<SelectListItem>(), "ResourceType", "Name"), "-- Select Resource --", new { @id = "ddlResources" })
@Html.ValidationMessageFor(model => model.Event.ResourceID)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.DoesRecur)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Event.DoesRecur)
@Html.ValidationMessageFor(model => model.Event.DoesRecur)
</div>
</div>
<div id="recurType" class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.Pattern)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.Event.Pattern, new SelectList(Model.Patterns, "PatternCode", "Pattern"), "-- Select Recurrence Pattern --")
@Html.ValidationMessageFor(model => model.Event.Pattern)
</div>
</div>
<div id="recurDayHappens" class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.DayIndex)
</div>
<div class="editor-field">
@Html.DropDownList("dayOfWeek")
@Html.ValidationMessageFor(model => model.Event.DayIndex)
</div>
</div>
<div id="recurInstance" class="controlcontainer">
<div class="editor-label">
@Html.LabelFor(model => model.Event.DayCount)
</div>
<div class="editor-field">
@Html.DropDownList("dayInstance")
@Html.ValidationMessageFor(model => model.Event.DayCount)
</div>
</div>
<div class="controlcontainer">
<p>
<input class="subButton" type="submit" value="Create" />
<input id="cancelBtn" class="cancelButton" type="button" value="Cancel" onclick="location.href='@Url.Action("Index", "Calendar")'" />
</p>
</div>
</fieldset>
}
がそこでありますTimeSpanアイテムのエディタテンプレート:
そして最後にTimeBinderクラス:
それだpublic class TimeBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Ensure there's incomming data
var key_hours = bindingContext.ModelName + ".Hours";
var valueProviderResult_hours = bindingContext.ValueProvider
.GetValue(key_hours);
var key_minutes = bindingContext.ModelName + ".Minutes";
var valueProviderResult_minutes = bindingContext.ValueProvider
.GetValue(key_minutes);
if (valueProviderResult_hours == null || string.IsNullOrEmpty(valueProviderResult_hours.AttemptedValue)
|| valueProviderResult_minutes == null || string.IsNullOrEmpty(valueProviderResult_minutes.AttemptedValue))
{
return null;
}
// Preserve it in case we need to redisplay the form
bindingContext.ModelState.SetModelValue(key_hours, valueProviderResult_hours);
bindingContext.ModelState.SetModelValue(key_minutes, valueProviderResult_minutes);
// Parse
var hours = ((string[])valueProviderResult_hours.RawValue)[0];
var minutes = ((string[])valueProviderResult_minutes.RawValue)[0];
// A TimeSpan represents the time elapsed since midnight
var time = new TimeSpan(Convert.ToInt32(hours), Convert.ToInt32(minutes), 0);
return time;
}
}
、それが関与しているすべてのコードです。このエラーがなぜ発生するのか、私は完全に困惑しています。原因と解決策に関するアイデアや提案は非常に高く評価されています。
感謝 nathj07
EDIT PK、私は番目のTimeSpanエディタテンプレートとdiffernt何か試してみました:これは、このエラーを克服しているようだが、今、私は問題aを取得
@model TimeSpan?
@Html.DropDownList("Hours", Enumerable.Range(0, 24)
.Select(i => new SelectListItem
{
Value = i.ToString(),
Text = i.ToString(),
Selected = Model.HasValue ? Model.Value.Hours == i : false
})) :
@Html.DropDownList("Minutes", Enumerable.Range(0, 60)
.Select(i => new SelectListItem
{
Value = i.ToString(),
Text = i.ToString(),
Selected = Model.HasValue ? Model.Value.Minutes == i : false
}))
を少し下に。ビューにはDropDownList( "ResourceTypes" ....)があります。これは基本的にDropDownListFor(model => model.Event.ResourceID .....)に表示される内容を制御するためのドロップダウンリストです。シンプルJavaScriptを作品:
$(document).ready(function() {
$("#ddlResourceTypes").change(function() {
var idResourceType = $('#ddlResourceTypes').val();
$.getJSON("/Resource/LoadResourcesByType", { id: idResourceType },
function (resourceData) {
var select = $("#ddlResources");
select.empty();
select.append($('<option/>', {
value: 0,
text: "-- Select Resource --"
}));
$.each(resourceData, function (index, itemData) {
select.append($('<option/>', {
value: itemData.Value,
text: itemData.Text
}));
});
});
});
});
今、私が手に問題がある:
オブジェクト参照がオブジェクトのDropDownListで
のインスタンスに設定されていない( "ResourceTypes" .....)
これに関するアイデアですか?
これはすべて意味があるので、Create Action Methodを試してみると、実際には1つのエラーがあることがわかります。問題は実際にはResourceTypesフィールドの変換です:System.StringをAssetManager.Models.ResourceTypeに変換できませんこのモデルでは変換をしたくありません(イベント)ResourceTypeを格納したくないのですが、コントロールのリソースドロップダウンリストのこれで次にどこへ行くべきかについての提案はありますか? – nathj07
私はいくつかのコードを追加しました。 – GvS
コードをありがとう、それは意味があり、私はそれを実装しました。 ResourceTypeの問題(IsValidがfalseになる原因)については、ここにアイディアがありますか?私はこれが何か簡単ではないと確信しています。 – nathj07