Solucionado (ver solução)
Solucionado
(ver solução)
19
respostas

Dúvida para editar um objeto

Galera, acompanhei o curso, e ele não ensina bem como editar um objeto e atualizá-lo no banco usando o entity framework core, eu sei como enviar um objeto pra exibir e preencher na view os campos de input para o usuario. Mas e para recebê-lo no método da controller para atualizar no banco? Eu to trabalhando num projeto que fiz exibir os dados, mas na hora de atualizar não estou sabendo justamente por causa do id do objetos, pois não é um só num formulário de cadastro...

19 respostas

Oi Lucas, tudo bom?

Se a gente der uma olhada lá nos arquivos do namespace DAO, como, por exemplo, o ProdutosDAO encontramos os métodos de adição, lista, buscaPorId e o Atualiza:

public void Atualiza(Produto produto)
        {
            using (var contexto = new EstoqueContext())
            {
                contexto.Entry(produto).State = System.Data.Entity.EntityState.Modified;
                contexto.SaveChanges();
            }
        }

Aqui a gente ta recebendo um produto, criando o contexto do Estoque e dizendo para nosso contexto que o produto foi alterado:

contexto.Entry(produto).State = System.Data.Entity.EntityState.Modified;

Assim, o entity saberá que aquela entidade deverá ser atualizada no banco =)

Depois apenas salvamos as mudanças com o método SaveChanges();

Como já temos esse código isolado, para atualizar uma entidade no controller, bastaria passa-la para esse método. Algo como:

    [HttpPost]
    // aqui estamos recebendo o produto todo, pois o ModelBind já converte pra gente =)
        public ActionResult EditaProduto(Produto produto)
        {
            if (ModelState.IsValid) { 
            // aqui também podemos isolar essa instancia no construtor do controller ou injetar como uma dependenecia
                    ProdutoDAO produtoDao = new ProdutoDAO();
            produtoDao.Atualiza(produto);
                    return RedirectToAction("Index");
                }
            } else
            {
            // devolve o(s) erro(s) de validação
            }
        }

Qualquer duvida é só falar.

Abraço e bons estudos =)

Olá Lucas,

para atualizar um objeto usando o entity framework primeiro você precisa do objeto preenchido completo, incluindo o seu id. Depois, a partir do context existe um método chamado de Entry, que recebe como um parâmetro um objeto que será mapeado com o seu banco de dados. Com o retorno deste método, você consegue alterar a propriedade State e indicar para o entity que o novo estado para este objeto é que ele tem que ser modificado no banco de dados System.Data.Entity.EntityState.Modified. Então para fazer o update no banco o código fica mais ou menos assim:

  contexto.Entry(variavel_que_referencia_o_ objeto).State = System.Data.Entity.EntityState.Modified;

Depois quando ocorrer o SaveChanges o Entity sozinho faz o update no banco a partir das informações contidas neste objeto dado o id preenchido nele.

Olá pessoal, valeu por me responder, consegui entender a parte do entity, mas e pra eu receber esse objeto com todos os dados da view? No caso como falei em um só formulário tenho varias tabelas para serem atualizadas, por exemplo:

View de edição:

 public ActionResult Editar(long id)
        {

            Dictionary<Vendedor, Dictionary<string, Object>> dadosVendedoresEdicao = dao.buscaTodosVendedores();

            foreach (KeyValuePair<Vendedor, Dictionary<string, Object>> par in dadosVendedoresEdicao)
            {
                Vendedor vendedor = par.Key;
                if (vendedor.IdVendedor == id)
                {
                    ViewBag.Vendedor = vendedor;

                    // todos os dados como email, endereço, telefone, etc...
                    ViewBag.DadosVendedor = par.Value;
                }
            }

            return View();
        }

Método que recebe dados do formuláro:

[HttpPost]
        public ActionResult EditarOpcoesBasicas(Vendedor vendedor, /*Usuario usuario,*/ Email email,
            Telefone telefone, Telefone celular, Endereco endereco, TipoPessoa tipoPessoa,
            TipoVendedor tipoVendedor)
        {
        //código para enviar para o entity
        return View();
        }

Só que no momento de receber do formulário ele não está recebendo todos os dados do vendedor, na view estou colocando os parâmetros pela tag name, por exemplo o input de nome do vendedor tem a tag name="vendedor.Nome", e além disso tem outras informações ocultas que são armazenadas como a DataInclusao do vendedor e não vai no formulário, como faço pra receber todas as infos dele?

Olá Lucas,

