Matthew Miller
Matthew Miller

Reputation: 605

How to create parent-child relationships implicitly in ASP.NET MVC?

I'm new to working with ASP.NET Core using MVC and starting on my first project. I have two simple model classes. There is a Course class which has a title and description and there is an Article class which has a title, description, content, and a reference to a course that contains the article. My logic for having articles point to their containing course is that it simplifies database queries and makes tracebacks more concise. I'm using ASP.NET Core 2.2 with an MVC framework. I've generated CRUD views by scaffolding the article and course controllers.

Models/Article.cs + Models/Course.cs

public class Article
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Content { get; set; }
    public Course Course { get; set; }
}

public class Course
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
}

In the application, a user should be able to create a course, view the course's articles, and create an article. The user should not have to specify the course that an article belongs to. Essentially, I want to create a link in a course details page that redirects to an article creation page with the course passed in. How should I pass the course ID or course from the course controller to the article controller when the user creates an article? Is this a good way to structure my data?

I've tried a standard link with query parameters but couldn't figure out how to transfer the courseId to the create article POST action.

Views/Courses/Details.cshtml

<a asp-action="Create" asp-controller="Articles" asp-route-courseId="@course.Id">Create New</a>

Controllers/ArticlesController.cs

public async Task<IActionResult> Create(int? courseId, [Bind("Id,Title,Description,Content")] Article article)
{
    if (ModelState.IsValid)
    {
        if (courseId != null)
        {
            article.Course = await _context.Course
                .FirstOrDefaultAsync(m => m.Id == courseId);
        }
        _context.Add(article);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(View), new { id = article.Id });
    }
    return View(article);
}

I know I could create a new view model that has a course ID property instead of a course property but this seems like a poor solution.


Solution

To solve my problem in what I think is an elegant solution, I partially constructed the article object in the controller and passed it into the create view and used the portion of code that Nan Yu recommended below.

public async Task<IActionResult> Create(int? courseId)
{
    Article article = new Article
    {
        Course = await _context.Course
            .FirstOrDefaultAsync(m => m.Id == courseId)
    };
    return View(article);
}

Upvotes: 0

Views: 1201

Answers (1)

Nan Yu
Nan Yu

Reputation: 27538

I know I could create a new view model that has a course ID property instead of a course property but this seems like a poor solution.

No , it's better to use view model which includes properties needed for the view , it helps removing the strong coupling of your entity classes to the UI layer .

Without changing your current codes to use view model , you can include a hidden field to help submitting the courseId:

<form asp-controller="Articles" asp-action="Create" method="post" >
    <div >


        ....
        <input type="hidden" name="courseId" value="@course.Id">


        .....

        <input id=" " type="submit" value="submit"  />

    </div>
</form>

And the server side keep same as your current codes.

Upvotes: 1

Related Questions