简体   繁体   中英

How to get ID from parent object inside child object when creating it? (MVC)

Overview

I'm using ASP.NET Core with MVC.

I am trying to make a simple helpdesk system as an experiment.

It has 2 objects, an Inquiry and a Comment .

  • An Inquiry has a list of comments.

  • A comment has the ID of the Inquiry it belongs to.

My question is

How can I get the ID of the Inquiry (which is a parent of the comment being created) to be added to a property in the comment?


Code examples

CommentsController.Create

I have tried the 2 POST examples, both seem to capture the form data from the comment but I don't know how to also bring in the Inquiry ID in that form data. When I am creating a child comment. The Inquiry ID was added to the model I sent to the Create view but lost when submitting the form in that view. The GET example works as intended.

    // GET: Comments/Create
    public IActionResult Create([FromRoute] int id) //id = InquiryID

    // POST: Comments/Create
    public async Task<IActionResult> Create(CommentCreationDTO commentCreationDTO)

    public async Task<IActionResult> Create([FromForm]CommentCreationDTO commentCreationDTO)

Views/Comments/Create.cshtml

@model HelpDesk2018.Models.CommentCreationDTO
@{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Comment</h4>
<hr/>
<div class="row">
<div class="col-md-4">
    <form asp-controller="Comments" asp-action="Create" method="post">
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="@Model.Comment.TimePosted" class="control-label"></label>
            <input asp-for="@Model.Comment.TimePosted" class="form-control" />
            <span asp-validation-for="@Model.Comment.TimePosted" class="text-danger"></span>
        </div>
        <div class="form-group">
            <label asp-for="@Model.Comment.Text" class="control-label"></label>
            <input asp-for="@Model.Comment.Text" class="form-control" />
            <span asp-validation-for="@Model.Comment.Text" class="text-danger"></span>
        </div>
    </form>
</div>


Elaboration of the current flow in the MVC program for better understanding

To add comments to the Inquiry I have made the following setup.

  1. A button on the Inquiry detail view triggers an action on the InquiryController named "CreateComment". This redirects to the Comments controller with the ID of the inquiry.
  2. The CommentsController received the redirect plus the attached InquiryID. It then sends back the Create view for creating comments, in the view a CommentCreation object is sent in as model. This holds the ID of the parent Inquiry as well as a new/empty comment.
  3. On the comment creation view I enter the comment information and submit.
  4. The form info submitted is now "caught" by the Create and I can see the information of the comment. However, and this is the issue , the ID for the parent Inquiry was lost in the process and now is set to the default value of 0 (as it's an integer).

Models

/// <summary>
/// Represents one inquiry on the helpdesk.
/// Category, Country and RelatedProduct should perhaps be objects instead of strings.
/// </summary>
public class Inquiry : TextPost
{
    public string Title { get; set; }
    public bool Solved { get; set; }
    public bool Private { get; set; }
    public List<Comment> Comments { get; set; } = new List<Comment>();
    public string Category { get; set; }

    #region optional properties
    /// <summary>
    /// Which country this inquiry is most relevant for. E.g. Denmark, Sweden, Norway etc., but can also be "All" or the name of a specific market.
    /// </summary>
    public string Country { get; set; }
    /// <summary>
    /// In case the inquiry is related to one or more products. Should perhaps be an object in it's own right instead of a string as it is now.
    /// </summary>
    public string RelatedProduct { get; set; }
    #endregion
}

public class Comment : TextPost
{
    public int InquiryID { get; set; }
}

public class TextPost
{
    [Key]
    public int ID { get; set; }
    public User User { get; set; }
    public DateTime TimePosted { get; set; }
    /// <summary>
    /// The main text.
    /// </summary>
    public string Text { get; set; }
}

public class CommentCreationDTO
{
    public int IDOfInquiryBelongingTo { get; set; }
    /// <summary>
    /// The comment that is being created.
    /// </summary>
    public Comment Comment { get; set; }
}

You need to include a route/query string value in the <form> or include a hidden input for IDOfInquiryBelongingTo , so that its value is sent in the request and will be bound to your model.

However, view models should not contain data models when editing data (your Comment property), and TimePosted appears to be a property that should not be editable by the user. I recommend you first modify the view model to

public class CommentVM
{
    public int? ID { get; set; } // if you also want to use this for editing existing comments
    [Required]
    public int? InquiryID { get; set; }
    [Required(ErrorMessage = "Please enter a comment")]
    public string Text { get; set; }
}

Your GET method will now return an instance of CommentVM to the view

public IActionResult Create([FromRoute] int id) //id = InquiryID
{
    CommentVM model = new CommentVM
    {
        InquiryID = id
    }
    return View(model);
}

View

@model ....CommentVM
<form asp-controller="Comments" asp-action="Create">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="InquiryID" /> // add hidden input
    <div class="form-group">
        <label asp-for="Text" class="control-label"></label>
        <input asp-for="Text" class="form-control" />
        <span asp-validation-for="Text" class="text-danger"></span>
    </div>
</form>

And in the POST method, map the view model to the data model

public async Task<IActionResult> Create([FromForm]CommentVM model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    Comment comment = new Comment
    {
        InquiryID = model.InquiryID,
        Text = model.Text,
        TimePosted = DateTime.Now,
        .... // set other properties (User etc) as required
    };
    // save and redirect
    ....
}

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