Webapi - TryUpdateModel or UpdateModel on an ApiController

1

I have the following situation, a Person class, with Id properties, Name, Birth, I want to implement a page that can update only the Person Name. My implementation should get a person by their Id, and change only the Name field.

I did the below implementation in MVC, but how could I do the same with WebApi, since I do not have the TryUpdateModel or UpdateModel. Implement functionality with this routine to make it easier to generalize in the future.

Controller code in MVC:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public JsonResult SetNome()
    {
        var sample = Pessoa.FindById(12);
        TryUpdateModel<Pessoa>(sample);
        return Json("sucesso");
    }
}

public class Pessoa
{
    [Required]
    public int Id { get; set; }

    [Required]
    public string Nome { get; set; }

    [Required]
    public DateTime? Nascimento { get; set; }

    public static Pessoa FindById(int id)
    {
        return new Pessoa() { Id = 12, Nome = "Meu Nome", Nascimento = Convert.ToDateTime("1989-01-31") };
    }
}

View Code:

<!DOCTYPE html>
<html>
<head>
    <script src="https://code.angularjs.org/1.4.3/angular.js"></script><script>varapp=angular.module("MainModule", []);

        var MainController = function ($scope, $http) {

            var url = 'http://localhost:47107/home/setnome';
            //var url = 'http://localhost:47107/api/values';

            $scope.pessoa = {};
            $scope.pessoa.nome = "xpto";

            $scope.enviar = function () {

                $http({
                    url: url,
                    method: "POST",
                    data: $scope.pessoa
                })
                .then(function (response) {
                    // success
                    console.log(JSON.stringify(response));
                },
                function (response) {
                    // failed
                    console.log(JSON.stringify(response));
                });
            }
        };

        app.controller("MainController", MainController);

    </script>
</head>

<body ng-app="MainModule">
    <div ng-controller="MainController">
        <input id="pessoa.nome" ng-model="pessoa.nome" />
        {{pessoa.nome}}
        <button ng-click="enviar()">Enviar</button>
    </div>
</body>
</html>

Current code in WebApi:

public class ValuesController : ApiController
{
    public void Post([FromBody]Pessoa pessoa)
    {
        var sample = Pessoa.FindById(12);

        sample.Nome = pessoa.Nome;
    }
}

I'd like to have to abstract the fill of the Name field, as it did in the MVC, with the UpdateModel, it identifies which fields were sent by View and only fill them, the others it keeps original. With WebApi I have to do this manually.

    
asked by anonymous 29.09.2015 / 16:17

2 answers

1

Ricardo, I solve this problem in a different way.

Initially, my Action or Operation does not receive an Entity / Model, but rather a DTO.

Receiving a DTO, I would do this treatment, which you want, in my repository.

Example of a Repository method with this feature:

public void Alterar(TDTO DTO)
    {           
            var entidadeSemAlteracao = Context.Set<Compra>().Find(DTO.sID);
            DTO.ToEntity(entidadeSemAlteracao);
            Context.SaveChanges();            
    }

ToEntity is an extension in my DTO, calling AutoMapper.

public static TDestino ToEntity<TOrigem, TDestino>(this TOrigem DTO, TDestino entidadeDestino) where TDestino : EntidadeBase
    {
        var result = Mapper.Map(DTO, entidadeDestino);
        return result;
    }

Everything happens when the extension is called, which will leave properties like Modified and SaveChanges () will only change those properties.

What do you think?

Is this what you wanted?

    
30.09.2015 / 06:53
1

Sharing knowledge for anyone going through the same problem. Remembering that the purpose of this function is not to validate the entity and only to fill (complement) an entity coming from the database with the data that came from the view.

    protected virtual void UpdateModel<T>(T original, bool overrideForEmptyList = true)
    {
        var json = ControllerContext.Request.Content.ReadAsStringAsync().Result;
        UpdateModel<T>(json, original, overrideForEmptyList);
    }

    private void UpdateModel<T>(string json, T original, bool overrideForEmptyList = true)
    {
        //faz um comportamento similar ao do UpdateModel do Controller, onde sobreescreve as propriedades Enumarables,
        //caso no json a mesma esteja preenchida, se ela estiver vazia (não nula), sobreescreve também a não ser que,
        //o parametro overrideForEmptyList seja false
        var newValues = JsonConvert.DeserializeObject<T>(json);            
        foreach (var property in original.GetType().GetProperties())
        {
            var isEnumerable = property.PropertyType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));

            if (isEnumerable && property.PropertyType != typeof(string))
            {
                var propertyOriginalValue = property.GetValue(original, null);
                if (propertyOriginalValue != null)
                {
                    var propertyNewValue = property.GetValue(newValues, null);

                    if (propertyNewValue != null && (overrideForEmptyList || ((IEnumerable<object>)propertyNewValue).Any()))
                    {
                        property.SetValue(original, null);
                    }
                }
            }
        }

        JsonConvert.PopulateObject(json, original);
    }

    public void Post()
    {           
        var sample = Pessoa.FindById(12);
        UpdateModel(sample);            
    }
    
29.09.2015 / 23:23