- 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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我需要开发一个简单的网站,我通常使用 bootstrap CSS 框架,但是我想使用 Gumbyn,它允许我使用 16 列而不是 12 列。 我想知道是否: 我可以轻松地改变绿色吗? 如何使用固定布局
这个问题在这里已经有了答案: 关闭 13 年前。 与直接编写 PHP 代码相比,使用 PHP 框架有哪些优点/缺点?
我开发了一个 Spring/JPA 应用程序:服务、存储库和域层即将完成。 唯一缺少的层是网络层。我正在考虑将 Playframework 2.0 用于 Web 层,但我不确定是否可以在我的 Play
我现有的 struts Web 应用程序具有单点登录功能。然后我将使用 spring 框架创建一个不同的 Web 应用程序。然后想要使用从 struts 应用程序登录的用户来链接新的 spring 应
我首先使用Spark框架和ORMLite处理网页上表单提交的数据,在提交中文字符时看到了unicode问题。我首先想到问题可能是由于ORMLite,因为我的MySQL数据库的字符集已设置为使用utf8
我有一个使用 .Net 4.5 功能的模块,我们的应用程序也适用于 XP 用户。所以我正在考虑将这个 .net 4.5 依赖模块移动到单独的项目中。我怎样才能有一个解决方案,其中有两个项目针对不同的版
我知道这是一个非常笼统的问题,但我想我并不是真的在寻找明确的答案。作为 PHP 框架的新手,我很难理解它。 Javascript 框架,尤其是带有 UI 扩展的框架,似乎通过将 JS 代码与设计分开来
我需要收集一些关于现有 ORM 解决方案的信息。 请随意编写任何编程语言。 你能谈谈你用过的最好的 ORM 框架吗?为什么它比其他的更好? 最佳答案 我使用了 NHibernate 和 Entity
除了 Apple 的 SDK 之外,还有什么强大的 iPhone 框架可供开始开发?有没有可以加快开发时间的方法? 最佳答案 此类框架最大的是Three20 。 Facebook 和许多其他公司都使用
有人可以启发我使用 NodeJS 的 Web 框架吗?我最近开始从免费代码营学习express js,虽然一切进展顺利,但我对express到底是什么感到困惑。是全栈框架吗?纯粹是为了后端吗?我发现您
您可以推荐哪种 Ajax 框架/工具包来构建使用 struts 的 Web 应用程序的 GUI? 最佳答案 我会说你的 AJAX/javascript 库选择应该较少取决于你的后端是如何实现的,而更多
我有生成以下错误的 python 代码: objc[36554]: Class TKApplication is implemented in both /Library/Frameworks/Tk.
首先,很抱歉,如果我问的问题很明显,因为我没有编程背景,那我去吧: 我想运行一系列测试场景并在背景部分声明了几个变量(我打印它们以仔细检查它们是否已正确声明),第一个是整数,另外两个字符串为你可以看到
在我们承担的一个项目中,我们正在寻找一个视频捕获和录制库。我们的基础工作(基于 google 搜索)表明 vlc (libvlc)、ffmpeg (libavcodec) 和 gstreamer 是三
我试过没有运气的情况下寻找某种功能来杀死/中断Play中的正常工作!框架。 我想念什么吗?还是玩了!实际没有添加此功能? 最佳答案 Java stop类中没有像Thread方法那样的东西,由于种种原因
我们希望在我们的系统中保留所有重大事件的记录。例如,在数据库可能存储当前用户状态的地方,事件日志应记录对该状态的所有更改以及更改发生的时间。 事件记录工具应该尽可能接近于事件引发器的零开销,应该容纳结
那里有 ActionScript 2.0/3.0 的测试框架列表吗? 最佳答案 2010-05-18 更新 由于这篇文章有点旧,而且我刚刚收到了赞成票,因此可能值得提供一些更新的信息,这样人们就不会追
我有一个巨大的 numpy 数组列表(一维),它们是不同事件的时间序列。每个点都有一个标签,我想根据其标签对 numpy 数组进行窗口化。我的标签是 0、1 和 2。每个窗口都有一个固定的大小 M。
我是 Play 的新手!并编写了我的第一个应用程序。这个应用程序有一组它依赖的 URL,从 XML 响应中提取数据并返回有效的 URL。 此应用程序需要在不同的环境(Dev、Staging 和 Pro
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
我是一名优秀的程序员,十分优秀!