Почему этот вызов AJAX.Helper Post получает ошибку Enity Framework, а "Get" - нет?

В качестве учебного проекта у меня есть проект MVC & Typescript и проект Web 2.0 и объектный каркас, проект MVC пытается общаться с проектом Web 2.0, и у меня странная ошибка.

Это мой контроллер плеера Web API 2.0:

 public class PlayerController : ApiController
{

    // GET api/<controller>/5
    public Player Get(int? id)
    {
        if (id == null || id == -1)
        {

            var player = new Player();
            LeaderBoardContext.Current.Players.Add(player);
            LeaderBoardContext.Current.SaveChanges();
            return player;
        }
        return LeaderBoardContext.Current.Players.FirstOrDefault(x => x.PlayerId == id);
    }


    // PUT: api/Scores/5
    [ResponseType(typeof(void))]
    public IHttpActionResult PostPlayer(LearningCancerAPICalls.Models.Player player)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var model = LeaderBoardContext.Current.Players.FirstOrDefault(x => x.PlayerId == player.PlayerId);
        LeaderBoardContext.Current.Entry<Player>(player).State = EntityState.Modified;

        try
        {
            LeaderBoardContext.Current.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {

        }
        return StatusCode(HttpStatusCode.NoContent);
    }
}

К этому моменту он прошел несколько итераций, в какой-то момент он инициализировал свой собственный контекст БД в верхней части файла, но во время публикации он был таинственно нулевым. Так что теперь я использую стиль, который мы используем в других проектах, который выглядит следующим образом:

public static LeaderBoardContext Current
    {
        get
        {
            try
            {
                //added because 'HttpContext.Current.Items["_EntityContext"] ' was mysteriously comming back null....
                if (HttpContext.Current.Items["_EntityContext"] == null)
                {
                    HttpContext.Current.Items["_EntityContext"] = new LeaderBoardContext();
                }

                var obj = HttpContext.Current?.Items["_EntityContext"] as LeaderBoardContext;
                return obj;
            }
            catch (Exception) //should only get here if using background task
            {
                return null;
            }
        }
    }

Итак, первая странность в посте - контекст настаивал на том, чтобы быть нулевым, но принуждение его не быть нулевым с помощью сложного метода, приведенного выше, не сильно улучшило ситуацию. Обратите внимание, что первый EF-вызов, который я сейчас вставил, в основном совпадает с GET:

var model = LeaderBoardContext.Current.Players.FirstOrDefault(x => x.PlayerId == player.PlayerId);

Я вызвал GET в обоих стилях (с -1, с действительным идентификатором), и он работает нормально, но POST до сих пор приводил к этой ошибке:

ошибка ef

Что я обычно ассоциирую с плохо инициализированным проектом EF, но GET работает! он делает именно то, что должен делать. Я даже пытался отправить на контроллер EF scafold с другой моделью, и у меня была та же проблема!

Основное различие между ними (кроме GET/POST) заключается в том, как я их называю, вот как я использую GET:

    var playerId = -1;
var activeUser:Player;
function initPlayerOnGameStart() {
    if (host === undefined) {
        host = 'http://localhost:52316';
    }
    if (playerId === undefined) {
        playerId = -1;
    }
    var uri = host + '/api/Player/' + playerId;
    jQuery.getJSON(uri).done(data => {
        activeUser = data;
        playerId = activeUser.PlayerId;
    });

}

В чистом Typescript Json позвоните. Чтобы сделать POST, я экспериментирую с AJAX.Helper:

    @model LearningCancerAPICalls.Models.Player

<a id="contact-us">Share Score!</a>
<div id="contact-form" class="hidden" title="Online Request Form">
    @using (Ajax.BeginForm("", "", null, new AjaxOptions
    {
        HttpMethod = "POST", Url = "/api/Player",
        OnSuccess ="OnSuccess",
        OnFailure ="OnFailure" 
    }, new { id = "formId", name = "frmStandingAdd" }))
    {
        @Html.LabelFor(m => m.PlayerName);
        @Html.TextBoxFor(m => m.PlayerName);
        @Html.LabelFor(m => m.Email);
        @Html.TextBoxFor(m => m.Email);
        @Html.HiddenFor(m => m.PlayerId);
        @Html.Hidden( "PlayerId");
         <input type="submit" name="submit" value="Ok" />

    }
