How Viewmodel works on Asp.net mvc

Asked

Viewed 2,267 times

8

I have a similar situation that question where I need to save several models at once and relate each. I would need to sort of cascade to first save one entity and then go saving the others, because of the relationships.

Following the question, I created a Viewmodel with all tables that would be saved at once, as in the code below:

public class AnamineseViewModel
{
    //
    //porque dessa forma ficaria mais fácil de salvar tudo relacionado a anaminese
    #region Mapeamento dos objetos

    public CliCliente CliCliente { get; set; }
    public Tabela2 Tabela2 { get; set; }
    public Tabela3 Tabela3 { get; set; }
    public Tabela4 Tabela4 { get; set; }
    public Tabela5 Tabela5 { get; set; }

    #endregion
}

My question is, how can I create a controller and views for that Viewmodel?

Remembering that first I must register the client, after I should be able to relate to tabela2, after saving the tabela2 relate it to the rest and be able to save everything in the bank and edit and list and everything else...

Would have as?

EDIT

The relationships I have are:

  • Table2 with Clicliente, Table3, Table4 and Table5;

  • Table 3 with Table 2

  • Table 4 with Table 2

  • Table 5 with Table 2

All of them being 1:1.

  • Could you add your real templates? Sometimes what you’re looking for doesn’t even need to be by Viewmodel.

  • @Randrade but would have how to do without being by Viewmodel? Are 5 entities...

  • 2

    If they are related entities (preferably 1:1), you do not need Viewmodel, but rather their relationship.

  • Even if I need to save everything at once? @Randrade

  • 1

    Yes. For example: If you are saving a Client, where this customer has a Addressee, one User, etc.. You are saving a Client, and from it you get all these values. Generally, it is used ViewModel only when your Model does not meet all needs.

  • 1

    If you notice, in the question you linked (it is my authorship, and was ashamed of it, but seems to be helping more people) not all Models have direct relationship, so a ViewModel is the best option.

  • Don’t be ashamed of her no @Randrade.. She’s the one who helped me with my solution! kkkk. But I get the idea, thank you very much!

  • 1

    I said "had" because I didn’t know she had helped anyone else. p

Show 3 more comments

2 answers

5


Make a Controller empty. My suggestion:

public class AnaminasesController : Controller
{
    protected SeuContexto contexto = new SeuContexto();

    public ActionResult Index()
    {
        /* Liste aqui o que pode ser interessante entre todos os Models envolvidos */
    }

    public ActionResult Create()
    {
        return View(new AnamineseViewModel 
        {
            CliCliente = new CliCliente(),
            Tabela2 = new Tabela2(),
            Tabela3 = new Tabela3(),
            Tabela4 = new Tabela4(),
            Tabela5 = new Tabela5(),
        });
    }

    [HttpPost]
    public ActionResult Create(AnamineseViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            /* Salve todas as informações aqui */

            return RedirectToAction("Index");
        }

        return View(viewModel);
    }

    /* Os demais métodos seguem a mesma lógica. */
}

As in the question mentioned, generate Partials for each entity to be created or edited. My suggestion for Create:

@model SeuProjeto.ViewModel.AnaminaseViewModel

@using Html.BeginForm() 
{
    @Html.Partial("_Cliente", Model.CliCliente)
    @Html.Partial("_Tabela2", Model.Tabela2)
    @Html.Partial("_Tabela3", Model.Tabela3)
    @Html.Partial("_Tabela4", Model.Tabela4)
    @Html.Partial("_Tabela5", Model.Tabela5)
}

To leave this creation of Partials more dynamic, make Scaffold of all entities and take advantage of their code.


As you complemented the answer, I need to make some addendums.

  • If CliCliente relates to Tabela2 and Tabela3, Tabela4 and Tabela5 also, the relationship between CliCliente and Tabela2 can be discarded. Note that I said "can", not that "should";
  • How @Randrade scored, if you have the chain of relationships, you don’t need to use the Viewmodel, because:

1. His Action Edit already carries everything for you

That is to say:

