Сохранение данных формы с использованием Web API и AngularJS (выберите список)

У меня есть форма, которая отображает данные персонала и дает возможность обновлять информацию о персонале и сохранять изменения в моей базе данных. См. Форму ниже:

Рисунок 1. Форма информации о персонале

Поля, привязанные к текстовым полям, у меня нет проблем с обновлением. Хотя, когда дело доходит до списка выбора HTML (выпадающего списка), я не могу сохранить только что выбранное значение.

ПРИМЕЧАНИЕ. Я могу внести изменения в любое количество текстовых полей, а затем внести изменения в выбор в списке выбора, а остальная часть формы будет сохранена правильно, просто без изменений в списке выбора, чтобы он не отображался никуда не денутся.

Я не получаю никаких ошибок; Тем не менее, я вижу дополнительный запрос OPTIONS перед PUT, о котором я не уверен (так что любые дополнительные советы о том, почему это было бы также оценено).

Здесь моя форма:

<form name="personForm" novalidate ng-controller="PersonnelEditCtrl as vm">
<fieldset class="col-md-4">
    <legend>Basic Personnel Information</legend>
    <div class="form-group row" ng-class="{'has-error':personForm.inputLastName.$invalid && personForm.inputLastName.$dirty}">
        <label class="col-md-3 control-label" for="inputLastName">Last Name</label>
        <div class="col-md-4">
            <input class="form-control" id="inputLastName" name="inputLastName" 
                   type="text" placeholder="Last Name (required)" 
                   ng-model="vm.person.lastName" required ng-minlength="1" ng-maxlength="30" />
        </div>
        <span class="help-block" has-error">
            <span ng-show="personForm.inputLastName.$error.required">
                Last name is required.
            </span>
            <span ng-show="person.form.inputLastName.$error.minlength">
                Last name must be at least 1 character in length.
            </span>
            <span ng-show="person.form.inputLastName.$error.maxlength">
                Last name cannot exceed 30 characters in length.
            </span>
        </span>
    </div>
    <div class="form-group row" ng-class="{'has-error':personForm.inputFirstName.$invalid && personForm.inputFirstName.$dirty}">
        <label class="col-md-3 control-label" for="inputFirstName">First Name</label>
        <div class="col-md-4">
            <input class="form-control" id="inputFirstName" name="inputFirstName" 
                   type="text" placeholder="First Name (required)" 
                   ng-model="vm.person.firstName" required ng-minlength="1" ng-maxlength="30" />
        </div>
        <span class="help-block" has-error">
            <span ng-show="personForm.inputFirstName.$error.required">
                First name is required.
            </span>
            <span ng-show="person.form.inputFirstName.$error.minlength">
                First name must be at least 1 character in length.
            </span>
            <span ng-show="person.form.inputFirstName.$error.maxlength">
                First name cannot exceed 30 characters in length.
            </span>
        </span>
    </div>
    <div class="form-group row" ng-class="{'has-error':personForm.inputMiddleInitial.$invalid && personForm.inputMiddleInitial.$dirty}">
        <label class="col-md-3 control-label" for="inputMiddleInitial">Middle Initial</label>
        <div class="col-md-4">
            <input class="form-control" id="inputMiddleInitial" name="inputMiddleInitial" 
                   type="text" placeholder="Middle Initial (required)" 
                   ng-model="vm.person.middleInitial" required ng-minlength="1" ng-maxlength="1" />
        </div>
        <span class="help-block" has-error">
            <span ng-show="personForm.inputMiddleInitial.$error.required">
                Middle initial is required.
            </span>
            <span ng-show="person.form.inputMiddleInitial.$error.minlength">
                Middle initial must be at least 1 character in length.
            </span>
            <span ng-show="person.form.inputMiddleInitial.$error.maxlength">
                Middle initial cannot exceed 1 characters in length.
            </span>
        </span>
    </div>
    <div class="form-group row">
        <label class="col-md-3 control-label" for="inputDateOfBirth">Date of Birth</label>
        <div class="col-md-4">
            <input class="form-control" ng-model="vm.person.dob" type="date" />
        </div>
    </div>
    <div class="form-group row">
        <label class="col-md-3 control-label" for="selectPayband">Payband</label>
        <div class="col-md-4">

            <select id="selectPayband" name="selectPayband" 
                    ng-model="vm.person.payband" ng-options="payband.name for payband in vm.paybands track by payband.id">
            </select>
        </div>
    </div>
    <div class="form-group row">
        <div class="col-md-4">
            <span>
                <button class="btn btn-primary" style="width:80px; margin-right:10px" 
                        ng-click="vm.submit()" ng-disabled="personForm.$invalid">Save</button>
            </span>
            <span>
                <button class="btn btn-default" style="width:70px" 
                        ng-click="vm.cancel(personForm)">Cancel</button>
            </span>
        </div>
    </div>
    <div class="form-group row" ng-show="vm.message">
        <div class="col-md-6">
            <pre style="font: inherit">{{ vm.message }}</pre>
        </div>
    </div>
