I'm creating a commenting system for my ASP.NET MVC blog engine using a view form that triggers a basic controller action method:
FORM:
@if (User.Identity.IsAuthenticated)
{
//using (Html.BeginForm())
// {
<div class="new_comment">
<h6 id="shortcodes" class="page-header"><i class="fa fa-file-text-o"></i> Leave a comment</h6>
<div class="hline"></div>
<form class="form-horizontal" action="@Url.Action("CreateComment")" method="post" role="form">
<div class="form-group">
<div class="col-sm-4 col-md-4">
<textarea rows="7" class="form-control" name="Message" placeholder="Your Comment Here..."></textarea>
@Html.AntiForgeryToken()
<input type="hidden" name="Slug" value="@Model.Slug"/>
<input type="hidden" name="PostId" value="@Model.Id"/>
<br/>
</div>
</div>
<div class="form-group">
<input type="submit" value="Post Comment" class="btn btn-primary" style="margin-left: 12px"/>
</div>
</form>
</div>
//}
}
CONTROLLER:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateComment([Bind(Include = "PostId,Message,Username,DatePosted")]Comment comment)
{
var post = db.BlogPosts.Find(comment.PostId);
if (post == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
if (ModelState.IsValid)
{
comment.Username = User.Identity.GetUserId();
comment.DatePosted = DateTimeOffset.Now;
db.Comments.Add(comment);
db.SaveChanges();
}
return RedirectToAction("BlogPostDetails", new { slug = post.Slug});
}
I've set breakpoints aside each of the expressions contained within the if statement and confirmed that none of the data values being passed ("PostId, Message, Username, DatePosted")
are null, and that db.SaveChances()
is commiting changes to the database. Next, here is Models.BlogPosts
...
MODELS:
public class BlogPosts
{
public BlogPosts()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public DateTimeOffset Created { get; set; }
public DateTimeOffset? Updated { get; set; }
[AllowHtml]
[Required]
public string Title { get; set; }
public string Slug { get; set; }
[Required]
public string Category { get; set; }
[AllowHtml]
[Required]
public string Body { get; set; }
public string MediaURL { get; set; }
public bool Published { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public int PostId { get; set; }
public string Username { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Message { get; set; }
public DateTimeOffset DatePosted { get; set; }
public Nullable<System.DateTimeOffset> Edited { get; set; }
public virtual BlogPosts BlogPost { get; set; }
public virtual ApplicationUser Author { get; set; }
//public int? ParentId { get; set; }
//[ForeignKey("ParentId")]
//public virtual ICollection<Comment> Children { get; set; }
//public string ParentComment { get; internal set; }
}
And here is the view that fails to execute:
VIEW THAT DOES NOT EXECUTE
@foreach (var item in Model.Comments.OrderByDescending(c => c.DatePosted))
{
<div class="comment">
<p>
@if (item.Username == null)
{
<small>By: Anonymous</small><span>|</span>
}
else
{
<small>By: @Html.DisplayFor(modelItem => item.Username)</small><span>|</span>
}
<small>Date: @Html.DisplayFor(modelItem => item.DatePosted)</small>
@if (item.Edited != null)
{
<span>|</span><small>Updated: @Html.DisplayFor(modelItem => item.Edited)</small>
}
</p>
<div>
@Html.DisplayFor(modelItem => item.Message)
</div>
</div>
if (item.Username == User.Identity.GetUserId() || User.IsInRole("Admin") || User.IsInRole("Moderator"))
{
<div>
@Html.ActionLink("Edit", "_EditComment", new { id = item.Id }) <span>|</span>
@Html.ActionLink("Delete", "_DeleteComment", new { id = item.Id })
</div>
}
<br />
<!--<div class="hline"></div>-->
}
<div>
<input type="button" class="btn btn-primary" value="Return to Blog Roll" onclick="location.href = '@Url.Action("BlogIndex")'">
</div>
<br />
@if (User.Identity.IsAuthenticated || User.IsInRole("Admin") || User.IsInRole("Moderator"))
{
<input type="button" class="btn btn-primary" value="Modify Post" onclick="location.href = '@Url.Action("BlogAdmin")'">
<br />
<br />
}
When setting a breakpoint on the first line in the view above: @foreach (var item in Model.Comments.OrderByDescending(c => c.DatePosted))
, the reference to public virtual ICollection<Comment> Comments
within the Models.BlogPosts
class remains null (which obviously means the logic in my view fails to execute and no comment is posted).
I am new to ASP.NET MVC, EF Code-First, etc., and clearly do not understand how my controller is failing to pass the comment values in the child model to the public virtual ICollection<Comment> Comments
in the parent... How is it that Models.Comment
as referenced in my CommentCreate
controller contains a value, and the very same virtual reference in my Models.BlogPosts
does not?
EDIT: After great feedback from several users on both the cosmetic errors and critical errors in my code, as well as helpful ASP.NET MVC resource pointers, I determined that the null references being passed had to do with incorrect property-naming conventions in my domain models. See answer below.
Youre including PostId however the actual property name is Id. Also you need to show what model your view is receiving. If youre concerened with what youre exposing (eg Id, why dont you just mark it as a hidden field?).
In the controller pass in nothing but the model that you want to edit, eg keep a controller solely for url routing, keep your model solely for your object and your view should be only for the model youre passing in.
Finally figured out that the BlogPost
property in public virtual BlogPosts BlogPost
in my Models.Comment
domain model needs to be renamed to match the domain model's foreign key: public int PostId
. The solution was executed as follows:
Post
, PostId
values from the Comment
table in my database, update-database -f
command in NuGet.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.