public ActionResult Edit(int id)
{
    var cliente = db.CliClientes
                    .Include(c => c.Tabela2)
                    .Include(c => c.Tabela3)
                    .Include(c => c.Tabela4)
                    .Include(c => c.Tabela5)
                    .FirstOrDefault(c => c.CliClienteId == id);

    return View();
}

2. His Action Create can use a CliCliente empty instead of a Viewmodel

public ActionResult Create()
{
    return View(new CliCliente 
    {
        Cliente2 = new Cliente2(),
        Cliente3 = new Cliente3(),
        Cliente4 = new Cliente4(),
        Cliente5 = new Cliente5(),
    });
}

3. The Actions of POST can directly receive your Model

[HttpPost]
public ActionResult Create(CliCliente cliente) { ... }

But be careful with that. Try to use the attribute [Bind] to limit the fields Binder will accept.

  • Great answer Gypsy! I have a question... How could I do to show what I wanted in the Index? Because it gets a little complicated to show by the fact of having many models... Would you have how to put in your answer? And another, how could I save that information?

  • For me to do this, I need to understand the relationships between the entities. To save the information, you must have an entity that is related to all the others. Simply add this entity using a context.Entidades.Add(entidade); that the RU does the rest.

  • Do you want me to put my models in the text of my question? It may get too big the question... But if you want I put... Or I could put it like relationships are, it can be?

  • put relationships!

  • @Érikthiago I will complement the answer. Check to see if there will be no doubts.

  • I understood Gypsy! But I think with Viewmodel would be easier to understand and give maintenance... At least for me... Because what happens is that Table 2 relates to all the others... It became easier for me to create a model view and...

  • Well, there it is with you. The goal is to show the possibilities. Good development ;)

  • Dude, I made a mistake when I was registering everything. = (. I’m gonna ask you another question, I wonder if you could help me?

  • Yes. Be more specific on this next.

Show 5 more comments

4

To create the controller and the view corresponding to a particular Viewmodel do:

Viewmodel

namespace WebApplication1.Models
{
    public class ExampleViewModel
    {
        public Produto Produto { get; set; }
        public Cliente Cliente { get; set; }
    }

    public class Produto
    {
        public int Id { get; set; }
        public string Descricao { get; set; }
    }

    public class Cliente
    {
        public int Id { get; set; }
        public string Nome { get; set; }
    }
}

In that Viewmodel (ExampleViewModel) i have two aggregations of classes Produto and Cliente and with them I will create view Leading (Create) and their respective views for each existing aggregation.

How would it be?

public class ExamplesController : Controller
{
    [HttpGet()]
    public ActionResult Create()
    {
        return View();
    }

    [HttpPost()]
    public ActionResult Create(ExampleViewModel exampleViewModel)
    {
        //ROTINAS DE GRAVAÇÃO
        return View();
    }
}

In the controller it’s simple to create a create with verb GET and another with the verb POST with a parameter of your ViewModel (ExampleViewModel).

Create your view in this format:

View Product: (_Product.cshtml)

@model WebApplication1.Models.ExampleViewModel
<div class="form-horizontal">
    <h4>Produto</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.Produto.Id, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Produto.Id, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Produto.Id, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(model => model.Produto.Descricao, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Produto.Descricao, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Produto.Descricao, "", new { @class = "text-danger" })
        </div>
    </div>
</div>

View Client: (_Client.cshtml)

@model WebApplication1.Models.ExampleViewModel
<div class="form-horizontal">
    <h4>Cliente</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.Cliente.Id, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Cliente.Id, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Cliente.Id, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(model => model.Cliente.Nome, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Cliente.Nome, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Cliente.Nome, "", new { @class = "text-danger" })
        </div>
    </div>
</div>

Notice that these two views which are complementary to their view Main has an interesting factor that I put your type in the @model your Viewmodel ExampleViewModel, because?

So that the binding(the information passed in the fields is loaded into your class in a simple and transparent manner) works properly, that is, so that when sending the information to Create (do verb POST) it loads the class correctly. Look at the nomenclature of an item:

@Html.EditorFor(model => model.Cliente.Id, new { htmlAttributes = new { @class = "form-control" } })

It was mounted upon its aggregation demonstrated in model.Cliente.Id, for him to give the binding correctly of the information.

Main View: (Create.cshtml)

@model WebApplication1.Models.ExampleViewModel
@{ Layout = null; }
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Create</title>
</head>
<body>
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryval")    

    @using (Html.BeginForm()) 
    {
        @Html.AntiForgeryToken()


        @Html.Partial("_Cliente", Model)
        <hr />
        @Html.Partial("_Produto", Model)

        <div class="form-horizontal">
            <h4>ExampleViewModel</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
    }
</body>
</html>

Generated html

<form action="/Examples/Create" method="post"><input name="__RequestVerificationToken" type="hidden" value="Mr207UcvrXamhyWWVqGbKBVZ8wY9ccJoBqcGVCjwg3G_tjHWIymMjfzE5o1XkXaJ8Q0WsL5XWMhq3biQfh7rKmuerIMuMPCgPFEOzA7baNc1" /><div class="form-horizontal">
    <h4>Cliente</h4>
    <hr />

    <div class="form-group">
        <label class="control-label col-md-2" for="Cliente_Id">Id</label>
        <div class="col-md-10">
            <input class="form-control text-box single-line" data-val="true" data-val-number="The field Id must be a number." data-val-required="O campo Id é obrigatório." id="Cliente_Id" name="Cliente.Id" type="number" value="" />
            <span class="field-validation-valid text-danger" data-valmsg-for="Cliente.Id" data-valmsg-replace="true"></span>
        </div>
    </div>
    <div class="form-group">
        <label class="control-label col-md-2" for="Cliente_Nome">Nome</label>
        <div class="col-md-10">
            <input class="form-control text-box single-line" id="Cliente_Nome" name="Cliente.Nome" type="text" value="" />
            <span class="field-validation-valid text-danger" data-valmsg-for="Cliente.Nome" data-valmsg-replace="true"></span>
        </div>
    </div>
</div>        <hr />
<div class="form-horizontal">
    <h4>Produto</h4>
    <hr />

    <div class="form-group">
        <label class="control-label col-md-2" for="Produto_Id">Id</label>
        <div class="col-md-10">
            <input class="form-control text-box single-line" data-val="true" data-val-number="The field Id must be a number." data-val-required="O campo Id é obrigatório." id="Produto_Id" name="Produto.Id" type="number" value="" />
            <span class="field-validation-valid text-danger" data-valmsg-for="Produto.Id" data-valmsg-replace="true"></span>
        </div>
    </div>
    <div class="form-group">
        <label class="control-label col-md-2" for="Produto_Descricao">Descricao</label>
        <div class="col-md-10">
            <input class="form-control text-box single-line" id="Produto_Descricao" name="Produto.Descricao" type="text" value="" />
            <span class="field-validation-valid text-danger" data-valmsg-for="Produto.Descricao" data-valmsg-replace="true"></span>
        </div>
    </div>
</div>        <div class="form-horizontal">
            <h4>ExampleViewModel</h4>
            <hr />

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
</form>

In this generation it is easy to see why class aggregations ExampleViewModel are loaded, the fields for example client name in your tag input of the kind text has the name Cliente.Nome. Client is the classe and the Name of property, that’s how the MVC can define the information and click on the corresponding classes.

If you ride different it won’t work !!!

  • Is it necessary to assemble models even if everything is in one model? Because the way I did, I made each model in a different class and another class, which is the one shown above in the question, putting together all... It needs to be that way?

  • The models are unique and alone, while Viewmodel has these models. That’s what you said, I believe you’re making some mess read it calmly and ask me again!

  • 1

    Only one correction: "parse" in ASP.NET MVC is known as Binding. Parse is something else. The rest is ok.

  • Yes it was a lapse on my part I really also know the difference. Thanks @gypsy

  • 1

    Man, it was great! Your answer and the Gypsy complement, for me right? Very good! Thanks! Now I get the idea!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.