</fieldset>

personnelEditCtrl.js

angular
.module("personnelService")
.controller("PersonnelEditCtrl",
             PersonnelEditCtrl);

function PersonnelEditCtrl(personnelResource, paybandResource, $filter) {
var vm = this;
vm.person = {};
vm.message = '';
vm.paybands = [];

paybandResource.query(function (data) {
    vm.paybands = $filter('orderBy')(data, 'Name');
});

personnelResource.get({ id: 2 },
    function (data) {
        vm.person = data;
        vm.person.dob = new Date(vm.person.dob);
        vm.originalPerson = angular.copy(data);
    });

if (vm.person && vm.person.personId) {
    vm.title = "Edit: " + vm.person.firstName + " " + vm.person.lastName;
}
else {
    vm.title = "New Person";
}

vm.submit = function () {
    vm.message = '';
    if (vm.person.personId) {
        vm.person.$update({ id: vm.person.personId },
            function (data) {
                vm.message = '... Save Complete';
            })
    }
    else {
        vm.person.$save(
            function (data) {
                vm.originalPerson = angular.copy(data);
                vm.message = '... Save Complete';
            })
    }
};

vm.cancel = function (editForm) {
    editForm.$setPristine();
    vm.person = angular.copy(vm.originalPerson);
    vm.message = "";
};
}

personnelResource.js

(function () {
"use strict";

angular
    .module("common.services")
    .factory("personnelResource",
            ["$resource",
             "appSettings",
                personnelResource])

function personnelResource($resource, appSettings) {
    return $resource(appSettings.serverPath + "/api/people/:id", null,
        {
            'update':{method:'PUT'}
        });
}
}());

paybandResource.js

(function () {
"use strict";

angular
    .module("common.services")
    .factory("paybandResource",
            ["$resource",
             "appSettings",
                paybandResource])

function paybandResource($resource, appSettings) {
    return $resource(appSettings.serverPath + "/api/paybands/:id");
}
}());

Структура базы данных:

dbo.People

PersonId : int (PK)
FirstName : string
MiddleInitial: string
LastName : string
DateOfBirth: datetime
PaybandId : int (FK)

dbo.Paybands

Id : int (PK)
Name : string

Вероятно, это не имеет никакого отношения к этому, но я также отправлю код своего API-интерфейса API для "Люди":

using System.Linq;
using System.Web.Http;
using CPS.WebAPI.Models;
using System.Web.Http.Cors;
using System.Data.Entity;

namespace CPS.WebAPI.Controllers
{
[EnableCorsAttribute("http://localhost:53265", "*", "*")]
public class PeopleController : ApiController
{
    private CPS_Context db = new CPS_Context();

    public IQueryable<Person> GetPeople()
    {
        return db.Person;
    }

    public Person Get(int id)
    {
        Person person;

        if (id > 0)
        {
            var people = db.Person;
            person = people.FirstOrDefault(p => p.PersonId == id);
        }
        else
        {
            person = db.Person.Create();
        }

        return person;
    }

    public void Post([FromBody]Person person)
    {
        CPS_Context db = new CPS_Context();
        var newPerson = db.Person.Add(person);
        db.SaveChanges();
    }

    public void Put(int id, [FromBody]Person person)
    {
        CPS_Context db = new CPS_Context();
        db.Entry(person).State = EntityState.Modified;
        var updatedPerson = db.SaveChanges();
    }

    public void Delete(int id)
    {

    }
}
}

Не обращайте внимания на минимальный код для веб-API, я просто делаю все с нуля и делаю все как можно меньше, чтобы заставить его работать в среднем.

Большое спасибо за любую помощь, которую вы можете предоставить, помогая мне сохранить выбранный вариант в моем списке выбора. Пожалуйста, дайте мне знать, если у вас есть какие-либо дополнительные вопросы или вам нужна дополнительная информация. Я старался быть максимально тщательным. Еще раз спасибо за любую помощь, которую любой может предоставить!

Ответ 1

Моя проблема оказалась в том, что данные JSON включали свойства навигации. Ошибка обновления произошла из-за конфликта ссылочной целостности.

Решение моей проблемы состояло в том, чтобы обновить мою модель данных Entity Framework, чтобы исключить свойства навигации из JSON. Я сделал это, добавив атрибут [JsonIgnore] следующим образом:

[ForeignKey("Payband")]
public virtual int PaybandId { get; set; }

[JsonIgnore]
public virtual Payband Payband { get; set; }