Best Practices for Insertion, Modification and Deletion with Entityframework

Asked

Viewed 1,184 times

16

For deletion of records you may not have, but for Insertion and Change I believe there should already be something discussed.

Probably the best practice on Insertion and Amendment is Viewmodel s, where you create a suitable view for each case, you will then have the data in a simple way to make an Insertion, needing only to pass the data of the Viewmodel to the Domain.

I don’t want to encourage or ask for advice on bad practices, but it’s always good details well clarified, and yet there may be many simple cases of Viewmodel s or cases where a Domain is simple as to our case of need for a Viewmodel that we decide not to replicate a class.

Well, an example:

Person:

public class Pessoa
{
    public int Id { get; set; }

    [StringLength(50)]
    [Required(AllowEmptyString = false)]
    public string Nome { get; set; }

    [Required]
    [DataType(DataType.Date)]
    [Column(TypeName = "Date")]
    public DateTime DataNascimento { get; set; }

    [InverseProperty("Pessoa")]
    public virtual ICollection<Telefone> Telefones { get; set; }
}

Telephone:

public class Telefone
{
    public int Id { get; set; }

    [Required]
    public TipoTelefone TipoTelefone { get; set; }

    [StringLength(3)]
    [Required(AllowEmptyString = false)]
    public string Ddd { get; set; }

    [StringLength(10)]
    [Required(AllowEmptyString = false)]
    public string Numero { get; set; }
}

And then we have a View for registering people that allows you to enter phone numbers, and with that we have some cases:

  1. There may be numbers already registered (editing case);
  2. New numbers can be inserted (for insertion or editing);
  3. You can remove some numbers and even add others (editing cases).

Note: I believe I have listed all.

In editing cases can even be simple, just leave the property Telefones of Pessoa fed and then add the database.

But for insertion, a question: The Id person will be passed directly to Telefones and then this simple example of a Pessoa and the Telefones?

[HttpPost]
public ActionResult Save(Pessoa model)
{
    if (ModelState.IsValid)
    {
        dbContext.Pessoas.Add(model);
        dbContext.SaveChanges();
    }
    return View(model);
}

To change: What is recommended to do to then meet the possible conditions presented?

[HttpPost]
public ActionResult Edit(Pessoa model)
{
    if (ModelState.IsValid)
    {
        var entry = dbContext.Pessoas.Find(model.Id);
        if (entry != null)
        {
            dbContext.Entry(Pessoa).CurrentValues.SetValues(model); // ???
            dbContext.SaveChanges();

            // E se:
            // 1. Existir números já cadastrados?
            // 2. Inserir novos números e outros já existiam?
            // 3. Alguns números e adicionar outros?
        }
        else
        {
            ModelState.AddModelError("", "Pessoa não encontrada!");
        }
    }
    return View(model);
}

What are the best practices in these scenarios, which I believe is very generic and explanatory for several cases?

Detail: Even if exemplifying with ASP.NET MVC, responds to other types and projects as well.

1 answer

9


Introducing

First of all, we need understand how the Defaultmodelbinder implementation works, which is not mentioned in any answer on the subject. It assumes that the variables on screen can be simple (primitive: int, float, String, bool...) or complex (one object within another, one collection within another). It is this class that makes the association of the Ids and names of the fields of your form with the objects that are received by Controller.

The Master-Detail, for the DefaultModelBinder, is an object (master) that has within itself a Collection of objects of another type (or, depending, even of the same type as the master). You, including, can implement its own ModelBinder, if you like, but I don’t think it’s necessary, because the DefaltModelBinder is excellent for most situations.

An example in the hand

Let’s do a manual implementation of a master-detail. Suppose an object called Jovem and its collection of Brinquedos:

public class Jovem
{
    [Key]
    public int JovemId { get; set; }

    [Required]
    public String Nome { get; set; }

    pubic virtual ICollection<Brinquedo> Brinquedos { get; set; }
}

public class Brinquedo
{
    [Key]
    public int BrinquedoId { get; set; }
    public int JovemId { get; set; }

    [Required]
    public String Nome { get; set; }

    public virtual Jovem Jovem { get; set; }
}

We want the same form to insert a Jovem and at least one Brinquedo to the Jovem. So the form looks something like this:

I’m using Bootstrap with Font Awesome to illustrate.

@using SistemaDeJovens.Resources
@model SistemaDeJovens.Models.Jovem

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

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

        <!-- Preste atenção nesse atributo -->
        <input type="hidden" name="Brinquedos.index" autocomplete="off" value="0">

        <div class="form-group">
            <label for="Brinquedos[0].Nome" class="control-label col-md-2" />
            <div class="col-md-10">
                <input type="text" name="Brinquedos[0].Nome" id="Brinquedos_0__Nome" class="form-control ckeditor" />
                <span class="field-validation-valid" data-valmsg-for="Brinquedos[0].Nome" data-valmsg-replace="true"></span>
            </div>
        </div>

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

<div>
    @Html.FontAwesomeActionLink("Voltar para Index", "Index", "fa-list", htmlAttributes: new { @class = "btn btn-default" })
</div>

Notice that I put everything fixed, plus an attribute called "index". Why it exists? Steve Sanderson explains that the ModelBinder need it to know what will be associated with the detail object (note that this is nothing new, it is something of MVC2). It does not need to be whole. In my projects, for example, "index" is always a Guid.

And why did I put it all together? Because Helpers Html are unable to generate Html in the format of the indexes that are required for the ModelBinder make the correct interpretation of your form.

In the aforementioned article, Steve Sanderson also explains the difficulty of doing this by hand, and that’s why the Nuget package was created that I insist so much for you to use: Begincollectionitem.

Examples of Begincollectionitemhelper

I have already addressed the subject in these replies:

Questions


The Person Id will be passed directly to Phones and then this simple example of insertion registers the Person and Phones?

In the case of insertion, if you have a Model Pessoa not yet saved with a Collection Telefones, in the case of Entity Framework 6, the object Pessoa will be saved before, will earn an id, and right after each Telefone will be inserted. No assignments need to be made.

In the case of Entity Framework 5, it may still be necessary to assign some more information to Telefone, because the framework was not yet too clever to read and save aggregated objects as does Entity Framework 6.

What is recommended to do to then meet the possible conditions presented?

  1. Existing numbers already registered?

    If the number is being registered in repetition, you can first search the phones (using AsNoTracking() to avoid context monitoring) and iterate the list that comes from the form with the list that came from the database.

  2. Insert new numbers and others already existed?

    Just enter the new numbers. The answers already mentioned give examples of how to do this.

  3. Some numbers and add others?

    I believe it is one of the two cases above.

What are the best practices in these scenarios, which I believe is very generic and explanatory for several cases?

It is not very secret: the checks are usually in the edition of the parent record, and never in the insertion. What you can look at when entering are duplicate numbers, invalid numbers, etc.

  • This Begincollectionitem would not be very good, where it has numerous fields, besides only being able to leave them all side by side, horizontally, at least it is the examples I see... many data would be a very ugly view kkk, but in the case of Tiago, only 3 properties, of good

  • @Rod Totally wrong what you said. The Begincollectionitem is a enabler data processing of dependent entities. Having more or less fields does not change anything in the behavior of the component. Anyway, I will improve this answer when I can (within the reward period).

Browser other questions tagged

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