2011-12-09 4 views
2

まあ、最近私が把握できないような興味深い問題に立ち向かいました。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 
    }))&nbsp;: 
@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" .....)

これに関するアイデアですか?

答えて

1

無効なフォームをPOSTすると、コードreturn View()で終了します。

モデルを渡さずに同じビューを表示すると、モデルはnullになります。最初にコードに値が必要なのは、TimeSpanのエディタにあります。その値は今はnullableですが、nullであることをテストすることはありません。

変更へのリターン:

return View(em); 

モデルを渡すか、モデルを再構築し、合格するために、GETアクションからコードを使用する:

return Create(/* Your create date */); 

編集コメント
後 ModelBinderのエラーは、次の行によって発生する可能性があります。

 var hours = ((string[])valueProviderResult_hours.RawValue)[0]; 
     var minutes = ((string[])valueProviderResult_minutes.RawValue)[0]; 

アレイをstring[]に変換します。私はできるだけ遅く文字列への変換を行い、そしてそれにより、よりエラーの証拠になるだろう:これは単なるオブジェクトの配列にキャストされますので、失敗する以下の変更があります

 var hours = Convert.ToString(((object[])valueProviderResult_hours.RawValue).FirstOrDefault()) ?? "00"; 

。最初の要素を取るか、nullを返し、Convertを使用して文字列に変換し、結果がまだnullの場合は "00"を返します。

+0

これはすべて意味があるので、Create Action Methodを試してみると、実際には1つのエラーがあることがわかります。問題は実際にはResourceTypesフィールドの変換です:System.StringをAssetManager.Models.ResourceTypeに変換できませんこのモデルでは変換をしたくありません(イベント)ResourceTypeを格納したくないのですが、コントロールのリソースドロップダウンリストのこれで次にどこへ行くべきかについての提案はありますか? – nathj07

+0

私はいくつかのコードを追加しました。 – GvS

+0

コードをありがとう、それは意味があり、私はそれを実装しました。 ResourceTypeの問題(IsValidがfalseになる原因)については、ここにアイディアがありますか?私はこれが何か簡単ではないと確信しています。 – nathj07

0

部分的なビューでブレークポイントを押して、Modelオブジェクトをチェックします。ここでは、部分的な表示に間違ったオブジェクトが見つかります。これがこのエラーの原因です

関連する問題