Neste caso, é mais fácil se você trabalhar com o conceito de View Model. A ideia é que neste momento a sua tela/view difere de qualquer entidade que existe no seu sistema, na verdade ela passou a ser um conjunto de dados de várias entidades diferentes.

Para trabalhar com isso, a ideia é você criar uma nova classe que contenha estes dados específicos da sua view, a tela em si pode ter como tipo forte esta classe nova, o seu controller receberá só um objeto dela e a classe contenha métodos, como um CriaVendedor() , que gerem os modelos que serão de fato cadastrados no seu banco e com as informações preenchidas de acordo com os dados que recebeu da tela.

Aqui um link com um exemplo de View Model.

Sim eu criando algo do tipo:

public class VendedorModelView
    {
        public Vendedor Vendedor { get; set; }
        public TipoVendedor TipoVendedor { get; set; }
        public Status Status { get; set; }
        public Email Email { get; set; }
        public Endereco Endereco { get; set; }
        public Telefone Telefone { get; set; }
        public Telefone Celular { get; set; }
        public TipoPessoa TipoPessoa { get; set; }
    }

e projetando na view, mas na hora de receber esses dados completos daria certo? porque parece que seria a mesma coisa pra recuperar certos dados eles não viria todos

Se o seu controller receber como parâmetro o VendedorModelView funciona. Só tem que tomar cuidado com o public Vendedor Vendedor { get; set; }, dado que usando o modelo inteiro está assumindo que o formulário terá também todos os dados do vendedor. Se isso não for verdade, você terá que criar propriedades uma para cada informação do Vendedor que precisará receber.

Eu tentei fazer isso e me deparei com esse erro:

   em CallSite.Target(Closure , CallSite , Object )
   em System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   em ASP._Page_Views_Vendedor_Editar_cshtml.Execute() na C:\Users\Lucas\source\repos\Recupera\ControlMVC\Views\Vendedor\Editar.cshtml:linha 269
   em System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   em System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   em System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   em System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
   em System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)
   em System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   em System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
   em System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   em System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)

Nada ver, ele ta dizendo que "Não é possível fazer associação em tempo de execução em uma referência nula ", to vendo a saída e ele gravou sim no meu objeto Model View

Ele dá esse erro

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Não é possível fazer associação em tempo de execução em uma referência nula'

nessa parte do código, quando fecha a chave do código else:

@{
                                                                        if (Model.TipoPessoa.IdTipoPessoa == 1)
                                                                        {
                                                                            <div class="i-checks col-lg-4 col-md-5 col-sm-6 col-lg-offset-2 col-md-offset-1 ">
                                                                                <label for="pfUser"><input type="radio" class="radio-primary" checked value="1" id="pf" name="dados.TipoPessoa.IdTipoPessoa"> Pessoa Fisíca</label>
                                                                            </div>
                                                                            <div class="i-checks col-lg-4 col-md-5 col-sm-6 col-lg-offset-2 col-md-offset-1 ">
                                                                                <label for="pjUser"><input type="radio" id="pjUser" name="dados.TipoPessoa.IdTipoPessoa" value="2"> Pessoa Jurídica</label>
                                                                            </div>
                                                                        }
                                                                        else
                                                                        {
                                                                            <div class="i-checks col-lg-4 col-md-5 col-sm-6 col-lg-offset-2 col-md-offset-1 ">
                                                                                <label for="pfUser"><input type="radio" class="radio-primary" value="1" id="pf" name="dados.TipoPessoa.IdTipoPessoa"> Pessoa Fisíca</label>
                                                                            </div>
                                                                            <div class="i-checks col-lg-4 col-md-5 col-sm-6 col-lg-offset-2 col-md-offset-1 ">
                                                                                <label for="pjUser"><input type="radio" id="pjUser" checked name="dados.TipoPessoa.IdTipoPessoa" value="2"> Pessoa Jurídica</label>
                                                                            </div>
                                                                        }
                                                                    }

Se eu comento vai para outra parte, e se comentar denovo vai para essa :

@{
    Layout = null;
}

Esse erro indica que provavelmente o Model está nulo quando vai carregar a tela. Na hora que você acessa a tela a primeira vez, o seu controller manda um objeto no return View para ser usado como Model? Porque mesmo quando você acessará a tela a primeira vez, como faz o

 if (Model.TipoPessoa.IdTipoPessoa == 1)

ele precisa do Model preenchido.

Olha o método da view Editar:

