根據官網給的https://aspnetboilerplate.com/Pages/Articles/Introduction-With-AspNet-Core-And-Entity-Framework-Core-Part-1/index.html和https://aspnetboilerplate.com/Pages/Articles/Introduction-With-AspNet-Core-And-Entity-Framework-Core-Part-2/index.html寫的demo
第一步在 MyABP.Core的Authorization文件夾下面創建一個文件夾Tasks,並新增兩個類
using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Abp.Domain.Entities; using Abp.Domain.Entities.Auditing; using Abp.Timing; namespace MyABP.Authorization.Tasks { [Table("AppTasks")] public class Task : Entity, IHasCreationTime { public const int MaxTitleLength = 256; public const int MaxDescriptionLength = 64 * 1024; //64KB [ForeignKey(nameof(AssignedPersonId))] public Person AssignedPerson { get; set; } public Guid? AssignedPersonId { get; set; } [Required] [StringLength(MaxTitleLength)] public string Title { get; set; } [StringLength(MaxDescriptionLength)] public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } public Task() { CreationTime = Clock.Now; State = TaskState.Open; } public Task(string title, string description = null, Guid? assignedPersonId = null) : this() { Title = title; Description = description; AssignedPersonId = assignedPersonId; } } public enum TaskState : byte { Open = 0, Completed = 1 } }
using Abp.Domain.Entities.Auditing; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace MyABP.Authorization.Tasks { [Table("AppPersons")] public class Person : AuditedEntity<Guid> { public const int MaxNameLength = 32; [Required] [StringLength(MaxNameLength)] public string Name { get; set; } public Person() { } public Person(string name) { Name = name; } } }
第二步 新增好類之后,在MyABP.EntityFrameworkCore項目的MyABPDbContext中,新增代碼
public DbSet<Task> Tasks { get; set; } public DbSet<Person> People { get; set; }
將MyABP.EntityFrameworkCore作為啟動項目,在管理器控制台中輸入Add-Migration "Initial"回車 (注意這里的Initial的命名每次都應該是不同的)更新需要遷移的,更新完成之后,繼續輸入update-database回車,這樣就將數據表更新到數據中
第三步 在MyABP.Application層新增一個文件夾Tasks,接着新增一個接口服務ITaskAppService.cs
using MyABP.Tasks.DTO; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace MyABP.Application.Tasks { public interface ITaskAppService : IApplicationService { Task<ListResultDto<TaskListDto>> GetAll(GetAllTasksInput input);//用於查詢任務列表 Task Create(CreateTaskInput input);//創建任務 } }
這是我們需要創建三個類TaskListDto、GetAllTasksInput和CreateTaskInput,則我們在Tasks文件夾下面新增一個文件夾DTO用於管理這些類

using Abp.Application.Services.Dto; using Abp.AutoMapper; using Abp.Domain.Entities.Auditing; using MyABP.Authorization.Tasks; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text; namespace MyABP.Tasks.DTO { public class GetAllTasksInput { public TaskState? State { get; set; } } [AutoMapFrom(typeof(Task))] public class TaskListDto : EntityDto, IHasCreationTime { public string Title { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } public Guid? AssignedPersonId { get; set; } public string AssignedPersonName { get; set; } } [AutoMapTo(typeof(Task))] public class CreateTaskInput { [Required] [StringLength(Task.MaxTitleLength)] public string Title { get; set; } [StringLength(Task.MaxDescriptionLength)] public string Description { get; set; } public Guid? AssignedPersonId { get; set; } } }
接着在Tasks文件夾中創建TaskAppService.cs類來實現ITaskAppService的接口方法

using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Abp.Application.Services.Dto; using Abp.Domain.Repositories; using Abp.Linq.Extensions; using Castle.Core.Logging; using Microsoft.EntityFrameworkCore; using MyABP.Tasks.DTO; namespace MyABP.Application.Tasks { public class TaskAppService : MyABPAppServiceBase, ITaskAppService { private readonly IRepository<Authorization.Tasks.Task> _taskRepository; public TaskAppService(IRepository<Authorization.Tasks.Task> taskRepository) { _taskRepository = taskRepository; } public async Task<ListResultDto<TaskListDto>> GetAll(GetAllTasksInput input) { var tasks = await _taskRepository .GetAll() .Include(t => t.AssignedPerson) .WhereIf(input.State.HasValue, t => t.State == input.State.Value) .OrderByDescending(t => t.CreationTime) .ToListAsync(); return new ListResultDto<TaskListDto>( ObjectMapper.Map<List<TaskListDto>>(tasks) ); } public async Task Create(CreateTaskInput input) { var task = ObjectMapper.Map<Authorization.Tasks. Task>(input); await _taskRepository.InsertAsync(task); } } }
由於在官方文檔的pass II部分創建了任務,還將任務指派給其他人,所以還需要做一個下拉框來裝AppPersons表的查詢出來的人員列表,則我們在Tasks文件夾中還需要定義一個ILookupAppService.cs接口

using Abp.Application.Services; using Abp.Application.Services.Dto; using System.Threading.Tasks; namespace MyABP.Application.Tasks { public interface ILookupAppService : IApplicationService { Task<ListResultDto<ComboboxItemDto>> GetPeopleComboboxItems(); } }
創建LookupAppService.cs來實現ILookupAppService的接口方法

using Abp.Application.Services.Dto; using Abp.Domain.Repositories; using MyABP.Authorization.Tasks; using System; using System.Linq; using System.Threading.Tasks; namespace MyABP.Application.Tasks { public class LookupAppService : MyABPAppServiceBase, ILookupAppService { private readonly IRepository<Person, Guid> _personRepository; public LookupAppService(IRepository<Person, Guid> personRepository) { _personRepository = personRepository; } public async Task<ListResultDto<ComboboxItemDto>> GetPeopleComboboxItems() { var people = await _personRepository.GetAllListAsync(); return new ListResultDto<ComboboxItemDto>( people.Select(p => new ComboboxItemDto(p.Id.ToString("D"), p.Name)).ToList() ); } } }
第四步 在MyABP.Web.Mvc項目的Controllers控制器文件夾中新增一個TasksController.cs控制室

using System.Linq; using System.Threading.Tasks; using Abp.Application.Services.Dto; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using MyABP.Application.Tasks; using MyABP.Controllers; using MyABP.Tasks.DTO; using MyABP.Web.Models.Tasks; namespace MyABP.Web.Controllers { public class TasksController : MyABPControllerBase { private readonly ITaskAppService _taskAppService; private readonly ILookupAppService _lookupAppService; public TasksController(ITaskAppService taskAppService, ILookupAppService lookupAppService) { _taskAppService = taskAppService; _lookupAppService = lookupAppService; } public async Task<ActionResult> Index(GetAllTasksInput input) { var output = await _taskAppService.GetAll(input); var model = new IndexViewModel(output.Items) { SelectedTaskState = input.State }; return View(model); } public async Task<ActionResult> Create() { var peopleSelectListItems = (await _lookupAppService.GetPeopleComboboxItems()).Items .Select(p => p.ToSelectListItem()) .ToList(); peopleSelectListItems.Insert(0, new SelectListItem { Value = string.Empty, Text = L("Unassigned"), Selected = true }); return View(new CreateTaskViewModel(peopleSelectListItems)); } } }
這是我們需要在Models文件夾中新增類IndexViewModel和CreateTaskViewModel,則我們在Models文件夾下面新增一個文件夾Tasks用於管理這些類

using Abp.Localization; using Microsoft.AspNetCore.Mvc.Rendering; using MyABP.Authorization.Tasks; using MyABP.Tasks.DTO; using System; using System.Collections.Generic; using System.Linq; namespace MyABP.Web.Models.Tasks { public class IndexViewModel { public IReadOnlyList<TaskListDto> Tasks { get; } public IndexViewModel(IReadOnlyList<TaskListDto> tasks) { Tasks = tasks; } public string GetTaskLabel(TaskListDto task) { switch (task.State) { case TaskState.Open: return "label-success"; default: return "label-default"; } } public TaskState? SelectedTaskState { get; set; } public List<SelectListItem> GetTasksStateSelectListItems(ILocalizationManager localizationManager) { var list = new List<SelectListItem> { new SelectListItem { Text = localizationManager.GetString( MyABPConsts.LocalizationSourceName, "AllTasks"), Value = "", Selected = SelectedTaskState == null } }; list.AddRange(Enum.GetValues(typeof(TaskState)) .Cast<TaskState>() .Select(state => new SelectListItem { Text = localizationManager.GetString(MyABPConsts.LocalizationSourceName, $"TaskState_{state}"), Value = state.ToString(), Selected = state == SelectedTaskState }) ); return list; } } }

using Microsoft.AspNetCore.Mvc.Rendering; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace MyABP.Web.Models.Tasks { public class CreateTaskViewModel { public List<SelectListItem> People { get; set; } public CreateTaskViewModel(List<SelectListItem> people) { People = people; } } }
轉回到Tasks控制器中,我們需要在Index方法里面右擊鼠標,添加視圖Index,接着我們進入Index.cshtml編寫代碼如下

@model MyABP.Web.Models.Tasks.IndexViewModel @{ ViewBag.Title = L("TaskList"); ViewBag.ActiveMenu = "TaskList"; //Matches with the menu name in SimpleTaskAppNavigationProvider to highlight the menu item } <h2> @L("TaskList") <span class="pull-right"> @Html.DropDownListFor( model => model.SelectedTaskState, Model.GetTasksStateSelectListItems(LocalizationManager), new { @class = "form-control", id = "TaskStateCombobox" }) </span> </h2> <a class="btn btn-primary btn-sm" asp-action="Create">@L("AddNew")</a> <div class="row"> <div> <ul class="list-group" id="TaskList"> @foreach (var task in Model.Tasks) { <li class="list-group-item"> <span class="pull-right label @Model.GetTaskLabel(task)">@L($"TaskState_{task.State}")</span> <h4 class="list-group-item-heading">@task.Title</h4> <div class="list-group-item-text"> @task.CreationTime.ToString("yyyy-MM-dd HH:mm:ss") </div> </li> } </ul> </div> </div>
在Create方法里面右擊鼠標,添加視圖Create,接着我們進入Create.cshtml編寫代碼如下

@model MyABP.Web.Models.Tasks.CreateTaskViewModel @section scripts { <environment names="Development"> <script src="~/view-resources/Views/Tasks/create.js"></script> </environment> <environment names="Staging,Production"> <script src="~/view-resources/Views/Tasks/create.min.js"></script> </environment> } <h2> @L("NewTask") </h2> <form id="TaskCreationForm"> <div class="form-group"> <label for="Title">@L("Title")</label> <input type="text" name="Title" class="form-control" placeholder="@L("Title")" required maxlength="@MyABP.Authorization.Tasks.Task.MaxTitleLength"> </div> <div class="form-group"> <label for="Description">@L("Description")</label> <input type="text" name="Description" class="form-control" placeholder="@L("Description")" maxlength="@MyABP.Authorization.Tasks.Task.MaxDescriptionLength"> </div> <div class="form-group"> @Html.Label(L("AssignedPerson")) @Html.DropDownList( "AssignedPersonId", Model.People, new { @class = "form-control", id = "AssignedPersonCombobox" }) </div> <button type="submit" class="btn btn-default">@L("Save")</button> </form>
這里我們需要創建一個js,在wwwroot文件夾的view-resources的Views中新增一個Tasks文件夾 ,創建create.js 代碼如下

(function ($) { $(function () { var _$form = $('#TaskCreationForm'); _$form.find('input:first').focus(); _$form.validate(); _$form.find('button[type=submit]') .click(function (e) { e.preventDefault(); if (!_$form.valid()) { return; } var input = _$form.serializeFormToObject(); abp.services.app.task.create(input) .done(function () { location.href = '/Tasks'; }); }); }); })(jQuery);
第五步 在MyABP.Web.Mvc項目的Startup文件夾的MyABPNavigationProvider.cs中的SetNavigation方法中插入代碼
.AddItem( new MenuItemDefinition( "TaskList", L("TaskList"), url: "Tasks", icon: "info"//圖標 // requiredPermissionName: PermissionNames.Pages_Roles權限 ) )
最后,在AppPersons中新增兩個人名,這樣下拉框就有數據了,運行代碼則可以看到
點擊Add New
保存之后,js代碼中寫了自動返回Tasks頁面