上一篇 我们介绍了什么是ABP,这一篇我们通过原作者的”简单任务系统”例子,演示如何运用ABP开发项目

创建实体

一般来说任务是需要分配给人来做的,所以我们创建两个实体模型类:Task和Persion


using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using System.ComponentModel.DataAnnotations.Schema;
using System; namespace SimpleTaskSystem
{
public class Task : Entity, IHasCreationTime
{
[ForeignKey("AssignedPersonId")]
public Person AssignedPerson { get; set; }
public long? AssignedPersonId { get; set; }
public string Description { get; set; }
public TaskState State { get; set; }
public DateTime CreationTime { get; set; } public Task()
{
State = TaskState.Active;
}
}
}

using System;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing; namespace SimpleTaskSystem
{
public class Person : Entity, IHasCreationTime
{
public string Name { get; set; }
public DateTime CreationTime { get; set; }
}
}

创建DbContext

使用EntityFramework需要先定义DbContext类,ABP的模板已经创建了DbContext文件,我们只需要把Task和Person类添加到IDbSet


using Abp.EntityFramework;
using System.Data.Entity; namespace SimpleTaskSystem.EntityFramework
{
public class SimpleTaskSystemDbContext : AbpDbContext
{
//TODO: Define an IDbSet for each Entity... //Example:
//public virtual IDbSet Users { get; set; }
public virtual IDbSet Tasks { get; set; }
public virtual IDbSet People { get; set; } /* NOTE:
* Setting "Default" to base class helps us when working migration commands on Package Manager Console.
* But it may cause problems when working Migrate.exe of EF. If you will apply migrations on command line, do not
* pass connection string name to base classes. ABP works either way.
*/
public SimpleTaskSystemDbContext()
: base("Default")
{ } /* NOTE:
* This constructor is used by ABP to pass connection string defined in SimpleTaskSystemDataModule.PreInitialize.
* Notice that, actually you will not directly create an instance of SimpleTaskSystemDbContext since ABP automatically handles it.
*/
public SimpleTaskSystemDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{ }
}
}

通过Database Migrations创建数据库

我们使用EntityFramework的Code First模式创建数据库架构。ABP模板生成的项目已经默认开启了数据迁移功能,我们添加一些默认数据进去。


using System.Data.Entity.Migrations; namespace SimpleTaskSystem.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "SimpleTaskSystem";
} protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
{
// This method will be called every time after migrating to the latest version.
// You can add any seed data here...
context.People.AddOrUpdate(
p => p.Name,
new Person { Name = "张三" },
new Person { Name = "王二" },
new Person { Name = "李四" },
new Person { Name = "老王" }
);
}
}
}

然后打开 程序包管理器控制台 ,选择默认项目并执行命令”Add-Migration InitialCreate”,此时会在Migrations目录下生成一个以 当前时间_InitialCreate.cs 的文件。

然后继续在 程序包管理器控制台 执行 Update-Database,等执行完毕则会在数据库自动创建相应的数据表

定义仓储接口

通过仓储模式,可以更好把业务代码与数据库操作代码更好的分离。我们把仓储的代码写到 Core 项目中


using Abp.Domain.Repositories;
using System.Collections.Generic; namespace SimpleTaskSystem.Repositorys
{
public interface ITaskRepository : IRepository<Task, long>
{
List GetAllWithPeople(long? assignedPersonId, TaskState? state);
}
}

我们可以为Persion类定义一个仓储,也可以不定义,这里我选择定义,虽然ABP默认提供的仓储在本例中已经够用了,但考虑到以后扩展方便,我们还是也给它定义一个。


using Abp.Domain.Repositories; namespace SimpleTaskSystem.Repositorys
{
public interface IPersionRepository : IRepository<Person, long>
{ }
}

这样后期我们要加一个自定义方法直接加在这里面就行了,不用改动太多。这里也介绍一下ABP自带的仓储方法,我们可以直接使用。

实现仓储类

因为本例用的是EntityFramework,所以我们将在EntityFramework项目中实现上面定义的ITaskRepository仓储接口。如果后期想换个ORM框架,比如NHibernate,我们就只要更换仓储实现的这个项目即可。

TaskRepository.cs


using System.Collections.Generic;
using Abp.EntityFramework;
using SimpleTaskSystem.EntityFramework.Repositories;
using SimpleTaskSystem.Repositorys;
using System.Linq;
using System.Data.Entity; namespace SimpleTaskSystem.EntityFramework.Repositorys
{
public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository
{
public TaskRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
{
} public List GetAllWithPeople(long? assignedPersonId, TaskState? state)
{
//在仓储方法中,不用处理数据库连接、DbContext和数据事务,ABP框架会自动处理。
var query = GetAll(); //返回一个 IQueryable接口类型 //添加一些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();
}
}
}

