Many Update to Many Entity Framework C #

7

Good morning,

I'm having trouble updating a record with entityframework, I'll tell you 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: one-to-one relationship with address many-to-many relationship 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; }

    }

Fluent API Configuration

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 not able to edit the data, when I save the categories, an error is shown stating that the MunicipalityId is invalid.

I have always worked with ADO and Stored Procedures, and I have always heard a lot of people say that this is an oversight, that with Entity is much faster and easier, but honestly I think I lost control, with SPs you have the application in hand , any error 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 can not 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 registry:

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>
}
    
asked by anonymous 02.02.2016 / 15:02

1 answer

4

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

Define 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, guarantees the uniqueness of the associative register. Additional validations may be required in the application to avoid extraneous errors of key duplication 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 do not need to use the Fluent API configuration.

The code below is enough to create the 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();
}
    
02.02.2016 / 15:28