关于ABP框架我就不多说了,网上资料很多,可以自行百度。直接进入 官网
选择你需要的核心框架。我选择了angular+.netCore 版本。点击创建模板,等待下载完成。
在使用vs打开项目前,你应该确保vs是2017版本及以上,还需要安装了.netCore相关的组件。
打开: 下载目录\MyDemo\5.1.1\aspnet-core 下的解决方案。重新生成解决方案,会自动更新NuGet包。设置web.host项目为启动项,修改数据库链接字符串
PM窗口下执行 update-database 命令,将会在指定数据库中进行数据迁移,也就是建立了系统表。
至此,就可以运行项目了。如果想启动angular的话,先下载好node.js,使用管理员权限打开node.js command prompt 命令窗体,执行CD /d “”目录“ 进入到 :下载目录\MyDemo\5.1.1\angular 的工程目录里,执行npm install
加载好相关包后,执行npm start。关于node.js的知识,这里不展开了,我也不是很懂。
如果成功的话,最后会显示监听的端口,打开该地址前,要确保后端的项目接口可以使用,我们先用调试模式去运行项目。效果如下
打开 http://localhost:4200地址,使用Admin账号,密码123qwe,登录后界面如下
第一步大功告成。接下来看看后端项目的结构。
为了减少复杂性和提高代码的可重用性,采用分层架构是一种被广泛接受的技术。
为了实现分层的体系结构,ABP遵循 DDD(领域驱动设计) 的原则,将分为四个层次:
Presentation对应了客户端angular,应用层对应项目MyDemo.Application,领域层对应MyDemo.Core,基础设施层对应MyDemo.EntityFrameworkCore,
创建实体
一个简单的应用场景:创建一些任务(tasks)并分配给人。 我们需要 Task 和 Person 这两个实体。
在领域层建立一个文件夹,取名为Demo,在这里建立这两个实体
public enum TaskState
{
Finish,
Active
}
public class Task : Entity< long >
{
[ForeignKey( " AssignedPersonId " )]
public virtual Person AssignedPerson { get ; set ; }
public virtual int ? AssignedPersonId { get ; set ; }
public virtual string Description { get ; set ; }
public virtual DateTime CreationTime { get ; set ; }
public virtual TaskState State { get ; set ; }
public Task()
{
CreationTime = DateTime.Now;
State = TaskState.Active;
}
}
public class Person : Entity
{
public virtual string Name { get ; set ; }
}
我们导航到Entity 和Entity<long>
public abstract class Entity : Entity< int >, IEntity, IEntity< int >
{
protected Entity();
}
public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey>
{
protected Entity();
//
// 摘要:
// Unique identifier for this entity.
public virtual TPrimaryKey Id { get ; set ; }
//
public virtual bool EntityEquals( object obj);
//
// 摘要:
// Checks if this entity is transient (it has not an Id).
//
// 返回结果:
// True, if this entity is transient
public virtual bool IsTransient();
public override string ToString();
}
可以了解到Entity有一个Id属性,默认int,可以改成你想要的数据类型。
在MyDemo.EntityFrameworkCore项目下找到MyProjectDbContext.cs,添加DBset,命名应该和数据库的表名称一致,为了区分系统表最好不要加上ABP
执行命令 Add-Migration InitialCreate,记得是在EntityFrammeworkCore项目下。成功后会出现一个新文件
在该文件下修改成如下代码
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
// 表名
name: " Person " ,
// Column,字段名
columns: table => new
{
Id = table.Column< int >(nullable: false ),
Name = table.Column< string >(nullable: false ),
},
// 约束,这里只有一个主键约束
constraints: table =>
{
table.PrimaryKey( " PK_Person " , x => x.Id);
});
migrationBuilder.CreateTable(
// 表名
name: " Task " ,
// Column,字段名
columns: table => new
{
Id = table.Column< int >(nullable: false ),
AssignedPersonId = table.Column< int ?>(nullable: true ),
Description = table.Column< string >(nullable: false ),
CreationTime = table.Column<DateTime>(nullable: false ),
State = table.Column< int >(nullable: false ),
},
// 约束,这里只有一个主键约束
constraints: table =>
{
table.PrimaryKey( " PK_Task " , x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: " Person " );
migrationBuilder.DropTable(
name: " Task " );
}
}
在程序包管理器控制台中输入命令:Update-Database,就可以在数据库中看到Person和Task表;
创建仓储
定义仓储接口的代码写到Core项目中,因为仓储接口是领域层的一部分。
这里不需要定义Person的仓储, ABP提供了一种注入通用仓储的方式, ,除非你需要自定义方法,大部分情况下默认接口里的方法已经够用了
ITaskRepository.cs代码如下,
public interface ITaskRepository :IRepository<Task, long >
{
List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state);
}
定义仓储实现类
public class TaskRepository: MyDemoRepositoryBase<Task, long > , ITaskRepository
{
public TaskRepository(IDbContextProvider<MyDemoDbContext> dbContextProvider) : base (dbContextProvider)
{
}
public List<Task> GetAllWithPeople( int ? assignedPersonId, TaskState? state)
{
// 在仓储方法中,不用处理数据库连接、DbContext和数据事务,ABP框架会自动处理。
var query = GetAll(); // GetAll() 返回一个 IQueryable<T>接口类型
// 添加一些Where条件
if (assignedPersonId.HasValue)
{
query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value);
}
if (state.HasValue)
{
query = query.Where(task => task.State == state);
}
return query
.OrderByDescending(task => task.CreationTime)
.Include(task => task.AssignedPerson)
.ToList();
}
}
创建应用服务
在MyDemo.Application项目下建立应用服务和交互类Dto
代码如下
public interface IPersonAppService : IApplicationService
{
// 定义一个方法
List<GetAllPersonOutPut> GetAllPerson();
}
public class TaskAppService : MyDemoAppServiceBase, ITaskAppService
{
private readonly ITaskRepository _taskRepository;
private readonly IRepository<Person> _personRepository;
/// <summary>
/// 构造函数自动注入我们所需要的类或接口
/// </summary>
public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
{
_taskRepository = taskRepository;
_personRepository = personRepository;
}
public List<Task> GetTasks(GetTasksInput input)
{
// 调用Task仓储的特定方法GetAllWithPeople
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
// 用AutoMapper自动将List<Task>转换成List<TaskDto>
return tasks;
}
public void UpdateTask(UpdateTaskInput input)
{
// 可以直接Logger,它在ApplicationService基类中定义的
Logger.Info( " Updating a task for input: " + input);
// 通过仓储基类的通用方法Get,获取指定Id的Task实体对象
var task = _taskRepository.Get(input.TaskId);
// 修改task实体的属性值
if (input.State.HasValue)
{
task.State = input.State.Value;
}
if (input.AssignedPersonId.HasValue)
{
task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value);
}
// 我们都不需要调用Update方法
// 因为应用服务层的方法默认开启了工作单元模式(Unit of Work)
// ABP框架会工作单元完成时自动保存对实体的所有更改,除非有异常抛出。有异常时会自动回滚,因为工作单元默认开启数据库事务。
}
public void CreateTask(CreateTaskInput input)
{
Logger.Info( " Creating a task for input: " + input);
// 通过输入参数,创建一个新的Task实体
var task = new Task { Description = input.Description };
if (input.AssignedPersonId.HasValue)
{
task.AssignedPersonId = input.AssignedPersonId.Value;
}
// 调用仓储基类的Insert方法把实体保存到数据库中
_taskRepository.Insert(task);
}
}
public class UpdateTaskInput
{
[Range( 1 , long .MaxValue)]
public long TaskId { get ; set ; }
public int ? AssignedPersonId { get ; set ; }
public TaskState? State { get ; set ; }
public void AddValidationErrors(List<ValidationResult> results)
{
if (AssignedPersonId == null && State == null )
{
results.Add( new ValidationResult( " AssignedPersonId和State不能同时为空! " , new [] { " AssignedPersonId " , " State " }));
}
}
}
public class GetTasksInput
{
public virtual int ? AssignedPersonId { get ; set ; }
public virtual TaskState State { get ; set ; }
}
public class CreateTaskInput
{
public int ? AssignedPersonId { get ; set ; }
[Required]
public string Description { get ; set ; }
}
必须注意的是大部分的类都需要用public,像TaskAppServicel类前面如果不用public的话就无法暴露出这个接口,仓储不用public的话自动依赖注入就会报错。
启动项目就可以看到刚写的服务出现在了界面上
测试一下
这里执行后出现了错误,但调试的项目并没有抛出异常,应该是被捕获到了。查看log日志
这里数据主键没有设置自增键,去数据设置一下,重新测试,OK,查询数据,确实增加了一条数据。Update-Database命令前修改的InitialCreate类里面的up方法,应该可以在这里设置自增键,下次有空再补上。
ABP的大致应用就是这样了,后面在慢慢探索其他功能。下期的话应该会写一下.netCore怎么在IIS上部署,前几天弄了一下午总发布报错,后面有时间在继续研究。
查看更多关于ABP框架(.netCore+angular)的初步使用——启动并建立操作表的详细内容...