Calvin Smith Calvin Smith - 1 month ago 8
C# Question

Share a single Razor Create View with a set of Models that have the property in a scalable way

In MVC C# Web Applications with Razor, I constantly end up wanting to reuse View code for Create actions.

Imagine this scenario:



public class Taco
{
public Lunch Lunch { get; set; }
public Supper Supper { get; set; }
public string Cheese { get; set; }
}

public class Lunch
{
public IEnumerable<Taco> Taco { get; set; }
}

public class Supper
{
public IEnumerable<Taco> Taco { get; set; }
}


You have Lunch and Supper that have Tacos.

Now take these two use cases:


  • From Supper's Details View


    1. Want to add a Taco

    2. Click 'Create New Taco'

    3. Enter Taco information

    4. Click 'Create' Button

    5. Redirected to Supper Details with new Taco there


  • From Lunch's Details View


    1. Want to add a Taco

    2. Click 'Create New Taco'

    3. Enter Taco information

    4. Click 'Create' Button

    5. Redirected to Lunch Details with new Taco there




What is a scalable and MVC-Correct way to do this?



I have always felt like my process for this is hacked together and not very scalable. I might do something like:

Supper View:



@Url.Action("Create", "Taco", new { From = "Supper" })


Lunch View:



@Url.Action("Create", "Taco", new { From = "Lunch" })


Then take the "From" variable and pass it to

Taco Controller>Taco View Model>Taco View>Link Back To From

Is there a built in way to pass referrer information and is there a pre-defined design template for MVC to handle these situations?


Answer

Just literally reuse everything. For example, you can have just one action with one view and use the URL to determine behavior. All you need is a view model so the form can work with just one class type, and then you can map the data onto wherever it should go. For example:

[HttpPost]
[ValidateAntiForgeryToken]
[Route("{mealType:regex(supper|lunch)}/create")]
public ActionResult CreateMeal(string mealType, MealViewModel model)
{
    if (ModelState.IsValid)
    {
        switch (mealType)
        {
            case "supper":
                // map data to new `Supper` and save
                break;
            case "lunch":
                // map data to new `Lunch` and save
                break;
        }

        // do redirect
    }

    return View(model);
}

There's other ways to handle this without using Attribute Routing, but the general idea is that in some form or fashion you indicate which type of meal is being saved, and branch accordingly, creating and saving the appropriate class.

Comments