Не рекомендуется использовать ресурсы сервера для отслеживания изменений в таких сценариях.В таких сценариях, как корзина покупок, список или пакетное редактирование, лучше отслеживать изменения на стороне клиента.
Требование получения представлений, генерируемых на стороне сервера, не означает, что вам нужно отслеживать изменения в DbContext
.Получить представление индекса и создать представление с сервера, но отслеживать изменения на клиенте.Затем для сохранения отправьте все данные на сервер, чтобы сохранить изменения на основе имеющейся у вас информации отслеживания.
Механизм отслеживания изменений на стороне клиента зависит от требований и сценария, например, вы можете отслеживать измененияиспользуя вводы html, вы можете отслеживать изменения, используя cookie, вы можете отслеживать изменения, используя объекты javascript в памяти браузера, такие как угловые сценарии.
В этом посте я покажу пример использования вводов html и привязки модели.Чтобы узнать больше об этой теме, взгляните на эту статью Phill Haack: Привязка модели к списку .
Пример
В следующем примере я опишу списокредактирование сценария для списка клиентов.Для простоты, я полагаю:
- У вас есть список клиентов, которые вы собираетесь редактировать на клиенте.Возможно, вы захотите добавить, отредактировать или удалить элементы.
- При добавлении нового элемента шаблон строки для новой строки должен исходить с сервера.
- При удалении вы помечаете элемент как удаленный, нажавна флажок в строке.
- При добавлении / редактировании вы хотите, чтобы рядом с ячейками отображались ошибки проверки.
- Чтобы сохранить изменения в конце, нажмите кнопку Сохранить.
Для реализации описанного выше сценария Затем необходимо создать следующие модели, действия и представления:
Отслеживаемый Модель
Этот класс является моделью, которая помогает нам в отслеживании на стороне клиента и редактировании списка:
public class Trackable<T>
{
public Trackable() { }
public Trackable(T model) { Model = model; }
public Guid Index { get; set; } = Guid.NewGuid();
public bool Deleted { get; set; }
public bool Added { get; set; }
public T Model { get; set; }
}
Модель клиента
Модель клиента:
public class Customer
{
[Display(Name ="Id")]
public int Id { get; set; }
[StringLength(20, MinimumLength = 1)]
[Required]
[Display(Name ="First Name")]
public string FirstName { get; set; }
[StringLength(20, MinimumLength = 1)]
[Required]
[Display(Name ="Last Name")]
public string LastName { get; set; }
[EmailAddress]
[Required]
[Display(Name ="Email Name")]
public string Email { get; set; }
}
Index.cshtml Представление
Представление Index отвечает за отображение List<Trackable<Customer>>
.При рендеринге каждой записи мы используем RowTemplate
view.То же представление, которое мы используем при добавлении нового элемента.
В этом представлении у нас есть кнопка отправки для сохранения и кнопка для добавления новых строк, которая вызывает действие Create с использованием ajax.
ЗдесьИндексное представление:
@model IEnumerable<Trackable<Customer>>
<h2>Index</h2>
<form method="post" action="Index">
<p>
<button id="create">New Customer</button>
<input type="submit" value="Save All">
</p>
<table class="table" id="data">
<thead>
<tr>
<th>
Delete
</th>
<th>
@Html.DisplayNameFor(x => x.Model.FirstName)
</th>
<th>
@Html.DisplayNameFor(x => x.Model.LastName)
</th>
<th>
@Html.DisplayNameFor(x => x.Model.Email)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
await Html.RenderPartialAsync("RowTemplate", item);
}
</tbody>
</table>
</form>
@section Scripts{
<script>
$(function () {
$('#create').click(function (e) {
e.preventDefault();
$.ajax({
url: 'Create',
method: 'Get',
success: function (data) {
$('#data tbody tr:last-child').after(data);
},
error: function (e) { alert(e); }
});
});
});
</script>
}
RowTemplate.cshtml Представление
Это представление отвечает за отображение записи клиента.В этом представлении мы сначала визуализируем Index
в скрытом виде, затем устанавливаем префикс [index]
для полей и затем визуализируем поля, включая индекс, добавленные, удаленные и идентификатор модели:
ВотRowTemplate View:
@model Trackable<Customer>
<tr>
<td>
@Html.HiddenFor(x => x.Index)
@{Html.ViewData.TemplateInfo.HtmlFieldPrefix = $"[{Model.Index}]";}
@Html.HiddenFor(x => x.Index)
@Html.HiddenFor(x => x.Model.Id)
@Html.HiddenFor(x => x.Added)
@Html.CheckBoxFor(x => x.Deleted)
</td>
<td>
@Html.EditorFor(x => x.Model.FirstName)
@Html.ValidationMessageFor(x => x.Model.FirstName)
</td>
<td>
@Html.EditorFor(x => x.Model.LastName)
@Html.ValidationMessageFor(x => x.Model.LastName)
</td>
<td>
@Html.EditorFor(x => x.Model.Email)
@Html.ValidationMessageFor(x => x.Model.Email)
</td>
</tr>
CustomerController
public class CustomerController : Controller
{
private static List<Customer> list;
}
Он будет выполнять следующие действия.
[GET] Индекс Действие
В этом действии вы можете загрузить данные из базы данных и сформировать их в List<Trackable<Customer>>
и передать в Index
View:
[HttpGet]
public IActionResult Index()
{
if (list == null)
{
list = Enumerable.Range(1, 5).Select(x => new Customer()
{
Id = x,
FirstName = $"A{x}",
LastName = $"B{x}",
Email = $"A{x}@B{x}.com"
}).ToList();
}
var model = list.Select(x => new Trackable<Customer>(x)).ToList();
return View(model);
}
[GET] Создать действие
Это действие отвечает за возврат нового шаблона строки.Он будет вызываться кнопкой в индексном представлении с использованием ajax:
[HttpGet]
public IActionResult Create()
{
var model = new Trackable<Customer>(new Customer()) { Added = true };
return PartialView("RowTemplate", model);
}
[POST] Индексное действие
Это действие отвечает заполучение отслеживаемого элемента от клиента и сохранение его.Модель, которую он получает, - List<Trackable<Customer>>
.Сначала он удаляет сообщения об ошибках проверки удаленных строк.Затем удаляет те, которые как удалены, так и добавлены.Затем проверяет, действительно ли состояние модели, пытается применить изменения к источнику данных.
Элементы, имеющие Deleted
свойство как true, удаляются, элементы, имеющие Added
как true и Deleted
как false, являются новыми элементами,а остальные пункты редактируются.Затем, без необходимости загружать все элементы из базы данных, просто используя цикл for, вызовите db.Entry
для каждого элемента, установите их состояния и, наконец, сохраните изменения.
[HttpPost]
public IActionResult Index(List<Trackable<Customer>> model)
{
//Cleanup model errors for deleted rows
var deletedIndexes = model.
Where(x => x.Deleted).Select(x => $"[{x.Index}]");
var modelStateDeletedKeys = ModelState.Keys.
Where(x => deletedIndexes.Any(d => x.StartsWith(d)));
modelStateDeletedKeys.ToList().ForEach(x => ModelState.Remove(x));
//Removing rows which are added and deleted
model.RemoveAll(x => x.Deleted && x.Added);
//If model state is not valid, return view
if (!ModelState.IsValid)
return View(model);
//Deleted rows
model.Where(x => x.Deleted && !x.Added).ToList().ForEach(x =>
{
var i = list.FindIndex(c => c.Id == x.Model.Id);
if (i >= 0)
list.RemoveAt(i);
});
//Added rows
model.Where(x => !x.Deleted && x.Added).ToList().ForEach(x =>
{
list.Add(x.Model);
});
//Edited rows
model.Where(x => !x.Deleted && !x.Added).ToList().ForEach(x =>
{
var i = list.FindIndex(c => c.Id == x.Model.Id);
if (i >= 0)
list[i] = x.Model;
});
//Reditect to action index
return RedirectToAction("Index");
}