Reputation: 81
I'm working on asp.net MVC 5 project.I have two models Post and Comment having one to many relationship between them. In Post's Details view, I want to add comments in that Post. How can I do it without using any ViewModel? Only Post model is passed in the view. I want to access Comment and its properties by using Post model reference, after all they have one many relationship between them, Post model have Comment's reference.
Here is my code example Details actions in Post's controller
[HttpPost]
public ActionResult Details(Comment comment, string post_id)
{
int id = int.Parse(post_id);
if (ModelState.IsValid)
{
var post = db.Posts.FirstOrDefault(u => u.id == id);
comment.post = post;
db.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(comment);
}
//
// GET: /Post/Details/5
public ActionResult Details(int id = 0)
{
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
return View(post);
}
here is Details.schtml
@model Blog.Models.Post
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<fieldset>
<legend>Post</legend>
<div class="display-label">
@Html.DisplayNameFor(model => model.title)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.title)
</div>
<div class="display-label">
@Html.DisplayNameFor(model => model.body)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.body)
</div>
@for (int i = 0; i <Model.comments.Count; i++)
{
<div class="display-label">
@Html.DisplayNameFor(model => model.comments[i].comment)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.comments[i].comment)
</div>
}
<h2>Comment</h2>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Article</legend>
<div class="editor-label">
@Html.LabelFor(model =>model.comments[Model.comments.Count].comment)
</div>
<div class="editor-field">
@Html.EditorFor(model =>model.comments[Model.comments.Count].comment))
@Html.ValidationMessageFor(model => model.comments[Model.comments.Count].comment)
</div>
<p>
<input type="submit" value="Add" />
</p>
</fieldset>
}
</fieldset>
There is an error
"Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index"
how can I successfully add comments ?
Upvotes: 1
Views: 5749
Reputation:
Your exception is generated by the following line in your view
@Html.LabelFor(model =>model.comments[Model.comments.Count].comment)
and will also be generated by the subsequent EditorFor()
and ValidationMessageFor()
methods.
Collection indexers are zero based, whereas .Count()
returns the number of items in the collection. If say you collection contained 2 Comment
, then you would access them as
model.Comments[0].comment
model.Comments[1].comment
but your [Model.comments.Count]
equates to
model.Comments[2].comment
which throws the exception because your referring to the 3rd item in the collection (which does not exist). Even if you referred to an existing item in the collection, you code would fail because you would be posting back a collection (defined by the indexer) but you POST method only excepts a single object
It appears you wanting to add a new comment to the Post
in which case you can either use a view model containing an additional property for Comment
public Comment NewComment { get; set; }
and then in you view, generate the form controls using
@Html.EditorFor(m => m.NewComment.comment)
and change the POST method signature to
public ActionResult Details([Bind(Prefix = "NewComment")]Comment comment, int post_id)
Note the use of the Bind.Prefix
property which is necessary to strip the "NewComment"
prefix from the name/value pair posted to the method. Note also that you can make the second parameter int
and avoid the unnecessary conversion from string
to int
(although you do not appear to be generating a form control for property post_id
so it will be null
anyway).
An alternative would be to use @Html.Action()
to call a child action method that returns a partial view (containing the form) based on a new Comment
.
Upvotes: 1
Reputation: 343
The error itself pretty much explains the problem. On the line where you get the error the Model.comments.Count
is out of the range. The indexation starts from 0 so the last comment should be retrieved this way .... model => model.comments[Model.comments.Count - 1].comment
.
Upvotes: 1
Reputation: 1402
It is because of db.Posts.Find(id);
. you have given initial value for id in public ActionResult Details(int id = 0)
. Check your code to be sure you always pass value to id.
Upvotes: 0