Reputation: 2892
I have some razor code for an MVC3 site that won't render. I'm getting a Cannot perform runtime binding on a null reference
runtime error for some reason. Here is my view:
@{
ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
<table>
@for (var i = 0; i < ViewBag.taskList.Count; i++)
{
<tr>
@for (var j = 0; j < 9; j++)
{
<td>@ViewBag.taskList.get(i).attr.get(j);</td>
}
</tr>
}
</table>
Here is my model:
public class parentTask
{
int id { get; set; }//task ID
int pId { get; set; }//parent task ID
String name { get; set; }//task name
String dDate { get; set; }//due date
String cDate { get; set; }//completion date
String description { get; set; }//Description of task
String assignedTo { get; set; }//assigned to
String tags { get; set; }//tags
public List<object> attr;//list for rendering attributes
public parentTask(int i, int p, String n, String d, String c, String de, String a, String t)//constructor
{
id = i;
pId = p;
name = n;
dDate = d;
cDate = c;
description = de;
assignedTo = a;
tags = t;
attr= new List<object>();
attr.Add(id);
attr.Add(pId);
attr.Add(name);
attr.Add(dDate);
attr.Add(cDate);
attr.Add(description);
attr.Add(assignedTo);
attr.Add(tags);
}
}
and here is my controller:
public class TaskController : Controller
{
String[] values = new fileReader().values;
public List<parentTask> taskList = new List<parentTask>();
public ViewResult index(){
String tId=values[0];
String tPId=values[1];
String tName=values[2];
String tD=values[3];
String tC=values[4];
String tDe=values[5];
String tA=values[6];
String tT=values[7];
for(int i=8; i< values.Length; i+=8){
taskList.Add(new parentTask(Convert.ToInt32(values[i]), Convert.ToInt32(values[i + 1]), values[i + 2], values[i + 3], values[i + 4], values[i + 5], values[i + 6], values[i + 7]));
}
ViewBag.taskList = taskList;
return View();
}
#region Status Codes
private static string ErrorCodeToString(MembershipCreateStatus createStatus)
{
// See http://go.microsoft.com/fwlink/?LinkID=177550 for
// a full list of status codes.
switch (createStatus)
{
case MembershipCreateStatus.DuplicateUserName:
return "User name already exists. Please enter a different user name.";
case MembershipCreateStatus.DuplicateEmail:
return "A user name for that e-mail address already exists. Please enter a different e-mail address.";
case MembershipCreateStatus.InvalidPassword:
return "The password provided is invalid. Please enter a valid password value.";
case MembershipCreateStatus.InvalidEmail:
return "The e-mail address provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidAnswer:
return "The password retrieval answer provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidQuestion:
return "The password retrieval question provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidUserName:
return "The user name provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.ProviderError:
return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
case MembershipCreateStatus.UserRejected:
return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
default:
return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
}
}
#endregion
}
When I run the program, it brings me to the view when the error is found, however I'm unsure where the problem lies so I figured I'd include the other code to help, which if anyone can help me, it'd be greatly appreciated.
Upvotes: 0
Views: 144
Reputation: 93611
There are a number of issues with the code, so easiest to supply a complete example. I have gone with industry standard naming conventions for properties, variables and parameters.
Always aim to keep views as simple as possible as they are the least readable part of MVC. Use a strongly typed model in preference to ViewBag variables.
As the data is contained in lists, use foreach
, rather than indexing them (both the tasks and the attributes in this case).
@model IEnumerable<TaskManager.Models.ParentTask>
@{
ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
<table>
@foreach (var parentTask in Model)
{
<tr>
@foreach (var attr in parentTask.Attr)
{
<td>@attr</td>
}
</tr>
}
</table>
Always go for readable variable/property/parameter names ahead of short names. Use of case allows you to reuse the names in a meaningful way as properties and matching parameters.
public class ParentTask
{
int TaskId { get; set; }
int ParentTaskId { get; set; }
String TaskName { get; set; }
String DueDate { get; set; }
String CompletionDate { get; set; }
String Description { get; set; }
String AssignedTo { get; set; }
String Tags { get; set; }
public List<object> Attr;
public ParentTask(int taskId, int parentTaskId, String taskName, String dueDate, String completionDate, String description, String assignedTo, String tags)//constructor
{
TaskId = taskId;
ParentTaskId = parentTaskId;
TaskName = taskName;
DueDate = dueDate;
CompletionDate = completionDate;
Description = description;
AssignedTo = assignedTo;
Tags = tags;
Attr = new List<object>();
Attr.Add(TaskId);
Attr.Add(ParentTaskId);
Attr.Add(TaskName);
Attr.Add(DueDate);
Attr.Add(CompletionDate);
Attr.Add(Description);
Attr.Add(AssignedTo);
Attr.Add(Tags);
}
}
You cannot guarantee the controller's constructor will be called once per page as they are reused, so you needed to clear your list of tasks each time.
public class TaskController : Controller
{
String[] values = new fileReader().values;
public List<ParentTask> TaskList = new List<ParentTask>();
public ViewResult Index()
{
// These were not used
//string tId = values[0];
//string tPId = values[1];
//string tName = values[2];
//string tD = values[3];
//string tC = values[4];
//string tDe = values[5];
//string tA = values[6];
//string tT = values[7];
// The constructor may not be called on each page load, so clear the list
TaskList.Clear();
for (int i = 8; i < values.Length; i += 8)
{
TaskList.Add(new ParentTask(Convert.ToInt32(values[i]), Convert.ToInt32(values[i + 1]), values[i + 2], values[i + 3], values[i + 4], values[i + 5], values[i + 6], values[i + 7]));
}
// Return a strongly typed view with our list of tasks as the ViewModel
return View(TaskList);
}
Upvotes: 1
Reputation: 568
Instead of using Viewbag to contains the parent task list return the view with the list itself.
In your controller:
return View(taskList)
Now in your view:
@model List<parentTask>
<h2>@ViewBag.Message</h2>
<table>
@for (var i = 0; i < Model.Count; i++)
{
<tr>
@for (var j = 0; j < 9; j++)
{
<td>@Model[i].attr[j];</td>
}
</tr>
}
Assuming that your class and other methods are error free.
Upvotes: 1