A vista parcial renderizada não corresponde ao modelo

Então, eu escrevi um código para permitir adicionar e remover elementos de uma coleção dinamicamente no ASP.NET MVC usando AJAX. Adicionar novos itens à coleção funciona conforme o esperado, mas a remoção não. A coleção de modelos é atualizada conforme o esperado (o item apropriado é removido pelo índice), mas o HTML renderizado mostra consistentemente que o último item foi removido (em vez do item no índice especificado).

Por exemplo, digamos que tenho os seguintes itens:

FooBarraBaz

Quando clico em "remover" ao lado do item chamado "Foo", espero que o HTML renderizado resultante tenha a seguinte aparência:

BarraBaz

Quando depuro através da ação do controlador, esse parece ser o caso, pois a coleção Names no modelo contém apenas esses itens. No entanto, o HTML renderizado retornado ao meu manipulador AJAX é:

FooBarra

Eu pensei que o problema tivesse a ver com cache, mas nada que eu tentei (diretiva OutputCache, definindo cache: false em $ .ajax, etc) está funcionando.

Aqui está o código:

DemoViewModel.cs
namespace MvcPlayground.Models
{
   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Text;
   using System.Threading.Tasks;

   public class DemoViewModel
   {
      public List<string> Names { get; set; }

      public DemoViewModel()
      {
         Names = new List<string>();
      }
   }
}
DemoController.cs

O problema aparente aqui está no método RemoveName. Posso verificar que a propriedade Model do PartialViewResult reflete o estado da coleção como eu esperava, mas, uma vez renderizado para o cliente, o HTML NÃO é o que eu esperava.

namespace MvcPlayground.Controllers
{
   using MvcPlayground.Models;
   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Web;
   using System.Web.Mvc;

    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            var model = new DemoViewModel();
            return View(model);
        }

        [HttpPost]
        public ActionResult AddName(DemoViewModel model)
        {
            model.Names.Add(string.Empty);

            ViewData.TemplateInfo.HtmlFieldPrefix = "Names";
            return PartialView("EditorTemplates/Names", model.Names);
        }

        [HttpPost]
        public ActionResult RemoveName(DemoViewModel model, int index)
        {
           model.Names.RemoveAt(index);

           ViewData.TemplateInfo.HtmlFieldPrefix = "Names";
           var result = PartialView("EditorTemplates/Names", model.Names);
           return result;
        }
    }
}
Names.cshtml

Este é o modelo de editor que estou usando para exibir a lista de nomes. Funciona como esperado ao adicionar um novo item à coleção.

@model List<string>

@for (int i = 0; i < Model.Count; i++)
{
   <p>
      @Html.EditorFor(m => m[i]) @Html.ActionLink("remove", "RemoveName", null, new { data_target = "names", data_index = i, @class = "link link-item-remove" })
   </p>
}
Index.cshtml

Esta é a página inicial carregada, nada muito complicado aqui.

@model MvcPlayground.Models.DemoViewModel

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>


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

    <div class="form-horizontal">
        <h4>Demo</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

       <div class="container-collection" id="names">
         @Html.EditorFor(m => m.Names, "Names")
       </div>
        @Html.ActionLink("Add New", "AddName", "Demo", null, new { data_target = "names", @class = "btn btn-addnew" })

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

Este script lida com as chamadas para AddName e RemoveName. Tudo aqui funciona como eu esperava.

$('form').on('click', '.btn-addnew', function (e) {
   e.preventDefault();

   var form = $(this).closest('form');
   var targetId = $(this).data('target');

   var target = form.find('#' + targetId);
   var href = $(this).attr('href');

   $.ajax({
      url: href,
      cache: false,
      type: 'POST',
      data: form.serialize()
   }).done(function (html) {
      target.html(html);
   });
});

$('form').on('click', '.link-item-remove', function (e) {
   e.preventDefault();

   var form = $(this).closest('form');
   var targetId = $(this).data('target');

   var target = form.find('#' + targetId);
   var href = $(this).attr('href');

   var formData = form.serialize() + '&index=' + $(this).data('index');

   $.ajax({
      url: href,
      cache: false,
      type: 'POST',
      data: formData
   }).done(function (html) {
      target.html(html);
   });

});

questionAnswers(1)

yourAnswerToTheQuestion