Many Update to Many Entity Framework c#

Asked

Viewed 310 times

7

Good morning,

I am having difficulty updating a record with entityframework, I will inform the whole structure below. In short, I have a register of artists, where these artists are related to address and categories.

An artist can have an address (one for one), and artists can have many categories (many for many).

entities:

Artist: relationship one to one with address many relationship to many with categories

public class Artista
    {

        public Artista()
        {
            ArtistaCategoria = new List<ArtistaCategoria>();
        }

        public int ArtistaId { get; set; }
        public string Nome { get; set; }
        public string Email { get; set; }
        public string Site { get; set; }
        public string Descricao { get; set; }

        public virtual Endereco Endereco { get; set; }

        public DateTime DataCadastro { get; set; }
        public DateTime DataAtualizacao { get; set; }

        public virtual ICollection<ArtistaCategoria> ArtistaCategoria { get; set; }
    }

public class Categoria
    {
        public Categoria()
        {
        }

        public int CategoriaId { get; set; }

        public string Nome { get; set; }

        public virtual ICollection<ArtistaCategoria> ArtistaCategoria { get; set; }
    }

public class Endereco
    {

        public Endereco()
        {
            Municipio = new Municipio();
        }
        public int EnderecoId { get; set; }
        public string Logradouro { get; set; }
        public string Numero { get; set; }
        public string Bairro { get; set; }
        public string Cep { get; set; }
        public int MunicipioId { get; set; }
        public virtual Municipio Municipio { get; set; }

    }

public class Municipio
    {
        public Municipio()
        {
        }

        public int MunicipioId { get; set; }

        public string Nome { get; set; }

        public string Cep { get; set; }

    }

Configuration Fluent API

