Error saving a Many-To-Many relation

Asked

Viewed 104 times

2

Following the models that of that question, I am having problems trying to save the data (keys of each of the records) in the table created to make the data relationship. Follows the post method I built:

Controller

[HttpPost, ValidateAntiForgeryToken]
    public ActionResult Novo(UsuariosNovo form, NivelCheckbox checkbox)
    {
        var usr = new Usuario();

        foreach (var nivelId in form.Niveis)
        {
            var item = new NivelUsuario() { nivelid = checkbox.nivelid, usuarioid = form.usuarioid };
            db.NivelUsuario.Add(item);
        }


        if (db.Usuario.Any(u => u.nome == form.nome))
            ModelState.AddModelError("nome", "O nome do usuario precisa ser unico.");

        if (!ModelState.IsValid)
            return View(form);

        usr.descricao = form.descricao;
        usr.nome = form.nome;
        usr.SetPassword(form.senha);

        db.Usuario.Add(usr);
        db.SaveChanges();

        return RedirectToAction("Index");
    }

Viewmodel

public class NivelCheckbox
{
    public int nivelid { get; set; }
    public bool IsChecked { get; set; }
    public string nome { get; set; }
}



public class UsuariosNovo
{
    public List<NivelCheckbox> Niveis { get; set; }

    [HiddenInput]
    public int usuarioid { get; set; }

    [Required(ErrorMessage = "Obrigatório informar a Descrição")]
    [Display(Name = "Descrição")]
    public string descricao { get; set; }

    [Required(ErrorMessage = "Obrigatório informar o Login")]
    [Display(Name = "Login")]
    public string nome { get; set; }

    [Required(ErrorMessage = "Obrigatório informar a Senha")]
    [Display(Name = "Senha"), DataType(DataType.Password)]
    public string senha { get; set; }
}

View

<div class="panel panel-default col-sm-10 col-sm-offset-2">
    <div class="panel-heading">Niveis</div>
    <div class="panel-body">
        <ul class="list-group">

            @for (var i = 0; i < Model.Niveis.Count; i++)
            {
                <li class="list-group-item">
                    @Html.Hidden("Niveis[" + i + "].nivelid", Model.Niveis[i].nivelid)
                    <label for="Niveis_@(i)__IsChecked">
                        @Html.CheckBox("Niveis[" + i + "].IsChecked", Model.Niveis[i].IsChecked)
                        @Model.Niveis[i].nome
                    </label>
                </li>
            }
        </ul>
    </div>
</div>

When this action is called an exception is invoked:

23503: insert or update in table "nivel_usuarios" viola foreign key constraint "nivel_usuarios_nivelid_fkey"

By the debug I did, the id’s come reset to the controller, does anyone know where I’m going wrong? Or is there a more correct way to do this? (Pass level id and user to another table)

  • Could you put in your question how is the Form also?

  • I put the part of the levels... I know the conditions are missing to only add in the bank if the Ischecked is true and tals...

  • I didn’t understand this variable NivelCheckbox. As checkbox you need are no longer in UsuarioNovo?

  • You talk about Controller, right? Really, I was going too far, it was not necessary to do this, I passed this because I was not able to catch the nivelid in the foreach loop... @Ciganomorrisonmendez

2 answers

1

The problem is that there are things missing here:

@for (var i = 0; i < Model.Niveis.Count; i++)
{
    <li class="list-group-item">
        @Html.Hidden("Niveis[" + i + "].nivelid", Model.Niveis[i].nivelid)
        <label for="Niveis_@(i)__IsChecked">
            @Html.CheckBox("Niveis[" + i + "].IsChecked", Model.Niveis[i].IsChecked)
            @Model.Niveis[i].nome
        </label>
    </li>
}

The line index is missing, as below:

@for (var i = 0; i < Model.Niveis.Count; i++)
{
    <li class="list-group-item">
        <input type="hidden" name="Niveis.index" id="Niveis_index" value="@(i)" />
        @Html.Hidden("Niveis[" + i + "].nivelid", Model.Niveis[i].nivelid)
        <label for="Niveis_@(i)__IsChecked">
            @Html.CheckBox("Niveis[" + i + "].IsChecked", Model.Niveis[i].IsChecked)
            @Model.Niveis[i].nome
        </label>
    </li>
}

This makes the Modelbinder better organised and guaranteed to define the variables.

  • Gypsy, I managed to solve the problem in another way, only now has another problem, when edit, synchronize the checkbox, if it exists in the bank, it brings checkout, otherwise it brings empty...

  • I think you are making the solution very difficult. The right thing would be to use bank-mapped entities and use @Html.CheckBoxFor, but it would have to reformulate, and much, its solution until then.

  • Hmm, but would you have any example of this? And how would it look in the controller for me to know if it’s checked or not?

  • I can, but you’ll have to rephrase your question, asking for an example where to change @Html.CheckBox for @Html.CheckBoxFor.

0

I managed to solve the problem as follows, I just changed the action:

Controller

[HttpPost, ValidateAntiForgeryToken]
    public ActionResult Novo(UsuariosNovo form)
    {
        var usr = new Usuario();

        if (db.Usuario.Any(u => u.nome == form.nome))
            ModelState.AddModelError("nome", "O nome do usuario precisa ser unico.");

        if (!ModelState.IsValid)
            return View(form);

        foreach (var nivelId in form.Niveis)
        {
            var item = new NivelUsuario() { nivelid = nivelId.nivelid, usuarioid = form.usuarioid };

            if (nivelId.IsChecked)
                db.NivelUsuario.Add(item);
        }

        usr.descricao = form.descricao;
        usr.nome = form.nome;
        usr.SetPassword(form.senha);

        db.Usuario.Add(usr);
        db.SaveChanges();

        return RedirectToAction("Index");
    }

Browser other questions tagged

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