Entity Framework 6, many X many relationship and Scaffolding

Asked

Viewed 469 times

1

I am conducting a college project and my knowledge in ASP.Net are not very deep. In my project I came across a relationship Mxm and I cannot usefully implement this situation. The relationship is as follows::

  • A company registers one or more garbage collection points and at the time of registration it chooses one or more garbage accepted in your collection point.

  • A type of garbage can be used in the registration of several collection points.

  • A collection point can associate several types of garbage that are accepted.

I have tried in numerous ways indicated here on this site. I tried this way here and creating the associative class manually, but in scaffolding the Entity does not generate the relation in the view.

Doing as indicated in this link Here, The Entity Framework generated the associative table automatically, but in scaffolding the views were created with no association. In 1xN relations the Entity automatically creates a dropdowlist to be selected in the view.

  1. In the Mxm relation you should not automatically create a group of checkbox or a selection list? (or am I expecting a lot from Entity...).

  2. If you cannot generate this Feature automatically, as you would that?

  3. After generating the checkbox group or checklist, how to do to manipulate the associative table? (the table of the class in question the Entity has already created CRUD).

Follows the code:

[Table("PontoDeColeta")]
public class PontoDeColeta
{
    public PontoDeColeta()
    {
        TipoDeLixo = new HashSet<TipoDeLixo>();
    }

    public int Id { get; set; }

    [Required(ErrorMessage = "O prenchimento é obrigatório")]
    [Display(Name = "Nome popular*: ")]
    public string NomePopular { get; set; }

    [Required(ErrorMessage = "O prenchimento é obrigatório")]
    [Display(Name = "Endereço do ponto de coleta*: ")]
    public string Endereco { get; set; }

    //Armazena usuário logado
    [HiddenInput(DisplayValue = false)]
    public string UsuarioResponsavel { get; set; }

    public ICollection<TipoDeLixo> TipoDeLixo { get; set; }
}
[Table("TipoDeLixo")]
public class TipoDeLixo
{
    public TipoDeLixo()
    {
        PontoDeColeta = new HashSet<PontoDeColeta>();
    }
    //[Key]
    //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage ="O prenchimento é obrigatório")]
    [Display(Name = "Nome do tipo de lixo*: ")]
    public string NomeTipoLixo { get; set; }

    [Display(Name = "Este tipo de lixo está ativo? ")]
    public bool Ativo { get; set; }

    public ICollection<PontoDeColeta> PontoDeColeta { get; set; }
}

Edit: Following the best response of the comment, I had to change my classes in addition to creating the associative class manually.

public class TipoDeLixo{
    public Guid Id { get; set; }


    [Required(ErrorMessage ="O prenchimento é obrigatório")]
    [Display(Name = "Nome do tipo de lixo*: ")]
    public string NomeTipoLixo { get; set; }

    [Display(Name = "Este tipo de lixo está ativo? ")]
    public bool Ativo { get; set; }


    public ICollection<PontoDeColetaTipoDeLixo> PontosDeColetaTiposDeLixo {  get; set; }

}

public class PontoDeColeta{
    public Guid Id { get; set; }

    [Required(ErrorMessage = "O prenchimento é obrigatório")]
    [Display(Name = "Nome popular*: ")]
    public string NomePopular { get; set; }

    public ICollection<PontoDeColetaTipoDeLixo> PontosDeColetaTiposDeLixo { get; set; }
     }

public class PontoDeColetaTipoDeLixo{

    public Guid Id { get; set; }

    public virtual TipoDeLixo TipoDeLixo { get; set; }

    public Guid TipoDeLixoId { get; set; }

    public virtual PontoDeColeta PontoDeColeta { get; set; }

    public Guid PontoDeColetaId { get; set; }
    }

Na View Create do Pontodecoleta:

 @Html.Partial("_TiposDeLixo",Model.PontosDeColetaTiposDeLixo)

I created Partialview _Typodetrash with content:

@model IEnumerable<IdentitySample.Models.PontoDeColetaTipoDeLixo>

<div class="actions">
<a class="btn btn-default btn-sm" id="adicionar-tipo-de-lixo">
    Adicionar Tipo de lixo
</a>
<script type="text/javascript">
    $("#adicionar-tipo-de-lixo").click(function () {
                $.get('/PontosDeColeta/NovaLinhaDeTipoDeLixo', function (template) {
                    $("#area-tipos-de-lixo").append(template);
                });
            });
</script>
</div>

<div id="area-tipos-de-lixo">
@if (Model != null)
{
    foreach (var lixo in Model)
    {
        @Html.Partial("_LinhaTipoDeLixo", lixo);
    }
}
</div>

Then I created Partiaview _Linhatipodelixo with the content:

@model IdentitySample.Models.PontoDeColetaTipoDeLixo

@using (Html.BeginCollectionItem("TipoDeLixo"))
{
<div class="form-group">
    @Html.HiddenFor(model => model.Id)
    @Html.HiddenFor(model => model.TipoDeLixoId)

    <label class="col-md-1 control-label">Nome</label>
    <div class="col-md-5">
        @Html.TextBoxFor(model => model.TipoDeLixo.NomeTipoLixo, new { @class = "form-control", placeholder = "Nome" })
        @Html.ValidationMessageFor(model => model.TipoDeLixo.NomeTipoLixo, "", new { @class = "text-danger" })
    </div>
    <div class="col-md-2">
        <a class="btn red" onclick="$(this).parent().parent().remove();">Excluir</a>
    </div>
</div>
}

And in Pontodecoletacontroller I created Action with the following content:

public ActionResult NovaLinhaDeTipoDeLixo()
    {
        return PartialView("_TiposDeLixo", new PontoDeColetaTipoDeLixo { Id = Guid.NewGuid() });
    }

Then when I run the system, the following error appears:

The model item passed into the dictionary is of type 'IdentitySample.Models.PontoDeColeta', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[IdentitySample.Models.PontoDeColetaTipoDeLixo]'.

EDIT: I took a screen print with the error. Print com o erro CONTROLLER UPDATE Pontodecoletacontroller complete:

namespace IdentitySample.Controllers
{
public class PontosDeColetaController : Controller
{
    private ApplicationDbContext db = new ApplicationDbContext();


    // GET: PontosDeColeta
    public ActionResult Index()
    {
        return View();
    }


    public ActionResult NovaLinhaDeTipoDeLixo()
    {
        return PartialView("_LinhaTipoDeLixo", new PontoDeColetaTipoDeLixo { Id = Guid.NewGuid() });
    }


    //Lista os potos de coleta de acordo com parametros passados
    public JsonResult Listar(string searchPhrase, int current = 1, int rowCount = 5)
    {
        var Identificacao = User.Identity.GetUserId().ToString();
        var Pontos = (from u in db.PontoDeColeta
                      where u.UsuarioResponsavel == Identificacao
                      select u);
        string Chave = Request.Form.AllKeys.Where(v => v.StartsWith("sort")).First();

        string Ordenacao = Request[Chave];

        string Coluna = Chave.Replace("sort[", String.Empty).Replace("]",String.Empty);

        var Soma = Pontos.Count();

        if (!String.IsNullOrWhiteSpace(searchPhrase))
        {
            Pontos = Pontos.Where("NomePopular.Contains(@0) OR Endereco.Contains(@0) OR Cidade.Contains(@0) OR Estado.Contains(@0) OR Apelido.Contains(@0)", searchPhrase);
        }

        string ColunaOrdenacao = String.Format("{0} {1}", Coluna, Ordenacao);

        var PontoDeColetaPagina = Pontos.OrderBy(ColunaOrdenacao).Skip((current - 1) * rowCount).Take(rowCount);

        return Json(new{
            rows = PontoDeColetaPagina.ToList(),
            current = current,
            rowCount = rowCount,
            total = Soma
        }
        , JsonRequestBehavior.AllowGet);
    }

    // GET: PontosDeColeta/Details/5
    public ActionResult Details(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        PontoDeColeta pontoDeColeta = db.PontoDeColeta.Find(id);
        if (pontoDeColeta == null)
        {
            return HttpNotFound();
        }

        return PartialView(pontoDeColeta);
    }

    // GET: PontosDeColeta/Create
    public ActionResult Create()
    {
        var UsuarioLogado = new PontoDeColeta();
        UsuarioLogado.UsuarioResponsavel = User.Identity.GetUserId();
        return View(UsuarioLogado);
    }