</div>

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script>
    function OnSuccess() {
        alert('Success');
    }
    function OnFailure(ajaxContext) {

        alert('Failure');
    }

</script>

Где я установил PlayerID из машинописи. Это успешно вызывает почту, но вылетает при первом использовании EF. Другая особенность в том, что если я поставлю отладку на пост. Модель не выглядит корректной, поскольку, когда я наведу на нее курсор, она показывает себя как модель Player, ошибки приведения не было, но она не позволяет мне расширять ее свойства. Если я использую переменные или непосредственное окно для проверки переменных, то все они в порядке. Но я думал, что это стоит упомянуть.

Позже я собираюсь попробовать чистый вызов ajax, чтобы увидеть, разрешит ли он его, но я не понимаю, почему Ajax.helper здесь виноват, он технически делает свою работу, и ошибка не связана с моделью, которую я можно увидеть.

ОБНОВЛЕНИЕ 1 Итак, я попробовал чистый вызов ajax:

Html:

 Name: <input type="text" name="fname" id="userName"><br />
        <button onclick="postJustPlayer()"> Ok </button>

Машинопись

function postJustPlayer() {
    let level = jQuery("#chooseGridLevel").val();
    let name = jQuery("#userName").val();
    let uri = host + '/api/Player';
    let player: Player = <Player>{};


    player.Email = "Testing";
    player.PlayerName = name;
    jQuery.post(uri, player);
}

И это работает!?? Я понятия не имею, почему работает чистый jQuery, конечно же, что касается EF, он делает то же самое? почему пост AJAX.helper будет другим...

1 ответ

Решение

Решил это! Это была настоящая головоломка, решаемая только тогда, когда я копался в сетевых данных (инструменты ftw).

Для других новичков в Интернете я объясню, как я нашел путь к этой проблеме. В Chrome Dev Tools есть вкладка Сеть, в которой будут отображаться ваши веб-запросы и ответы. Итак, открыв его после нажатия моей кнопки OK, я вижу это для моего чистого вызова AJAX:

Затем я могу сравнить это с тем, когда я нажал "Отправить" в моей форме AJAX:

Я копирую и вставляю их оба в KDiff3, который выделил одно ОЧЕНЬ важное отличие адреса локального хоста!

Вы заметите, что в чисто ajax-запросе, который я указал, это объясняется тем, что, как я уже упоминал, мой проект web api и проект моего веб-сайта разделены, поэтому они находятся на разных хостах!

Таким образом, на самом деле вспомогательный вызов AJAX никогда не должен был работать, но, как это случилось за день до того, как я решил, что мне нужна модель из моего проекта API в моем проекте веб-сайта, и в то время я подумал: "Я, вероятно, не должен включать свой проект API в качестве ссылки на моем главном сайте, но только сейчас....". Так что это приводит к вызову API с неправильным хостом, действующим! Конечно, с принципиальной разницей, что EF не был установлен на ЭТОМ хосте.

Так что бедный старый помощник по ajax получил много моих проклятий за ошибку, к которой мог привести только особый тип идиота. Изменение помощника ajax для использования полного пути:

    @model LearningCancerAPICalls.Models.Player

<a id="contact-us">Share Score!</a>
<div id="contact-form" class="hidden" title="Online Request Form">
    @using (Ajax.BeginForm("", "", null, new AjaxOptions
    {
        HttpMethod = "POST", Url = "http://localhost:52316/api/Player",
        OnSuccess ="OnSuccess",
        OnFailure ="OnFailure" 
    }, new { id = "formId", name = "frmStandingAdd" }))
    {
        @Html.LabelFor(m => m.PlayerName);
        @Html.TextBoxFor(m => m.PlayerName);
        @Html.LabelFor(m => m.Email);
        @Html.TextBoxFor(m => m.Email);
        @Html.HiddenFor(m => m.PlayerId);
        @Html.Hidden( "PlayerId");
         <input type="submit" name="submit" value="Ok" />

    }
</div>

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script>
    function OnSuccess() {
        alert('Success');
    }
    function OnFailure(ajaxContext) {

        alert('Failure');
    }

</script>

Решил проблему! Спасибо за любого, кто почесал голову над этим, надеюсь, эта поломка странной ошибки будет кому-то полезна.

Другие вопросы по тегам