根据官网给的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; } } }View Code
接着在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); } } }View Code
由于在官方文档的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(); } }View Code
创建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() ); } } }View Code
第四步 在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)); } } }View Code
这是我们需要在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; } } }View Code
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; } } }View Code
转回到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>View Code
在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>View Code
这里我们需要创建一个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);View Code
第五步 在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页面