PersionRepository.cs


using SimpleTaskSystem.Repositorys;
using Abp.EntityFramework; namespace SimpleTaskSystem.EntityFramework.Repositories
{
public class PersionRepository : SimpleTaskSystemRepositoryBase<Person, long>, IPersionRepository
{
public PersionRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
{ }
}
}

创建服务(Services)

首先在Application项目中定义Task的应用服务层的接口,各dto的类请下载源码查看。

ITaskAppService.cs


using Abp.Application.Services; namespace SimpleTaskSystem.Services
{
public interface ITaskAppService : IApplicationService
{
GetTasksOutput GetTasks(GetTasksInput input);
void UpdateTask(UpdateTaskInput input);
void CreateTask(CreateTaskInput input);
}
}

然后,我们写TaskAppService类来实现ITaskAppService接口

TaskAppService.cs


using Abp.Application.Services;
using AutoMapper;
using SimpleTaskSystem.Repositorys;
using System.Collections.Generic; namespace SimpleTaskSystem.Services
{
public class TaskAppService : ApplicationService, ITaskAppService
{
private readonly ITaskRepository _taskRepository;
private readonly IPersionRepository _personRepository; ///
/// 构造函数自动注入我们所需要的类或接口
///
public TaskAppService(ITaskRepository taskRepository, IPersionRepository personRepository)
{
_taskRepository = taskRepository;
_personRepository = personRepository;
} 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 GetTasksOutput GetTasks(GetTasksInput input)
{
//调用Task仓储的特定方法GetAllWithPeople
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State); //用AutoMapper自动将List转换成List
return new GetTasksOutput
{
Tasks = Mapper.Map<list>(tasks)
};
} public void UpdateTask(UpdateTaskInput input)
{
//可以直接Logger,它在ApplicationService基类中定义的
Logger.Info("Updating a task for input: " + input); //通过仓储基类的通用方法Get,获取指定Id的Task实体对象
var task = _taskRepository.Get(input.Id); //修改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框架会工作单元完成时自动保存对实体的所有更改,除非有异常抛出。有异常时会自动回滚,因为工作单元默认开启数据库事务。
}
}
}

数据验证

如果应用服务(Application Service)方法的参数对象实现了IInputDto或IValidate接口,ABP会自动进行参数有效性验证。

CreateTaskInput.cs


using Abp.Application.Services.Dto;
using System.ComponentModel.DataAnnotations; namespace SimpleTaskSystem.Services
{
public class CreateTaskInput : IInputDto
{
[Required]
public string Description { get; set; } public long? AssignedPersonId { get; set; }
}
}

如果你想使用自定义验证,你可以实现ICustomValidate 接口

UpdateTaskInput.cs


using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Abp.Application.Services.Dto;
using Abp.Runtime.Validation; namespace SimpleTaskSystem.Services
{
public class UpdateTaskInput : IInputDto, ICustomValidate
{
public int Id { get; set; } public string Description { get; set; } public long? AssignedPersonId { get; set; } public TaskState? State { get; set; } public void AddValidationErrors(List results)
{
if (AssignedPersonId == null && State == null)
{
results.Add(new ValidationResult("AssignedPersonId和State不能同时为空!", new[] { "AssignedPersonId", "State" }));
}
}
}
}

创建Web Api服务

ABP默认已经开户了动态API,我们不需要任何设置即可非常轻松地把Application Service的public方法发布成Web Api接口,可以供客户端通过ajax调用。

下一篇我们将在此基础上构建前台页面,实现在浏览器中对任务进行简单的增删改查,静请期待……

本节源码链接: http://pan.baidu.com/s/1jIvZPSM 密码: njk5

