Почему этот вызов 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, но 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>
Решил проблему! Спасибо за любого, кто почесал голову над этим, надеюсь, эта поломка странной ошибки будет кому-то полезна.