Marco Marco - 12 days ago 5
ASP.NET (C#) Question

Why does the PartialView keep calling itself?

I've tried to set up a small demo, in which an article has multiple comments. The article details view, should render the comments in a partial view. The partialView itself contains another partial view for adding a new comment.

When I try to add another comment I receive a

InsufficientExecutionStackException
, because the action in the controller keeps calling itself. Why does this happen?

(If somebody has the course material at hand. A similar example should be at module 9 in the 70-486 course from Msft; that's what I trying to build.)

Edit: The full code is on github

Controller:

[ChildActionOnly]
public PartialViewResult _GetCommentsForArticle(int articleId)
{
ViewBag.ArticleId = articleId;
var comments = db.Comments.Where(x => x.Article.ArticleId == articleId).ToList();
return PartialView("_GetCommentsForArticle", comments);
}


public PartialViewResult _CreateCommentForArticle(int articleId)
{
ViewBag.ArticleId = articleId;
return PartialView("_CreateCommentForArticle");
}

[HttpPost]
public PartialViewResult _CreateCommentForArticle(Comment comment, int articleId)
{
ViewBag.ArticleId = articleId;
comment.Created = DateTime.Now;
if (ModelState.IsValid)
{
db.Comments.Add(comment);
db.SaveChanges();
}
var comments = db.Comments.Where(x => x.Article.ArticleId == articleId).ToList();
return PartialView("_GetCommentsForArticle", comments);
}


relevant line in the Details.cshtml for Article:

@Html.Action("_GetCommentsForArticle", "Comments", new { articleId = Model.ArticleId})


_GetCommentsForArticle:

@model IEnumerable<Mod9_Ajax.Models.Comment>
<div id="all-comments">
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.Text)
</th>
</tr>

@foreach (var item in Model)
{
@* ... *@
}
</table>
</div>
@Html.Action("_CreateCommentForArticle", "Comments", new { articleId = ViewBag.ArticleId })


_CreateCommentForArticle:

@model Mod9_Ajax.Models.Comment
@using (Ajax.BeginForm("_CreateCommentForArticle", "Comments", new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "all-comments"
}))
{
@* ... *@

<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}

Answer

To explain what is happening, you have a form that posts to you _CreateCommentForArticle() method, which then renders your _GetCommentsForArticle.cshtml partial which in turn includes @Html.Action("_CreateCommentForArticle", ...).

In the initial GET method for Details() the view will be rendered correctly, but when you submit the form, the current request for the _GetCommentsForArticle page is a [HttpPost] method, so @Html.Action() will look for a [HttpPost] method (not the [HttpGet] method). That [HttpPost] in turn renders the _GetCommentsForArticle.cshtml partial and again calls the _CreateCommentForArticle() POST method which renders the _GetCommentsForArticle.cshtml partial and so on until you run out of memory and the exception is thrown.

You can solve this by changing the name of the POST method, for example

[HttpPost]
public PartialViewResult Create(Comment comment, int articleId)

and modify the form to suit

@using (Ajax.BeginForm("Create", "Comments", new AjaxOptions { ...
Comments