Для получения Id при окончании действия HttpPost (ADO.NET и WEB API) нужно перезагружать страницу
Я использую WebApi(ADO.NET и JS с FetchApi) для формы и таблицы.
Как видно из картинки, имеется Id 79 и после него идёт Id = 0(только что создал новую строку). Но после того как я перезагружу страницу Id примет нормальное значение. Стоит уточнить что с Ef Core всё происходит гладко
Post метод в контроллере:
[HttpPost]
public async Task<ActionResult<FormAbsence>> Create(FormAbsence model)
{
if (model != null)
{
var result = await _changeDataRepository.CreateDataAsync(model);
if (result == 0)
return this.StatusCode((int)HttpStatusCode.InternalServerError);
return Ok(model);
}
return BadRequest();
}
Метод CreateDataAsync():
public async Task<int> CreateDataAsync(FormAbsence model)
{
_context.Connection();
int countChangeData = 0;
if (model == null)
return countChangeData;
var sqlExpression =
"INSERT INTO FormAbsence (reason, start_date, duration, discounted, description)" +
$" VALUES ({((int)model.Reason)}, '{model.StartDate.Date}', {model.Duration}, '{model.Discounted}', '{model.Description}')";
using (SqlConnection connection = new SqlConnection(_context.ConnectString))
{
await connection.OpenAsync();
SqlCommand command = new SqlCommand(sqlExpression, connection);
countChangeData = await command.ExecuteNonQueryAsync();
}
return countChangeData;
}
И главный класс с перечеслением:
public class FormAbsence
{
public int Id { get; set; }
public Causes Reason { get; set; }
public DateTime StartDate { get; set; }
public int Duration { get; set; }
public bool Discounted { get; set; }
public string Description { get; set; }
}public enum Causes
{
Отпуск,
Больничный,
Прогул
}
Что касается html.
<form name="absenceForm">
<input name="id" type="hidden" value="0" />
<div class="form-group col-md-5">
<label for="validationDefault01">Причина отсутствия:</label>
<select class="form-select" name="reason" id="validationDefault01" aria-label="Default select example" required>
<option value="0">Отпуск</option>
<option value="1">Больничный</option>
<option value="2">Прогул</option>
</select>
<div class="valid-feedback">
Все хорошо!
</div>
<div class="invalid-feedback">
Это поле обязательно
</div>
</div>
<div class="panel-body">
<button type="submit" id="submit" class="btn btn-primary">Сохранить</button>
<a id="reset" class="btn btn-primary">Сбросить</a>
</div>
</form>
в js я использую fetchApi:
async function CreateFormAbsence(Freason, FstartDate, Fduration, Fdiscounted, Fdescription) {
const response = await fetch("/api/Values", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json" },
body: JSON.stringify({
reason: parseInt(Freason, 10),
startDate: FstartDate,
duration: Fduration,
discounted: Fdiscounted,
description: Fdescription
})
});
if (response.ok) {
let result = await response.json();
document.querySelector("tbody").append(row(result));
reset();
console.log("создана новая форма");
}
и последняя функция row (добавление новой строки и заполнение её данными) и events для кнопок 'сохранить' и сбросить
function row(form) {
let tr = document.createElement('tr');
tr.setAttribute("data-rowid", form.id);
let idTd = document.createElement("td");
idTd.append(form.id);
tr.append(idTd);
let reasonTd = document.createElement("td");
reasonTd.append(form.reason);
tr.append(reasonTd);
let startDateId = document.createElement("td");
startDateId.append(form.startDate);
tr.append(startDateId);
let durationId = document.createElement("td");
durationId.append(form.duration)
tr.append(durationId);
let discountedId = document.createElement("td");
discountedId.append(form.discounted);
tr.append(discountedId);
let descriptionId = document.createElement("td");
descriptionId.append(form.description);
tr.append(descriptionId)}
document.forms["absenceForm"].addEventListener("submit", e => {
e.preventDefault();
form = document.forms["absenceForm"];
let id = form.elements["id"].value;
let reason = form.elements["reason"].value;
let startDate = form.elements["startDate"].value;
let duration = form.elements["duration"].value;
let discounted = form.elements["discounted"].value;
let description = form.elements["description"].value;
if (id == 0) {
CreateFormAbsence(reason, startDate, duration, discounted, description)
} else {
EditFormAbsecne(id, reason, startDate, duration, discounted, description);
}
});
Ответы (1 шт):
EF Core после добавления (INSERT) данных в БД сразу же выполняет получение (SELECT) ID из неё. А в рукопашном коде у вас я вижу только INSERT.
Это же нужно сделать и вам.
public async Task<int> CreateDataAsync(FormAbsence model)
{
if (model == null)
return 0;
var sqlExpression =
"INSERT INTO FormAbsence (reason, start_date, duration, discounted, description) " +
"VALUES (@reason, @startDate, @duration, @discounted, @description); " +
"SELECT scope_identity();";
using (var connection = new SqlConnection(_context.ConnectString))
{
await connection.OpenAsync();
using (var command = new SqlCommand(sqlExpression, connection))
{
command.Parameters.Add("reason", SqlDbType.NVarChar).Value = model.Reason;
command.Parameters.Add("startDate", SqlDbType.DateTime2).Value = model.StartDate.Date;
command.Parameters.Add("duration", SqlDbType.Int).Value = model.Duration;
command.Parameters.Add("discounted", SqlDbType.NVarChar).Value = model.Discounted;
command.Parameters.Add("description", SqlDbType.NVarChar).Value = model.Description;
int id = (int)(decimal)(await command.ExecuteScalarAsync());
model.Id = id;
return id;
}
}
}
Обязательно используйте параметризованные запросы! Это полностью избавит от sql-инъекций и повысит производительность.
Я не знаю, какие именно типы у вас в таблице, поэтому сами отредактируйте значения SqlDbType.
Аналогично, я не уверен в возвращаемом типе id. Укажите правильный.
Ваш метод возвращает Task<int> - что значит этот int? Я вернул id, но это не имеет большого смысла. Решайте сами.
Если включить логирование в EF Core, то при вставке данных будет сгенерирован примерно такой запрос (Orders и UserId - название таблицы и колонки в моей БД):
SET NOCOUNT ON;
INSERT INTO [Orders] ([UserId])
VALUES (@p0);
SELECT [Id]
FROM [Orders]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
Как видим, сразу после вставки осуществляется получение только что сгенерированного айдишника функцией scope_identity() и это значение возвращается. И EF Core обновляет Id в сущности.
Отмечу, что SCOPE_IDENTITY - это функция Sql Server. При работе с другой СУБД нужно будет использовать что-то другое. ORM освобождают нас от переписывания кода при переходе на другую СУБД (впрочем, не всегда).
Судя по SqlConnection, SqlCommand вы работаете именно с Sql Server.
Что такое NOCOUNT. Нужно ли это вам - думайте сами.