public ActionResult Editar(long id)
        {

            VendedorDAO dao = new VendedorDAO();

            Dictionary<Vendedor, Dictionary<string, Object>> dadosVendedoresEdicao = dao.buscaTodosVendedores();

            VendedorModelView dados = null;

            foreach (KeyValuePair<Vendedor, Dictionary<string, Object>> par in dadosVendedoresEdicao)
            {
                Vendedor vendedor = par.Key;
                if (vendedor.IdVendedor == id)
                {
                    Status status = (Status)par.Value["status"];
                    TipoVendedor tipoVendedor = (TipoVendedor)par.Value["tipoVendedor"];
                    Telefone telefone = (Telefone)par.Value["telefone"];
                    Telefone celular = (Telefone)par.Value["celular"];
                    Email email = (Email)par.Value["email"];
                    Endereco endereco = (Endereco)par.Value["endereco"];
                    TipoPessoa tipoPessoa = (TipoPessoa)par.Value["tipoPessoa"];

                    dados = new VendedorModelView(vendedor, tipoVendedor, status, email, endereco, telefone, celular, tipoPessoa);

                    break;
                }
            }

            if (dados != null)
            {
                return View(dados);
            }
            else
            {
                throw new Exception("Erro ao pegar dados do vendedor");
            }
        }

Disso que você está falando né, estou enviando para a view o model no return.

Era isso, estranho. Me manda toda a view de Editar e a classe TipoPessoa para eu dar uma olhada?

Eu consegui aqui agora, refiz tudo denovo, provavelmente era alguma parte da digitação no html, mas estou naquele problema amigo que não consigo pegar os dados completos das tabelas mesmo através do Model, vou te mandar o código da view e do controller pra vc ver, ele não está pegando os dados da tabela que não está no formulário:

[Link do código] (https://gist.github.com/LucasMAlbano/43bb0996049f95f344742160b0630579)

Se olhar no código, vai ver que o único que sai Id é o vendedor, mas nem isso ele está copiando para o método EditarOpcoesBasicas através do Model somente as outras propriedades que estão no formulario

Um ponto que pode estar atrapalhando são os name dos inputs que você não precisa marcar dados., pode deixar somente name="TipoVendedor.IdTipoVendedor" por exemplo que ele já deveria entender que ele precisa preencher a propriedade TipoVendedor.

Outra coisa que eu notei em algumas das tags select as suas option não estavam com o value preenchido. Isso pode explicar algumas das informações que acabaram não sendo enviadas.

então Lucas, seguinte, isso pode ser uma pequena parte do problema, mas na hora de eu receber os objetos no método de post do formulário ele vem sem outras propriedades que não está no formulário como por exemplo a data de inclusao do Endereco, Email, etc... são dados que usamos como historico aqui, como recupero todos mesmo não estando no formularios?

Adorei a discussão!

Então depende da sua regra de negócio. As vezes a própria View Model dentro do método que criará o objeto pode definir um valor para aquela propriedade que não foi preenchida pelo formulário. Por exemplo, uma informação como data da última alteração poderia ser preenchida automaticamente com a data de hoje.

Se for uma informação que já estava no banco, você usar o context ou o DAO para recuperar estas informações antigas e preencher no modelo na hora que for criá-lo. Outra opção seria guardar estes dados na session do usuário para poder recuperar depois. E uma terceira você poderia no próprio VendedorModelView ter estas propriedades que não vão aparecer no formulário visualmente para o usuário, mas criar inputs do tipo hidden que vão enviar as informações novamente para o controller na requisição.

Acho que me familiarizei mais com a terceira opção, acho que você entendeu tipo tenho varias entidades para serem alteradas que fazer relacionamento com esse vendedor, tipo Email tem EnderecoEmail, DataInclusao, IdEmail, só que não coloco essas informações no formulario e na hora de recuperar vem tudo vazio, ai colocando o input hidden resolveria o problema certo? Só que tenho muitas propriedades pra colocar como input hidden, teria outro jeito mais facil pra nao ficar escrevendo esse tanto de código ou só assim mesmo? Mas qualquer coisa já vou fazer dessa forma mesmo

solução!

Então para não ter muito código na View que consigo pensar agora rápido seria guardando na Session mesmo ou recuperando os dados do banco na hora de criar o objeto modelo.

Para simplificar e organizar um pouco sua view você pode trabalhar com partials views para isolar um pouco as partes deste formulário. Por exemplo, ter uma partial view de_DadosVendedor.cshtml que só tem os inputs referentes ao vendedor, outro chamado _DadosContato.cshtml que teria os inputs de Email, Telefone, Endereco, etc.

Entendi, valeu Lucas, agora vai! rsrsrsrs