Master-detail in MVC C# with Razor

Asked

Viewed 5,558 times

10

Guys I need to set up a master-detail record. I have already researched and seen some examples on the internet, but as I do for when the master is not registered, the details are stored temporarily to in the general recording save the master record and the detail records.

I thought about doing in Session, but I think this may be a problem. Is there any solution to this?

public class Cliente
{
    [Key]
    public int ClienteID { get; set; }
    public string Nome { get; set; }
    public virtual ICollection<Telefone> Telefones { get; set; }
}

public class Telefone
{
    [Key]
    public int TelefoneID { get; set; }                
    public string Telefone { get; set; }
    public virtual Cliente Cliente { get; set; }
}

The idea is to do something similar to the image below, but without the need for the customer registered in the database. Do everything in one post.

The big question, is how can I "record" the phone data and return to the screen this information without doing refresh...

inserir a descrição da imagem aqui

  • What is a master-detail record?

  • @Tiagocésaroliveira I believe it must be some entity of his system, and detail probably more details. Anyway, it is not clear. This question should be reformulated.

  • Master-Detail, means a parent record with several children. Example, Client (master) and Telephone (detail). I only found examples with the saved master, not saving everything in a single post.

  • @Marlon.Tiedt, can you be clearer in your doubt? If you can isolate it in code, so much the better.

  • If you have the Customer model and the Phone model, I see no difficulty in applying the model... A simple <input type="hidden" /> worthwhile 0 if it is a new client or the ID value of the Cliente if it is an update would solve your problem.

3 answers

9


Since it’s to save everything at once, I’d use one client approach, no postback until such time as everything has to be saved.

You could argue about the possible loss of data in complex registrations, in which the user unintentionally closes the browser, but using HTML 5 resources (if this is an option) you can save the data in localstorage or using a DB client (websql) which prevents such data loss.

Functional example:

View code:

@model MvcApplication2.Controllers.MasterDetailObj
<html>
<head>
    <title>Postar master/detail</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

    <script type="text/javascript">
        $(function () {
            $("#add").on("click", function () {
                var tel = $("#numero").val();
                var ddd = $("#ddd").val();
                var idx = $("#telefones > tbody > tr").length;
                $("#telefones > tbody").append("<tr><td><input type='hidden' name='Telefones[" + idx + "].Numero' value='" + tel + "' />" + tel
                    + "</td><td><input type='hidden' name='Telefones[" + idx + "].Ddd' value='" + ddd + "' />" + ddd + "</td></tr>");
            });
        });
    </script>
</head>
<body>
    <h2>Postar master/detail</h2>

    <div>
        <div>
            <label for="numero">Número</label>
            <input type="text" id="numero" />
        </div>
        <div>
            <label for="ddd">DDD</label>
            <input type="text" id="ddd" />
        </div>
        <button id="add">Add</button>
    </div>

    @using (this.Html.BeginForm())
    {
        <div>
            @Html.LabelFor(x => x.Nome)
            @Html.TextBoxFor(x => x.Nome)
        </div>
        <table id="telefones">
            <thead>
                <tr>
                    <th>Número</th>
                    <th>Ddd</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>

        <button type="submit">submit</button>
    }
</body>
</html>

Controller code:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult MultiDetailSubmit()
    {
        return this.View();
    }

    [HttpPost]
    public ActionResult MultiDetailSubmit(MasterDetailObj obj)
    {
        if (this.ModelState.IsValid)
        {
            // aqui estarão disponíveis todos os itens adicionados na lista de telefones no client

            return this.RedirectToAction("Index", "Home");
        }

        return this.View(obj);
    }

}

Models:

public class MasterDetailObj
{
    public string Nome { get; set; }
    public List<Telefone> Telefones { get; set; }
}

public class Telefone
{
    public string Numero { get; set; }
    public string Ddd { get; set; }
}
  • but how I would store the phone items in the View and then recover in the controller?

  • On the Add button you add a new row in the table, and for each field to be sent to the server you create a <input type='hidden'> with the value to be sent. If you add multiple values, with the same attribute name you can receive them on the server in the form of an array.

  • Yes, I had forgotten this. In my case the name would be "Phones," am I right? and if it is a complex element? Example if we had DDD and Telephone how should proceed with the input element?

  • In this case you can receive an array of complex objects in the action (e. g. class Telefone { public string Telefone {get;set;} public string DDD {get;set;} }), and on the page render inputs with the name using the pattern [0].Telefone, [0].DDD to the phone-0, [1].Telefone and [1].DDD to the phone-1, and so on.

  • Could you give an example of what the view and controller would look like? Once you have the same question as me, you will see the complete answer. Thanks

  • Sure... I was working on it! =)

  • I posted an example of how to do... you will notice that the script generates Telefones[0].Numero rather than simply [0].Numero as I said before... this is due to the fact that the model is not a simple list, but rather master data along with data from the various detais.

  • Note: If you implement deletion of records, you will need to rename the inputs so that the indexes are in order. If you jump from Telefones[0].Numero straight to the Telefones[4].Numeros, then it won’t work. It can’t contain jumps.

  • Miguel your answer was very clear, can you give me a brief explanation, and when it’s time to edit? where to reload this list, as done?

Show 4 more comments

5

