- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章基于ASP.NET MVC的ABP框架入门学习教程由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
为什么使用ABP 我们近几年陆续开发了一些Web应用和桌面应用,需求或简单或复杂,实现或优雅或丑陋。一个基本的事实是:我们只是积累了一些经验或提高了对,NET的熟悉程度。 随着软件开发经验的不断增加,我们发现其实很多工作都是重复机械的,而且随着软件复杂度的不断提升,以往依靠经验来完成一些简单的增删改查的做法已经行不通了。特别是用户的要求越来越高,希望添加的功能越来多,目前这种开发模式,已经捉襟见肘。我很难想象如何在现有的模式下进行多系统的持续集成并添加一些新的特性。 开发一个系统时,我们不可避免的会使用各种框架。数据持久层实现、日志、ASP.NET MVC、IOC以及自动映射等。一个高质量的软件系统往往还有全局容错,消息队列等组件。 把上述这些组件组合到一起的时候,其复杂度会急剧上升。一般个人和小团队的技术水平,很难设计出一个均衡协调的框架。对于传统的所谓三层架构,我也是很持怀疑态度的。(月薪15k的程序员搞的三层架构,我也仔细读过,也是问题多多,并不能解释为什么要使用三层)。 其实,我们无非是希望在编程的时候,把大部分的注意力全部集中到业务实现上。不要过多的考虑基础的软件结构上的种种问题。应该有一个框框或者一种范式来提供基本的服务,如日志、容错和AOP,DI等。 稍微正规一点的公司经过多年沉淀都形成了自己的内部软件框架,他们在开发软件的时候并不是从一片空白开始的。而是从一个非常牢固的基础平台上开始构建的。这样大大提高了开发速度,而且一种架构往往也决定了分工协作的模式。我们目前之所以无法分工协作,根本原因也是缺少一套成熟稳定的基础开发架构和工作流程。 目前.NET上有不少开源框架。比如Apworks和ABP。其中Apworks是中国人写的一套开源框架。它是一个全功能的,不仅可以写分布式应用,也可以写桌面应用。ABP的全称是Asp.net boilerplate project(asp.net样板工程)。是github上非常活跃的一个开源项目。它并没有使用任何新的技术,只是由两名架构师将asp.net开发中常用的一些工具整合到了一起,并且部分实现了DDD的概念。是一个开箱即用的框架,可以作为asp.net分布式应用的一个良好起点。 使用框架当然有代价,你必须受到框架强API的侵入,抑或要使用他的方言。而且这个框架想要吃透,也要付出很大的学习成本。但是好处也是显而易见的。业界顶尖的架构师已经为你搭建好了一套基础架构,很好的回应了关于一个软件系统应该如何设计,如何规划的问题,并且提供了一套最佳实践和范例。 学习虽然要付出成本,但是经过漫长的跋涉,我们从一无所知已经站到了工业级开发的门槛上。基于这个框架,我们可以很好的来划分任务,进行单元测试等。大大降低了软件出现BUG的几率.
从模板创建空的web应用程序 。
ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate ABP提供了一个启动模板用于新建的项目(尽管你能手动地创建项目并且从nuget获得ABP包,模板的方式更容易)。 转到www.aspnetboilerplate.com/Templates从模板创建你的应用程序。 你可以选择SPA(AngularJs或DurandalJs)或者选择MPA(经典的多页面应用程序)项目。可以选择Entity Framework或NHibernate作为ORM框架。 这里我们选择AngularJs和Entity Framework,填入项目名称“SimpleTaskSystem”,点击“CREATE MY PROJECT”按钮可以下载一个zip压缩包,解压后得到VS2013的解决方案,使用的.NET版本是 4.5.1.
每个项目里引用了Abp组件和其他第三方组件,需要从Nuget下载.
黄色感叹号图标,表示这个组件在本地文件夹中不存在,需要从Nuget上还原。操作如下:
要让项目运行起来,还得创建一个数据库。这个模板假设你正在使用SQL2008或者更新的版本。当然也可以很方便地换成其他的关系型数据库。 打开Web.Config文件可以查看和配置链接字符串:
。
。
<add name="Default" connectionString="Server=localhost; Database=SimpleTaskSystemDb; Trusted_Connection=True;" /> 。
。
。
创建实体 把实体类写在Core项目中,因为实体是领域层的一部分。 一个简单的应用场景:创建一些任务(tasks)并分配给人。 我们需要Task和Person这两个实体。 Task实体有几个属性:描述(Description)、创建时间(CreationTime)、任务状态(State),还有可选的导航属性(AssignedPerson)来引用Person.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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;
}
}
|
1
2
3
4
|
public
class
Person : Entity
{
public
virtual
string
Name {
get
;
set
; }
}
|
创建DbContext 使用EntityFramework需要先定义DbContext类,ABP的模板已经创建了DbContext文件,我们只需要把Task和Person类添加到IDbSet,请看代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
SimpleTaskSystemDbContext : AbpDbContext
{
public
virtual
IDbSet<Task> Tasks {
get
;
set
; }
public
virtual
IDbSet<Person> People {
get
;
set
; }
public
SimpleTaskSystemDbContext()
:
base
(
"Default"
)
{
}
public
SimpleTaskSystemDbContext(
string
nameOrConnectionString)
:
base
(nameOrConnectionString)
{
}
}
|
通过Database Migrations创建数据库表 我们使用EntityFramework的Code First模式创建数据库架构。ABP模板生成的项目已经默认开启了数据迁移功能,我们修改SimpleTaskSystem.EntityFramework项目下Migrations文件夹下的Configuration.cs文件:
internal sealed class Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
DbMigrationsConfiguration<SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext>
{
public
Configuration()
{
AutomaticMigrationsEnabled =
false
;
}
protected
override
void
Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
{
context.People.AddOrUpdate(
p => p.Name,
new
Person {Name =
"Isaac Asimov"
},
new
Person {Name =
"Thomas More"
},
new
Person {Name =
"George Orwell"
},
new
Person {Name =
"Douglas Adams"
}
);
}
}
|
。
在VS2013底部的“程序包管理器控制台”窗口中,选择默认项目并执行命令“Add-Migration InitialCreate” 。
会在Migrations文件夹下生成一个xxxx-InitialCreate.cs文件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public
partial
class
InitialCreate : DbMigration
{
public
override
void
Up()
{
CreateTable(
"dbo.StsPeople"
,
c =>
new
{
Id = c.Int(nullable:
false
, identity:
true
),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.StsTasks"
,
c =>
new
{
Id = c.Long(nullable:
false
, identity:
true
),
AssignedPersonId = c.Int(),
Description = c.String(),
CreationTime = c.DateTime(nullable:
false
),
State = c.Byte(nullable:
false
),
})
.PrimaryKey(t => t.Id)
.ForeignKey(
"dbo.StsPeople"
, t => t.AssignedPersonId)
.Index(t => t.AssignedPersonId);
}
public
override
void
Down()
{
DropForeignKey(
"dbo.StsTasks"
,
"AssignedPersonId"
,
"dbo.StsPeople"
);
DropIndex(
"dbo.StsTasks"
,
new
[] {
"AssignedPersonId"
});
DropTable(
"dbo.StsTasks"
);
DropTable(
"dbo.StsPeople"
);
}
}
|
。
1
|
PM> Update-Database
|
数据库显示如下:
(以后修改了实体,可以再次执行Add-Migration和Update-Database,就能很轻松的让数据库结构与实体类的同步) 。
定义仓储接口 通过仓储模式,可以更好把业务代码与数据库操作代码更好的分离,可以针对不同的数据库有不同的实现类,而业务代码不需要修改。 定义仓储接口的代码写到Core项目中,因为仓储接口是领域层的一部分。 我们先定义Task的仓储接口:
1
2
3
4
|
public
interface
ITaskRepository : IRepository<Task,
long
>
{
List<Task> GetAllWithPeople(
int
? assignedPersonId, TaskState? state);
}
|
它继承自ABP框架中的IRepository泛型接口。 在IRepository中已经定义了常用的增删改查方法:
所以ITaskRepository默认就有了上面那些方法。可以再加上它独有的方法GetAllWithPeople(...).
不需要为Person类创建一个仓储类,因为默认的方法已经够用了。ABP提供了一种注入通用仓储的方式,将在后面“创建应用服务”一节的TaskAppService类中看到.
实现仓储类 我们将在EntityFramework项目中实现上面定义的ITaskRepository仓储接口.
通过模板建立的项目已经定义了一个仓储基类:SimpleTaskSystemRepositoryBase(这是一种比较好的实践,因为以后可以在这个基类中添加通用的方法).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public
class
TaskRepository : SimpleTaskSystemRepositoryBase<Task,
long
>, ITaskRepository
{
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();
}
}
|
。
TaskRepository继承自SimpleTaskSystemRepositoryBase并且实现了上面定义的ITaskRepository接口.
创建应用服务(Application Services) 在Application项目中定义应用服务。首先定义Task的应用服务层的接口:
1
2
3
4
5
6
|
public
interface
ITaskAppService : IApplicationService
{
GetTasksOutput GetTasks(GetTasksInput input);
void
UpdateTask(UpdateTaskInput input);
void
CreateTask(CreateTaskInput input);
}
|
ITaskAppService继承自IApplicationService,ABP自动为这个类提供一些功能特性(比如依赖注入和参数有效性验证).
然后,我们写TaskAppService类来实现ITaskAppService接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
public
class
TaskAppService : ApplicationService, ITaskAppService
{
private
readonly
ITaskRepository _taskRepository;
private
readonly
IRepository<Person> _personRepository;
/// <summary>
/// 构造函数自动注入我们所需要的类或接口
/// </summary>
public
TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
{
_taskRepository = taskRepository;
_personRepository = personRepository;
}
public
GetTasksOutput GetTasks(GetTasksInput input)
{
//调用Task仓储的特定方法GetAllWithPeople
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
//用AutoMapper自动将List<Task>转换成List<TaskDto>
return
new
GetTasksOutput
{
Tasks = Mapper.Map<List<TaskDto>>(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);
}
}
|
TaskAppService使用仓储进行数据库操作,它通往构造函数注入仓储对象的引用.
数据验证 。
如果应用服务(Application Service)方法的参数对象实现了IInputDto或IValidate接口,ABP会自动进行参数有效性验证.
CreateTask方法有一个CreateTaskInput参数,定义如下:
1
2
3
4
5
6
7
|
public
class
CreateTaskInput : IInputDto
{
public
int
? AssignedPersonId {
get
;
set
; }
[Required]
public
string
Description {
get
;
set
; }
}
|
Description属性通过注解指定它是必填项。也可以使用其他 Data Annotation 特性.
如果你想使用自定义验证,你可以实现ICustomValidate 接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
UpdateTaskInput : IInputDto, ICustomValidate
{
[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"
}));
}
}
}
|
你可以在AddValidationErrors方法中写自定义验证的代码.
创建Web Api服务 ABP可以非常轻松地把Application Service的public方法发布成Web Api接口,可以供客户端通过ajax调用.
1
2
3
|
DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(
typeof
(SimpleTaskSystemApplicationModule)),
"tasksystem"
)
.Build();
|
SimpleTaskSystemApplicationModule这个程序集中所有继承了IApplicationService接口的类,都会自动创建相应的ApiController,其中的公开方法,就会转换成WebApi接口方法.
可以通过http://xxx/api/services/tasksystem/Task/GetTasks这样的路由地址进行调用.
通过上面的案例,大致介绍了领域层、基础设施层、应用服务层的用法.
现在,可以在ASP.NET MVC的Controller的Action方法中直接调用Application Service的方法了.
如果用SPA单页编程,可以直接在客户端通过ajax调用相应的Application Service的方法了(通过创建了动态Web Api).
最后此篇关于基于ASP.NET MVC的ABP框架入门学习教程的文章就讲到这里了,如果你想了解更多关于基于ASP.NET MVC的ABP框架入门学习教程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Hive —— 入门 Hive介绍 Apache Hive是一款建立在Hadoop之上的开源数据仓库系统,可以将存储在Hadoop文件中的结构化、半结构化数据文件映射为一张数据库表,基于表提供了一
HBase —— 入门 HBase介绍 HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”
零:前端目前形势 前端的发展史 HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) ->
在本教程中,您将了解在计算机上运行 JavaScript 的不同方法。 JavaScript 是一种流行的编程语言,具有广泛的应用程序。 JavaScript 以前主要用于使网页具有交
我曾经是一个对编程一窍不通的小白,但因为对互联网世界的好奇心和求知欲的驱使,我踏入了编程的殿堂。在学习的过程中,我发现了一门神奇的编程语言——Python。Python有着简洁、易读的语法,让初学者能
嗨,亲爱的读者们! 今天我要给大家分享一些关于Python爬虫的小案例。你是否曾为了获取特定网页上的数据而烦恼过?或者是否好奇如何从网页中提取信息以供自己使用?那么,这篇文章将会给你一些启示和灵感。
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
我想创建一个像https://apprtc.appspot.com/?r=04188292这样的应用程序。我对 webrtc 了解一点,但无法掌握 google app-engine。如何为 java
我刚刚开始使用 Python 并编写了一个简单的周边程序。但是,每当我在终端中键入 python perimeter.py 时,都会收到以下错误,我不知道如何解决。 >>> python perime
Redis有5个基本数据结构,string、list、hash、set和zset。它们是日常开发中使用频率非常高应用最为广泛的数据结构,把这5个数据结构都吃透了,你就掌握了Redis应用知识的一半了
创建发布web项目 具体步骤: 1.在开发工具中创建一个dynamic web project helloword 2.在webContent中创建index.html文件 3.发布web应用到
如果你在 Ubuntu 上使用终端的时间很长,你可能会希望调整终端的字体和大小以获取一种良好的体验。 更改字体是一种最简单但最直观的 Linux 的终端自定义 的方法。让我
1. 前言 ADODB 是 Active Data Objects Data Base 的简称,它是一种 PHP 存取数据库的函式组件。现在 SFS3 系统 (校园自由软件交流网学务系统) 计划的
我对 neo4j 完全陌生,我很抱歉提出这样一个基本问题。我已经安装了neo4j,我正在使用shell“localhost:7474/webadmin/#/console/” 我正在寻找一个很好的例子
我正在阅读 ios 4 的核心音频,目的是构建一个小测试应用程序。 在这一点上,我对所有 api 的研究感到非常困惑。理想情况下,我想知道如何从两个 mp3 中提取一些样本到数组中。 然后在回调循环中
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是无关紧要的,因
我下载了 GNUStep并安装了它,但是我不确定在哪里可以找到 IDE。有谁知道什么程序可以用作 GNUStep IDE/从哪里获取它们?否则,有没有人知道有关如何创建和编译基本 GNUStep 程序
我正在尝试开始使用 Apache Solr,但有些事情我不清楚。通读tutorial ,我已经设置了一个正在运行的 Solr 实例。我感到困惑的是 Solr 的所有配置(架构等)都是 XML 格式的。
请问有没有关于如何开始使用 BruTile 的文档? 我目前正在使用 SharpMap,我需要预缓存切片以加快进程 最佳答案 我今天正在研究这个:)Mapsui项目site严重依赖 SharpMap
尽我所能,我无法让 CEDET 做任何事情。 Emacs 24.3。我下载了最新的 CEDET 快照。我从他的底部(不是这样)Gentle Introduction 中获取了 Alex Ott 的设置
我是一名优秀的程序员,十分优秀!