public class ArtistaConfiguration : EntityTypeConfiguration<Artista>
    {
        public ArtistaConfiguration()
        {
            HasKey(a => a.ArtistaId);

            Property(a => a.Nome)
                .IsRequired();

            Property(a => a.Email)
                .HasMaxLength(150);

        }


public class EnderecoConfiguration : EntityTypeConfiguration<Endereco>
    {
        public EnderecoConfiguration()
        {
            HasKey(x => x.EnderecoId);
            Property(x => x.Logradouro).IsRequired();
            HasRequired(m => m.Municipio)
                .WithMany()
                .HasForeignKey(m => m.MunicipioId);

            Property(m => m.Cep)
                .IsFixedLength()
                .HasMaxLength(9)
                .HasColumnType("char");

        }
    }

I am unable to edit the data, when I save the categories, an error is shown stating that Municipioid is invalid.

I’ve always worked with ADO and Stored Procedures, and I’ve always heard a lot of people say that this is a drag, that with Entity it’s much faster and easier, but honestly I think I lost control, with Sps you have the application in hand, any mistake is easy to identify.

In this case, when editing the relationship of the category and the artist, an error is generated in the municipality (artist > address > municipality), I honestly cannot identify the problem.

Follow the update code:

public void Update(Artista obj, string[] arrayCategoria)
        {
            AtualizaEndereco(obj);
            ValidaCategorias(obj, arrayCategoria);
            Db.Entry(obj).State = EntityState.Modified;
            Db.SaveChanges();
        }

private void AtualizaEndereco(Artista artista)
        {
            var endereco = artista.Endereco;
            endereco.Municipio = null;
            Db.Entry(endereco).State = EntityState.Modified;
        }


private void AtualizarCategorias(Artista artista, string[] categorias)
    {
        var artistaAtual = Db.Artistas
            .FirstOrDefault(a => a.ArtistaId == artista.ArtistaId);
        //todo: fazer lista de categoria de acordo com array recebido
        List<Categoria> categoriasSelecionadas = new List<Categoria>();

        if (categorias != null)
        {
            foreach (var cat in categorias)
            {
                categoriasSelecionadas.Add(Db.Categorias.Find(int.Parse(cat)));
            }
        }

        foreach (var categoria in categoriasSelecionadas)
        {
            var artistaCategoria = new ArtistaCategoria
            {
                Artista = artistaAtual,
                Categoria = categoria
            };

            Db.ArtistaCategoria.Add(artistaCategoria);
            Db.SaveChanges();
        }

    }

Controller code that edits the register:

public class ArtistaController : Controller
    {
        private readonly IArtistaAppService _artistaApp;
        private readonly IMunicipioAppService _municipioApp;
        private readonly ICategoriaAppService _categoriaApp;
        public ArtistaController(IArtistaAppService artistaApp, IMunicipioAppService municipioApp, ICategoriaAppService categoriaApp)
        {
            _artistaApp = artistaApp;
            _municipioApp = municipioApp;
            _categoriaApp = categoriaApp;
        }
[...]

[HttpPost]
        [ValidateInput(false)]
        public ActionResult Edit(ArtistaViewModel artista, string[] arrayCategoria)
        {
            if (ModelState.IsValid)
            {

                var artistaDomain = Mapper.Map<ArtistaViewModel, Artista>(artista);
                _artistaApp.Update(artistaDomain, arrayCategoria);

                return RedirectToAction("index");
            }

            return View(artista);
        }


@model ViewModels.ArtistaViewModel

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

    <div class="form-horizontal">
        <div class="row">
            <div class="col-md-12">
                <div class="panel panel-default" data-collapsed="0">
                    <div class="panel-heading">
                        <div class="panel-title">
                            Dados do cadastro<br />
                        </div>

                        <div class="panel-options">
                            <a href="#" data-rel="collapse"><i class="entypo-down-open"></i></a>
                        </div>
                    </div>
                    <div class="panel-body">
                        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                        @Html.HiddenFor(model => model.ArtistaId)

                        <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.Nome, "", new { @class = "text-danger" })
                            </div>
                        </div>

                        <div class="form-group">
                            @Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-10">
                                @Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } })
                                @Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" })
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <button class="btn btn-blue btn-icon" type="button" id='addButton'>Adicionar telefone<i class="entypo-phone"></i></button>
                            </div>
                        </div>

                        <div class="form-group">
                            @Html.LabelFor(model => model.Endereco.Cep, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-5">
                                @Html.EditorFor(model => model.Endereco.Cep, new { htmlAttributes = new { @class = "form-control" } })
                            </div>

                        </div>

                        <div class="form-group">
                            @Html.LabelFor(model => model.Endereco.Logradouro, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-5">
                                @Html.EditorFor(model => model.Endereco.Logradouro, new { htmlAttributes = new { @class = "form-control" } })
                            </div>
                        </div>

                        <div class="form-group">
                            @Html.LabelFor(model => model.Endereco.Numero, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-5">
                                @Html.EditorFor(model => model.Endereco.Numero, new { htmlAttributes = new { @class = "form-control" } })
                            </div>
                        </div>

                        <div class="form-group">
                            @Html.LabelFor(model => model.Endereco.Bairro, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-5">
                                @Html.EditorFor(model => model.Endereco.Bairro, new { htmlAttributes = new { @class = "form-control" } })
                            </div>
                        </div>

                        <div class="form-group">
                            @Html.LabelFor(model => model.Endereco.MunicipioId, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-5">
                                <select id="Endereco_MunicipioId" name="Endereco.MunicipioId" class="form-control"></select>
                            </div>
                        </div>
                        @Html.HiddenFor(model => model.Endereco.EnderecoId)

                        @{

                            List<ViewModels.CategoriasSelecionadas> categorias = ViewBag.Categorias;

                            foreach (var categoria in categorias)
                            {

                                <input type="checkbox"
                                       name="arrayCategoria"
                                       value="@categoria.CategoriaId"
                                       @(Html.Raw(categoria.Selecionada ? "checked=\"checked\"" : "")) />
                                    @categoria.CategoriaId @:  @categoria.Nome


                            }
                        }

                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="form-group default-padding">
        <button class="btn btn-success btn-icon" type="submit">Salvar<i class="entypo-check"></i></button>
        <button type="reset" class="btn btn-icon btn-default">Cancelar alterações <i class="entypo-cancel"></i></button>
        <a href="@Url.Action("Index")" class="btn btn-icon btn-info">Voltar para listagem<i class="entypo-reply"></i></a>
    </div>
}

1 answer

4

This is the hard way to do it. This Fluent API configuration has long since been exceeded.

Set your associative entity manually:

public class ArtistaCategoria
{
    [Key]
    public int ArtistaCategoriaId { get; set; }
    [Index("IUQ_ArtistaCategoria_ArtistaId_CategoriaId", IsUnique = true, Order = 1)]
    public int ArtistaId { get; set; }
    [Index("IUQ_ArtistaCategoria_ArtistaId_CategoriaId", IsUnique = true, Order = 2)]
    public int CategoriaId { get; set; }

    public virtual Artista Artista { get; set; }
    public virtual Categoria Categoria { get; set; }
}

[Index], introduced in this form from the Entity Framework 6.1.0, ensures the uniqueness of the associative record. Additional validations may be required in the application to avoid strange key duplicity errors for the user.

Also change your Models to the following:

public class Artista
{
    [Key]
    public int ArtistaId { get; set; }
    public string Nome { get; set; }
    public string Email { get; set; }
    public string Site { get; set; }
    public string Descricao { get; set; }

    public virtual Endereco Endereco { get; set; }

    public DateTime DataCadastro { get; set; }
    public DateTime DataAtualizacao { get; set; }

    public virtual ICollection<ArtistaCategoria> ArtistaCategorias { get; set; }
}

public class Categoria
{
    [Key]
    public int CategoriaId { get; set; }

    public string Nome { get; set; }

    public virtual ICollection<ArtistaCategoria> ArtistaCategorias { get; set; }
}

With this, you don’t need to use the Fluent API configuration.

The code below is enough to create relationships between artists and categories:

var artistaAtual = Db.Artistas
            .FirstOrDefault(a => a.ArtistaId == artista.ArtistaId);
var tresCategorias = Db.Categorias.Take(3).ToList();

foreach (var categoria in tresCategorias)
{
    var artistaCategoria = new ArtistaCategoria
    {
        Artista = artistaAtual,
        Categoria = categoria
    };

    Db.ArtistaCategorias.Add(artistaCategoria);
    Db.SaveChanges();
}
  • 1

    Valew Gypsy, I will test and put here the result, so it looks much simpler anyway.

  • Still generating error in Db.Savechanges(); I am informed that the Address cannot be null.

  • Ah, yes. Note that the example I gave does not contemplate the other validations you need to do. Possibly you will have to enter example addresses, and so on.

  • why I say I have no control, I am not registering address, I am editing the registration, the address already exists, and it generates an error qnd I use Db.Savechanges() after adding or removing a category.. but the category is unrelated to address..

  • Would the address come from the completed View? This would be another question.

  • yes, it comes from the view, but there is a case that will not be informed, it should be null even.. when I ignore the categories I can edit everything normal, with or without address, but qnd will edit the categories generates this error..

  • Well, let’s go to another question. There you put there as is the screen currently and the specific error.

Show 3 more comments

Browser other questions tagged

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