This code was written at the time the Entity Framework was in version 5, so possibly the logic of manipulating the phone collection may have changed. Please notify me by comments if any error condition is found for me to fix.

Use the package BeginCollectionItem, available on Nuget:

http://www.nuget.org/packages/BeginCollectionItem/

The following tutorial documents as well as implementing the master-detail:

http://www.joe-stevens.com/2011/07/24/asp-net-mvc-2-client-side-validation-for-dynamic-fields-added-with-ajax/

Would look like this:

_Createoredit.cshtml (Client)

@model SeuProjeto.Models.Cliente

@* Demais campos do seu model *@

@if (Model != null && Model.Telefones != null)
{
    foreach (var telefone in Model.Telefones)
    {
        Html.RenderPartial("_TelefonesEditor", telefone);
    }
}

@* Botões de submit, fechamento de <fieldset>, etc. *@

_Phoneseditor.cshtml

@model SeuProjeto.Models.Telefone

@using (Html.BeginCollectionItem("Telefones"))
{ 
    @Html.HiddenFor(model => model.TelefoneID)
    @Html.HiddenFor(model => model.ClienteID)
    @Html.EditorFor(model => model.Telefone)
}

Customersscontroller.Cs

namespace SeuProjeto.Controllers
{
    public class ClientesController : Controller
    {
        [HttpPost]
        public ActionResult Create(Cliente cliente)
        {
            if (ModelState.IsValid)
            {
                if (shop.Telefones != null)
                {
                    foreach (var telefone in cliente.Telefones)
                    {
                        telefone.ClienteID = cliente.ClienteID;
                        context.Entry(telefone).State = System.Data.Entity.EntityState.Modified;
                        context.SaveChanges();
                    }
                }
            }

            // Lógica adicional, caso Model não seja válido
        }

        [HttpPost]
        public ActionResult Edit(Cliente cliente)
        {
            if (ModelState.IsValid)
            {
                // Telefones Originais
                List<Telefones> telefonesOriginais = context.Telefones.AsNoTracking().Where(t => t.ClienteID == cliente.ClienteID).ToList();

                if (cliente.Telefones != null)
                {
                    // Telefones Excluídos
                    foreach (var telefone in telefonesOriginais)
                    {
                        if (!cliente.Telefones.Where(t => t.TelefoneID == telefone.telefoneID).Any())
                        {
                            var telefoneExcluido = context.Telefones.Single(t => t.TelefoneID == telefone.TelefoneID);
                            context.Telefones.Remove(telefoneExcluido);
                            context.SaveChanges();
                        }
                    }

                    // Telefones Novos ou Editados
                    foreach (var telefone in cliente.Telefones)
                    {
                        if (telefone.ClienteID == 0)
                        {
                            telefone.ClienteID = cliente.ClienteID;
                            context.Telefones.Add(telefone);
                        }
                        else
                        {
                            context.Entry(telefone).State = System.Data.Entity.EntityState.Modified;
                        }

                        context.SaveChanges();
                    }
                }

                context.Entry(cliente).State = System.Data.Entity.EntityState.Modified;
                context.SaveChanges();
            }

            // Lógica adicional, caso Model não seja válido
        }
    }
}
  • This is what I’m looking for. I’ll test it to see if it suits me. You could tell me what the term is for researching the topic in English. I saw that library you passed a while ago that has no update... Thanks for the tip...

  • The term you can use for research is BeginCollectionItem tutorial. This link I gave you is the most complete as a tutorial. There are also questions on Stack Overflow gringo that can help. Now that you have a little code, I can improve the answer. I’ll see if I can do it soon.

  • @See if logic serves you now.

  • @Ciganomorrisonmendez after much trying to understand this plugin kkk I got kkkk, but I got a doubt in there in the method of Edit, where has a comment "deleted phones" the phone condition. Id == phone.Id, is always true, and it removes... Below, the Customer Condition =0 is false, when it will edit, of the error, because it just deleted earlier

  • @Rod True, I was in error. This code I made for EF5. Possibly in EF6 do not need so many checks. It is necessary to test and fix.

  • @Gypsy rhythmandness but what point would you tell me if the phone was deleted? Edit, add, all right, problem I have no way to parameterize if it was deleted

  • @Rod does. You go to the bank and take all the original values, comparing it to what came from the screen. Separates all arches that did not come from the screen and excludes in context.

  • @Gypsy?

  • I think it only slows down if it’s too many records even, type 1000 records, but then I believe it is easier to lock JS than EF.

  • @Ciganomorrisonmendez understood, another thing, I realized, is that the validation msgs in the Client do not work...because to work the ID and Name, must be the same...there is some solution ?

Show 5 more comments

2

I recommend you use the MVVM standard, especially Knockoutjs, it is a great package where brings your model to the view and based on it you can have this implementation of the list. The coolest thing about working with it is that you can choose to transact the final object or part of it.

Below are some extremely useful links for knowledge and see the implementation of Knockoutjs with MVC

MVC-4-Knockout-CRUD

Building with MVVM

Knockoutjs and bootstrap

Introduction to Knockoutjs

And of course I couldn’t miss the link from Knockoutjs himself

Knockoutjs Official

(Available in Nugget tbm [Install-Package Knockoutjs])

Browser other questions tagged

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