Introduction
这是系列文章 “Using ASP.NET Core, Entity Framework Core and ASP.NET Boilerplate to Create NLayered Web Application” 的第一部分。
- Part I (this one) - Using ASP.NET Core, Entity Framework Core and ASP.NET Boilerplate to Create NLayered Web Application
- Part II - Using ASP.NET Core, Entity Framework Core and ASP.NET Boilerplate to Create NLayered Web Application
本文将演示如何创建一个跨平台分层的网页程序。我们将用到以下工具:
- .Net Core as base cross platform application development framework.
- ASP.NET Boilerplate (ABP) as startup template and application framework.
- ASP.NET Core as web framework.
- Entity Framework Core as ORM framework.
- Twitter Bootstrap as HTML&CSS framework.
- jQuery as client side AJAX/DOM library.
- xUnit and Shouldly for server side unit/integrations tests.
还将用到 ABP startup 模板里面包含的 Log4Net 和 AutoMapper。我们将用到以下技术:
我们将开发一个简单的任务管理程序。
Prerequirements
- Visual Studio 2017
- SQL Server (you can change connection string to localdb)
- Visual Studio Extensions:
Create the Application
按照教程创建程序,记得取消勾选 Authentication。
整个解决方案包含 6 个项目:
- .Core 领域/业务层(实体,领域服务等)
- .Application 应用层(DTOs,应用服务等)
- .EntityFramework 集成 EF Core(从其他层抽象出 EF Core)
- .Web ASP.NET MVC 层
- .Tests 单元测试和集成测试(直到 Application 应用层,不包括 Web 层)
- .Web.Tests ASP.NET Core 集成测试(完整的集成测试,包括 Web 层)
Developing the Application
Creating a Task Entity
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using Abp.Timing;
namespace Acme.SimpleTaskApp.Tasks
{
[Table("AppTasks")]
public class Task : Entity, IHasCreationTime
{
public const int MaxTitleLength = 256;
public const int MaxDescriptionLength = 64 * 1024; //64KB
[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)
: this()
{
Title = title;
Description = description;
}
}
public enum TaskState : byte
{
Open = 0,
Completed = 1
}
}
- 继承自 ABP Entity 类。Entity 包含类型为 int 的 Id 属性。可以通过 Entity
- IHasCreationTime 是个简单的接口,只定义了 CreationTime 属性(最好就用 CreationTime 这个标准名称)
- Task 实体类定义了一个必须的 Title 和可选的 Description
- TaskState 是任务状态的枚举
- Clock.Now 默认返回 DateTime.Now。但它提供了抽象,以便我们轻松切换使用 DateTime.UtcNow。使用 ABP 框架时始终使用 Clock.Now
- 将 Task 实体保存在 AppTasks 表中
Adding Task to DbContext
.EntityFrameworkCore project includes a pre-defined DbContext. I should add a DbSet for the Task entity into the DbContext:
public class SimpleTaskAppDbContext : AbpDbContext
{
public DbSet<Task> Tasks { get; set; }
public SimpleTaskAppDbContext(DbContextOptions<SimpleTaskAppDbContext> options)
: base(options)
{
}
}
Creating the First Database Migration
这里我按照教程的操作顺序尝试了几遍都失败了。
后来先临时卸载了两个测试项目,然后迁移数据库就成功了。
Task Application Service
Application Services are used to expose domain logic to the presentation layer. An Application Service is called from presentation layer with a Data Transfer Object (DTO) as parameter (if needed), uses domain objects to perform some specific business logic and returns a DTO back to the presentation layer (if needed)
GetAllTasksInput:之所以用它作为 GetAll 方法的参数而不直接用 state 是为了避免后期修改方法参数时破坏现有代码。
ObjectMapper:继承自 AppServiceBase 类,并默认通过 AutoMapper实现。用于将 Task 对象列表映射至 TaskListDtos 对象列表。
Task List View
Adding a New Menu Item
NavigationProvider 在 Web 项目的 Startup 文件夹中。
Localization
应用 Localization 前:
应用 Localization 后:
Filtering Tasks
学了一波如何使用 Bundler & Minifier 压缩 JS。
Automated Testing Task List Page
居然是使用 AngleSharp 来解析测试网页。