简体   繁体   中英

How do I pass this child model reference to a virtual ICollection in parent domain model in ASP.NET MVC?

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:

  1. Changing the property name to Post ,
  2. then manually deleting the null PostId values from the Comment table in my database,
  3. and then running 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM