How do I checkbox asp.net mvc database

1

Hello. I'm doing a project for a music player. In the PlayList register, I would like the songs to appear in a checkbox in order to mark the desired ones and save them to the database.

This is my controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using XMusic.MVC.Models;
using XMusic.MVC.Persistencia;

namespace XMusic.MVC.Controllers
{
    public class PlayListController : Controller
    {

        private PersistenciaPlayList persistenciaPlayList;
        private ContextoEF contextoEF;

        private PersistenciaUsuario persistenciaUsuario;
        private PersistenciaMusica persistenciaMusica;

        public PlayListController()
        {
            persistenciaPlayList = new PersistenciaPlayList();
            persistenciaMusica = new PersistenciaMusica();
            contextoEF = new ContextoEF();

            persistenciaUsuario = new Persistencia.PersistenciaUsuario();

            ViewData["usuarios"] = persistenciaUsuario.ObterTodos();
            ViewData["musicas"] = persistenciaMusica.ObterTodas();
        }

        public ActionResult Index()
        {
            var playlists = persistenciaPlayList.ObterTodos();
            return View(playlists);
        }

        [HttpGet]
        public ActionResult Adicionar()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Adicionar(PlayList playlist)
        {


            var msgAlerta = string.Empty;
            var tipoAlerta = string.Empty;

            try
            {
                if (!ModelState.IsValid)
                    throw new Exception();

                persistenciaPlayList.Adicionar(playlist);

                ModelState.Clear();

                msgAlerta = "PlayList cadastrada com sucesso";
                tipoAlerta = "alert-success";

            }
            catch (Exception e)
            {
                msgAlerta = "Ocorreu um erro ao cadastrar a PlayList" + e;
                tipoAlerta = "alert-danger";
            }

            TempData.Add("MsgAlerta", msgAlerta);
            TempData.Add("TipoAlerta", tipoAlerta);

            return View();
        }


        [HttpGet]
        public ViewResult Alterar(int playlistId)
        {
            var playlist = persistenciaPlayList.Obter(playlistId);
            return View(playlist);
        }

        [HttpPost]
        public ActionResult Alterar(PlayList playlist)
        {
            var msgAlerta = string.Empty;
            var tipoAlerta = string.Empty;

            var alterado = false;

            try
            {
                if (!ModelState.IsValid)
                    throw new Exception();

                alterado = persistenciaPlayList.Alterar(playlist);

                if (alterado)
                {
                    msgAlerta = "PlayList alterada com sucesso";
                    tipoAlerta = "alert-success";
                }
                else
                {
                    msgAlerta = "Ocorreu um erro ao alterar a PlayList";
                    tipoAlerta = "alert-danger";
                }

            }
            catch (Exception e)
            {
                alterado = false;
                msgAlerta = "Ocorreu um erro ao alterar a PlayList: " + e;
                tipoAlerta = "alert-danger";

            }


            TempData.Add("MsgAlerta", msgAlerta);
            TempData.Add("TipoAlerta", tipoAlerta);

            if (alterado)
                return RedirectToAction("Index");
            else
                return View();
        }


        [HttpGet]
        public JsonResult ObterUsuarios()
        {
            var msg = string.Empty;

            var listaUsuarios = persistenciaUsuario.ObterTodos();


            if (!listaUsuarios.Any())
            {
                msg = "Nenhum usuário encontrado";
            }

            var usuarios = from u in listaUsuarios
                           select new { UsuarioId = u.UsuarioId, Nome = u.Nome };

            return Json(new { ret = usuarios, mensagem = msg },
                JsonRequestBehavior.AllowGet);
        }


        [HttpGet]
        public JsonResult ObterMusicas()
        {
            var msg = string.Empty;

            var listaMusicas = persistenciaMusica.ObterTodas();


            if (!listaMusicas.Any())
            {
                msg = "Nenhum música encontrada";
            }

            var musicas = from m in listaMusicas
                           select new { MusicaId = m.MusicaId, Titulo = m.Titulo };

            return Json(new { ret = musicas, mensagem = msg },
                JsonRequestBehavior.AllowGet);
        }



        [HttpGet]
        public JsonResult ObterTodos()
        {
            var msg = string.Empty;

            var listaPlayList = persistenciaPlayList.ObterTodos().ToList();



            if (!listaPlayList.Any())
            {
                msg = "Nenhuma PlayList encontrada";
            }


            var playlist = from p in listaPlayList
                           select new { PlayListId = p.PlayListId, Titulo = p.Titulo };



            return Json(new { ret = playlist, mensagem = msg },
                JsonRequestBehavior.AllowGet);
        }
    }


}

This is the persistence of PlayList:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using XMusic.MVC.Models;

namespace XMusic.MVC.Persistencia
{
    public class PersistenciaPlayList
    {
        private ContextoEF _contexto = new ContextoEF();


        public PlayList Adicionar(PlayList playlist)
        {
            _contexto.Playlists.Add(playlist);
            _contexto.SaveChanges();

            return playlist;
        }


        public IEnumerable<PlayList> ObterTodos()
        {
            return _contexto.Playlists.ToList();
        }

        public PlayList Obter(int id)
        {
            try
            {
                return _contexto.Playlists.FirstOrDefault(p => p.PlayListId == id);
            }
            catch
            {
                throw;
            }

        }


        public bool Alterar(PlayList playlist)
        {
            try
            {
                var p = _contexto.Playlists.Find(playlist.PlayListId);

                if (p != null)
                {

                    p.Titulo = playlist.Titulo;
                    p.UsuarioId = playlist.UsuarioId;
                    p.Musicas = playlist.Musicas;

                    _contexto.Entry(p).State = EntityState.Modified;
                    _contexto.SaveChanges();

                    return true;
                }
                else
                    return false;
            }
            catch
            {
                throw;
            }
        }




        public PlayList Remover(int id)
        {
            try
            {
                var p = _contexto.Playlists.Find(id);

                _contexto.Playlists.Remove(p);
                _contexto.SaveChanges();

                return p;
            }
            catch
            {
                return null;
            }
        }

    }
}

And here's where I'm showing the data. I would like the music data to be shown in checkbox:

@model XMusic.MVC.Models.PlayList

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

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

        <div class="form-group">
            @Html.LabelFor(model => model.UsuarioId, "Usuário", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.DropDownList(
               "UsuarioId",
               new SelectList(
                   (IEnumerable<XMusic.MVC.Models.Usuario>)ViewData["usuarios"],
                   "UsuarioId",
                   "Nome"),

               htmlAttributes: new { @class = "form-control" })


                @Html.ValidationMessageFor(model => model.UsuarioId, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Musicas, "Músicas", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">


                @Html.DropDownList(
               "MusicaId",
               new SelectList(
                   (IEnumerable<XMusic.MVC.Models.Musica>)ViewData["musicas"],
                   "MusicaId",
                   "Titulo"),

               htmlAttributes: new { @class = "form-control" })


                @Html.ValidationMessageFor(model => model.Musicas, "", new { @class = "text-danger" })
            </div>
        </div>


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

<div>
    @Html.ActionLink("Lista de PlayLists", "Index")
</div>
    
asked by anonymous 19.11.2016 / 20:29

2 answers

1

The best way is ViewModels :

public class PlaylistViewModel
{
    public Playlist Playlist { get; set; }
    public ICollection<UsuarioViewModel> Usuarios { get; set; }
}

public class MusicaViewModel
{
    public bool Selecionado { get; set; }
    public Musica Musica { get; set; }
}

This is lousy to do. It's good to remove.

    public PlayListController()
    {
        persistenciaPlayList = new PersistenciaPlayList();
        persistenciaMusica = new PersistenciaMusica();
        contextoEF = new ContextoEF();

        persistenciaUsuario = new Persistencia.PersistenciaUsuario();

        ViewData["usuarios"] = persistenciaUsuario.ObterTodos();
        ViewData["musicas"] = persistenciaMusica.ObterTodas();
    }

Same thing for your persistence layer. Here are the reasons

Controller

The Index looks like this:

    public ActionResult Alterar(int playlistId)
    {
        var playlist = contexto.Playlists
                                .Include(p => p.Musicas)
                                .Include(p => p.Usuarios)
                                .FirstOrDefault(p => p.PlaylistId == playlistId);

        var viewModel = new PlaylistViewModel
        {
            Playlist = playlist, 
            Musicas = playlist.Musicas.Select(m => new MusicaViewModel
            {
                Selecionado = false,
                Musica = m
            })
        };

        ViewModel.Usuarios = contexto.Usuarios.ToList();
        return View(playlist);
    }

View

@model XMusic.MVC.ViewModels.PlayListViewModel

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

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

        <div class="form-group">
            @Html.LabelFor(model => model.UsuarioId, "Usuário", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.DropDownListFor(model => model.Playlist.UsuarioId,
                    ((IEnumerable<XMusic.MVC.Models.Usuario>)ViewBag.Usuarios).Select(u => new SelectListItem {
                        Text = u.Nome,
                        Value = u.UsuarioId.ToString(),
                        Selected = (Model != null) && (Model.Playlist != null) && (Model.Playlist.UsuarioId == u.UsuarioId)
                    }), "Selecione...", new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Playlist.UsuarioId, "", new { @class = "text-danger" })
            </div>
        </div>

        @foreach (var musica in Model.Musicas)
        {
            <div>
                @Html.Partial("_Musicas", musica)
            </div>
        }

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

<div>
    @Html.ActionLink("Lista de PlayLists", "Index")
</div>

Partial _LinhaMusica.cshtml

For this to work well, you'll need to use the BeginCollectionItem package:

@model XMusic.MVC.ViewModels.MusicaViewModel

@using (Html.BeginCollectionItem("Musicas"))
{
    @Html.HiddenFor(model => model.Musica.MusicaId)
    @Html.CheckBoxFor(model => model.Selecionado) @Model.Musica.Nome
}

Controller ( POST )

    [HttpPost]
    public ActionResult Alterar(PlayListViewModel playlist)
    {
        var msgAlerta = string.Empty;
        var tipoAlerta = string.Empty;

        var alterado = false;

        // Não use try ... catch dentro de Actions.
        // Use o método OnException do Controller ancestral para tratar exceções.
        // try
        // {
            if (ModelState.IsValid)
            {
                // Se o Model é inválido, não levante exceção.
                // A maneira correta é enviando ao Form mensagens de validação de cada campo que falhou.
                // throw new Exception();

                // Monte uma nova lógica aqui. 
                // Aqui você pode conferir as CheckBoxes que foram marcadas, etc.
                // alterado = persistenciaPlayList.Alterar(playlist);

                if (alterado)
                {
                    TempData.Add("MsgAlerta", "PlayList alterada com sucesso");
                    TempData.Add("TipoAlerta", "alert-success");
                    return RedirectToAction("Index");
                }

                TempData.Add("MsgAlerta", "Ocorreu um erro ao alterar a PlayList");
                TempData.Add("TipoAlerta", "alert-danger");
            }
        // catch (Exception e)
        // {
        //    alterado = false;
        //    msgAlerta = "Ocorreu um erro ao alterar a PlayList: " + e;
        //    tipoAlerta = "alert-danger";
        // }

        ViewModel.Usuarios = contexto.Usuarios.ToList();            
        return View(alterado);
    }
    
04.05.2017 / 20:59
0

Posting lists of objects usually does a bit of work. In these cases I usually use Custom Model Binders. By default Asp.NET binds the data from our Views to the parameters in our Actions, so when we use the approach I mentioned above, we give up part of Automatic Binding. To do this you just have to implement some interfaces provided by Asp.NET itself, but depending on the version instead of interfaces, you will have to extend some classes. In the following example, I implemented its functionality using Asp.NET.Core. Here's how the code went:

class ListaMusicaModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        if (context.Metadata.PropertyName != null && context.Metadata.ContainerType.GetProperty(context.Metadata.PropertyName).PropertyType.IsConstructedGenericType)
            return new ListaMusicaModelBinder(context.Metadata.ModelType);
        return null;
    }
}

We first created the Provider, which will provide the correct instance responsible for performing the Bind of the data for you. Note that for all metadata that should not be handled by ListMusicModelBinder we return null, I do this because I want every property other than the music collection to remain under the responsibility of Asp.NET to perform Bind.

Now our ModelBinder

class ListaMusicaModelBinder : IModelBinder
{
    public ListaMusicaModelBinder(Type type)
    {
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        return Task.Run(() =>
        {
            List<Musica> musicas = new List<Musica>();
            var request = bindingContext.HttpContext.Request.Form.Keys.Where(w => w.Contains(bindingContext.FieldName)).ToArray();
            var grupos = request.GroupBy(s => s.Split('.').First());
            foreach (var item in grupos)
            {                    
                var value = bindingContext.ValueProvider.GetValue(item.First()).FirstValue;
                musicas.Add(new Musica
                {
                    MusicaId = int.Parse(value)
                });
            }
            bindingContext.Result = ModelBindingResult.Success(musicas);
        });
    }
}

This class is responsible for mapping the data coming from the requests, to the respective properties of their parameters, if not the parameter itself.

But I want to draw attention to the BindModelAsync method, there are other approaches to implementing this method, as in article Custom Template Binding in ASP.Net Core .

I tried to implement it in the simplest possible way, as I did not see the need to treat (N) types of collections and map (N) property types, this would only inflate the code with unnecessary complexity, you only need to treat collections of songs, and your view will return only the Ids of the selected songs.

In the Startup file, in the ConfigureServices method, we configure our provider

public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddMvc().AddMvcOptions((opt) =>
        {
            // aqui configuramos nosso provider.
            opt.ModelBinderProviders.Insert(0, new XMusic.Controllers.ListaMusicaModelBinderProvider());
        });
    }

Asp.NET allows us to add (N) providers for a variety of things, ranging from Validation Customizers to Mapping data from requests.

You will find the Add method in ModelBinderProviders, but I recommend using Insert, this ensures that our customized provider is the first to be analyzed.

Finally the View is as follows

@*
    For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
*@
@{
}
@model XMusic.Models.PlayList

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

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

        <div class="form-group">
            @Html.LabelFor(model => model.UsuarioId, "Usuário", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.DropDownList(
               "UsuarioId",
               new SelectList(
                   (IEnumerable<XMusic.Models.Usuario>)ViewData["usuarios"],
                   "UsuarioId",
                   "Nome"),

               htmlAttributes: new { @class = "form-control" })


                @Html.ValidationMessageFor(model => model.UsuarioId, "", new { @class = "text-danger" })
            </div>
        </div>
        @{ 
            var Musicas = (IList<XMusic.Models.Musica>)ViewData["musicas"];
        }
        <table>
            <thead>
                <tr>
                    <td>#</td>
                    <td>Música</td>
                </tr>
                @for (int i = 0; i < Musicas.Count; i++)
                {
                    <tr>
                        <td>
                            <input type="checkbox" value="@Musicas[i].MusicaId" 
                                   id="@Html.IdFor(m =>m.Musicas[i].MusicaId)"
                                   name="@Html.NameFor(m =>m.Musicas[i].MusicaId)"/>
                        </td>
                        <td>
                            @Musicas[i].Titulo
                        </td>
                    </tr>
                }
            </thead>

        </table>

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

<div>
    @Html.ActionLink("Lista de PlayLists", "Index")
</div>

Your songs are displayed in a table, and in this table we have the checkbox that will allow the user to choose the songs that will be saved.

When you run the Post, you will see that your playlist within the PlayList will only be populated with the selected songs.

But why this approach?

There are other ways to do this, one of which is to use ajax requests, but in order not to get away from what you've done so far, I recommend this approach.

If you want an example of ModelBind in other versions of Asp.NET I recommend the links below:

How to bind to custom objects in action signatures in MVC / WebAPI

Custom Model Binder for ASP.NET MVC on GET request

    
25.02.2017 / 21:31