Я предлагаю, чтобы Контроллер был прост и принимал простые решения.Вот один из моих ASP.NET MVC 5
Во-первых, я использую внедрение зависимостей для накачки интерфейса бизнес-сервисов в конструктор.Я также использую базовый класс, в котором живет весь код, общий для контроллера, поэтому я передаю в него бизнес-интерфейс, на который все еще можно ссылаться из этого класса.Вам нужно будет прочитать об IoC и о том, как его реализовать.У Microsoft есть простая, которую я использую, но есть и другие.
public SparesSectionsController(IBusinessService bs) : base(bs)
{
}
Для операции CRUD у меня есть пары действий, а также индекс, в котором перечислены записи для CREATE, EDIT, DELETE.Вот один для редактирования существующей записи
[HttpGet]
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
SparesSection sparesSection = await _bs.SparesSection_GetAsync(id.Value);
if (sparesSection == null)
{
return HttpNotFound();
}
return View(sparesSection);
}
Соответствующее действие POST связывает любые обязательные поля как часть параметров действия.
Для простоты, но, вероятно, из-за плохой практики, я пропускаю состояние моделик бизнес-уровню, чтобы можно было добавить любое дополнительное поле или общие ошибки.Было бы лучше определить ошибки возвращаемого бизнес-уровня в отдельном классе, определенном на более низком уровне, и добавить их в состояние модели.Этот последний подход нарушает зависимость от MVC и делает абстрагирование бизнес-уровня для использования в качестве веб-интерфейса или другого независимого бизнес-источника.
Обратите внимание, что ViewMessage - это свойство базового класса, которое просто помещает некоторый текст в свойство TempData.Затем _Layout показывает сообщение, если оно не равно нулю в представлении.Этот подход обеспечивает постоянство действий во время перенаправления, которое ViewBag не делает
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "SparesSectionId,SparesSectionTitle")] SparesSection sparesSection)
{
if (ModelState.IsValid)
{
await _bs.SparesSection_UpdateAsync(sparesSection, ModelState);
if (ModelState.IsValid)
{
ViewMessage = "Saved " + sparesSection.SparesSectionTitle;
return RedirectToAction("Index");
}
}
return View(sparesSection);
}
Представление является легким и компактным
@model YourProjectName.Models.SparesSection
@{
ViewBag.Title = "Edit Spares Section";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container w-50">
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.SparesSectionId)
<div class="form-group">
@Html.LabelFor(model => model.SparesSectionTitle, htmlAttributes: new { @class = "control-label" })
@Html.EditorFor(model => model.SparesSectionTitle, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SparesSectionTitle, "", new { @class = "text-danger" })
</div>
<div class="d-flex">
<div class="ml-auto">
<input type="submit" value="Save" class="btn btn-primary btn-sm btn-default" />
</div>
</div>
</div>
}
</div>
Что касается моей модели, поскольку это небольшой класс,Я использую Entity Framework напрямую и расширяю его, используя частичный класс.Для больших или более сложных данных я предлагаю отдельную модель представления, которую вы заполняете, полученную из данных EF вместе с любыми другими ресурсами
//-----------------------------------------------------------------------
#region Spares Section
[MetadataType(typeof(SparesSectionMetaData))]
public partial class SparesSection { }
public class SparesSectionMetaData
{
[ScaffoldColumn(false)]
[DefaultValue(0)]
[Required]
public int SparesSectionId { get; set; }
[DisplayName("Spares Section Title")]
[StringLength(100, MinimumLength = 2, ErrorMessage = "2-100 characters")]
[Required]
public string SparesSectionTitle { get; set; }
}