ABP教程(三)- 开始一个简单的任务管理系统 – 后端编码的更多相关文章

  1. ABP教程(四)- 开始一个简单的任务管理系统 - 实现UI端的增删改查

    接上一篇:ABP教程(三)- 开始一个简单的任务管理系统 – 后端编码 1.实现UI端的增删改查 1.1添加增删改查代码 打开SimpleTaskSystem.sln解决方案,添加一个“包含视图的MV ...

  2. Directx11教程(6) 画一个简单的三角形(2)

    原文:Directx11教程(6) 画一个简单的三角形(2)      在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变, ...

  3. Directx11教程(5) 画一个简单的三角形(1)

    原文:Directx11教程(5) 画一个简单的三角形(1)       在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作 ...

  4. 响应式编程笔记三:一个简单的HTTP服务器

    # 响应式编程笔记三:一个简单的HTTP服务器 本文我们将继续前面的学习,但将更多的注意力放在用例和编写实际能用的代码上面,而非基本的APIs学习. 我们会看到Reactive是一个有用的抽象 - 对 ...

  5. Directx11教程(19) 画一个简单的地形

    原文:Directx11教程(19) 画一个简单的地形       通常我们在xz平面定义一个二维的网格,然后y的值根据一定的函数计算得到,比如正弦.余弦函数的组合等等,可以得到一个看似不错的地形或者 ...

  6. PureMVC和Unity3D的UGUI制作一个简单的员工管理系统实例

    前言: 1.关于PureMVC: MVC框架在很多项目当中拥有广泛的应用,很多时候做项目前人开坑开了一半就消失了,后人为了填补各种的坑就遭殃的不得了.嘛,程序猿大家都不喜欢像文案策划一样组织文字写东西 ...

  7. php+js实现一个简单的用户管理系统

    php + js 实现一个简单的用户管理系统 说实话,我对PHP是抵触的,但是我们的WEB课程刚好学的就是这个,不得已看了看,下面是用PHP实现的一个简单的用户管理系统. 我们首先来看一下目录结构 a ...

  8. 使用 ADD-ON SDK 开发 基于 Html JQuery 和 CSS 的 firefox 插件入门教程1: 创建一个简单的 Add-on

    [本文转载自http://sixpoint.me/942/implementing-simple-addon/] 实现一个简单的插件 教程的这个部分带你使用 SDK 来实现, 运行并打包一个插件. 这 ...

  9. Linux内核分析 笔记三 构造一个简单的Linux系统MenuOS ——by王玥

    一.知识点总结 (一)Linux源代码简介 arch/x86目录下的代码是我们重点关注的 内核启动相关代码都在init目录下 start_kernel函数相当于普通C程序的main函数 linux的核 ...

随机推荐

  1. 精通CSS:高级Web标准解决方式(第2版)

    精通CSS:高级Web标准解决方式(第2版) 跳转至: 导航. 搜索 层叠重要度:(也就是说.用户!important能够覆盖inline style) !important.用户>作者.最后是 ...

  2. Arch Linux 下Android 源代码的下载以及编译

    之前把公司的开发环境由Ubuntu Kylin 换成了Arch Linux.而Arch 下由于种种问题公司的代码一直编只是去.搞定了之后也一直忘了写下来,希望能给相同在Arch 下做Android 开 ...

  3. Node.js 101(2): Promise and async

    --原文地址:http://blog.chrisyip.im/nodejs-101-package-promise-and-async 先回想一下 Sagase 的项目结构: lib/ cli.js ...

  4. HTML5中meta属性

    meta属性在HTML中占据了很重要的位置.如:针对搜索引擎的SEO,文档的字符编码,设置刷新缓存等.虽然一些网页可能没有使用meta,但是作为正规军,我们还是有必要了解一些meta的属性,并且能够熟 ...

  5. OSI模型与TCP/IP模型基础

    一.OSI七层模型 OSI(Open System Interconnection),OSI是一个开放性的通行系统互连参考模型,是一个协议规范.OSI七层模型是一种框架性的设计方法 ,建立七层模型的主 ...

  6. C#基础第一天 VS2013基本设置

    1 .net能干什么 桌面应用程序 winform internet应用程序 Asp.net->京东.msdn.招聘银行 手机开发 wp8 unity3D游戏开发或者虚拟现实 2 .net两种交 ...

  7. HDU 5916 Harmonic Value Description (构造)

    题意:给你 n 和 m,求一个1-n的排列,使得∑gcd(Ai,Ai+1) 恰为第  m 小. 析:可以想到最小的就是相邻都互质,然后依次,第 m 小就可以有一个是gcd为 k,然后其他的为1喽. 那 ...

  8. zoj 2532 Internship【最小割】

    就是求哪些边在最大流上满流,也就是找割边.把0作为t点,s向所有的1~n连流量为inf的边,其他的边按照流量连.跑一遍最大流,从s顺着有残余流量的正向边dfs打标记fr,从t顺着正向边有残余流量的反向 ...

  9. P4949 最短距离(树链剖分+树状数组+基环树)

    传送门 一个中午啊-- 本来打算用仙人掌搞的,后来发现直接基环树就可以了,把多出来的那条边单独记录为\((dx,dy,dw)\),剩下的树剖 然后最短路径要么直接树上跑,要么经过多出来的边,分别讨论就 ...

  10. ios手机Safari本地服务连不上

    问题: 今天在本地起服务准备测下ios手机端页面,结果发现:页面可以打开,但是登录不上. 用alert定位了下,await fn() 报错被try()catch(){}捕获了... 原因: 该机子不支 ...