    // POST: PontosDeColeta/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Id,NomePopular,Endereco,Cidade,Estado,Latitude,Longitude,Remuneracao,InfoAdicional,Ativo,Apelido,UsuarioResponsavel")] PontoDeColeta pontoDeColeta)
    {
        if (ModelState.IsValid)
        {
            pontoDeColeta.Id = Guid.NewGuid();
            db.PontoDeColeta.Add(pontoDeColeta);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(pontoDeColeta);
    }

    // GET: PontosDeColeta/Edit/5
    public ActionResult Edit(Guid? id)
    {

        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        PontoDeColeta pontoDeColeta = db.PontoDeColeta.Find(id);
        if (pontoDeColeta == null)
        {
            return HttpNotFound();
        }
        pontoDeColeta.UsuarioResponsavel = User.Identity.GetUserId();
        return View(pontoDeColeta);
    }

    // POST: PontosDeColeta/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "Id,NomePopular,Endereco,Cidade,Estado,Latitude,Longitude,InfoAdicional,Ativo,Apelido,UsuarioResponsavel")] PontoDeColeta pontoDeColeta)
    {
        if (ModelState.IsValid)
        {
            db.Entry(pontoDeColeta).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(pontoDeColeta);
    }

    // GET: PontosDeColeta/Delete/5
    public ActionResult Delete(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        PontoDeColeta pontoDeColeta = db.PontoDeColeta.Find(id);
        if (pontoDeColeta == null)
        {
            return HttpNotFound();
        }
        return PartialView(pontoDeColeta);
    }

    // POST: PontosDeColeta/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(Guid id)
    {
        PontoDeColeta pontoDeColeta = db.PontoDeColeta.Find(id);
        db.PontoDeColeta.Remove(pontoDeColeta);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            db.Dispose();
        }
        base.Dispose(disposing);
    }
}
}

1 answer

0


I have tried in numerous ways indicated here on this site. I tried this way here and creating the associative class manually, but in scaffolding the Entity does not generate the relation in the view.

In what sense does it not generate? If it is a master-detail relationship, it does not generate. The Scaffolding may be somewhat limited in some situations.

  1. Shouldn’t Mxm automatically create a checkbox group or checklist? (or am I expecting a lot from Entity...).

Is expecting much from the mechanism of Scaffolding, in fact. Unfortunately, until then, this implementation is manual. I have tried a few times to produce a Scaffold for this case, but it is quite complicated. I have not found satisfactory solution. If I find it, I promise to come back here and propose a solution.

  1. If you cannot generate this Feature automatically, how would you do it?

Using the package Begincollectionitem. See here the answers already given on the site. This is my best answer.

  1. After generating the checkbox group or checklist, how to manipulate the associative table? (the table of the class in question the Entity has already created the CRUD).

Depends on the Controller. If it is a Controller of the association table itself, is trivial. If it is the Controller of PontoDeColeta, follow my best answer that has examples of how to do.


POST EDIT

All right in its implementation, except this one:

@using (Html.BeginCollectionItem("TipoDeLixo"))

If @model of Create is PontoDeColeta, the name of Collection cannot be "Junk type" because there is no browsing property with this name here:

public class PontoDeColeta
{
    public Guid Id { get; set; }

    [Required(ErrorMessage = "O prenchimento é obrigatório")]
    [Display(Name = "Nome popular*: ")]
    public string NomePopular { get; set; }

    public ICollection<PontoDeColetaTipoDeLixo> PontosDeColetaTiposDeLixo { get; set; } // <---- Você quis usar esta propriedade, provavelmente
 }

So it should be:

@using (Html.BeginCollectionItem("PontosDeColetaTiposDeLixo"))

POST EDIT 2

The object going to the Partial View is void. PontosDeColetaTiposDeLixo needs to be initialized to work.

// GET: PontosDeColeta/Create
public ActionResult Create()
{
    var pontoDeColeta = new PontoDeColeta 
    {
        UsuarioResponsavel = User.Identity.GetUserId(),
        PontosDeColetaTiposDeLixo = new List<PontoDeColetaTipoDeLixo>()
    };

    return View(pontoDeColeta);
}

The error message is kind of tricky. How PontosDeColetaTiposDeLixo was null, MVC tries to pass the parent object. It is a fairly common error when using Partials.

  • 1

    I edited the question complementing everything I did based on your answer.

  • @Victorprojetofinal Almost certain. See the expansion of my answer.

  • I made the change:

  • I made the change: @using (Html.BeginCollectionItem("PontosDeColetaTiposDeLixo")) and still noticed that in the controller action was calling the wrong Partialview. The correct is return PartialView("_LinhaTiposDeLixo", new PontoDeColetaTipoDeLixo { Id = Guid.NewGuid() });. But still the system has the same error. I have compared the whole code and saw nothing wrong.

  • Are you sure it’s this message, exactly written this way? The model item passed into the dictionary is of type 'IdentitySample.Models.PontoDeColeta', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[Identitysample.Models.Pontodecoletatipodelixo]'. ` I think the types are different. I need the new error message.

  • I took a print now and edited the question. Are not the attributes of the classes? It’s the only difference I saw from your example.

  • The error is clear. What you are passing to Partial is a variable of the type PontoDeColeta, And as much as the name is right, you’re not passing IEnumerable<PontoDeColetaTipoDeLixo>. Edit your question and also enter the code for Controller.

  • I put the complete controller code

  • @Victorprojetofinal You fell into one of the most treacherous messages in ASP.NET MVC. See my issue.

Show 4 more comments

Browser other questions tagged

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