基於ASP.NET MVC的ABP架構入門學習教程_實用技巧

來源:互聯網
上載者:User

為什麼使用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;" />

(在後面用到EF的Code first資料移轉時,會自動在SQL Server資料庫中建立一個名為SimpleTaskSystemDb的資料庫。)
就這樣,項目已經準備好運行了!開啟VS2013並且按F5:
下面將逐步實現這個簡單的任務系統程式

建立實體
把實體類寫在Core項目中,因為實體是領域層的一部分。
一個簡單的應用情境:建立一些任務(tasks)並分配給人。 我們需要Task和Person這兩個實體。
Task實體有幾個屬性:描述(Description)、建立時間(CreationTime)、任務狀態(State),還有可選的導覽屬性(AssignedPerson)來引用Person。

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;  }}
Person實體更簡單,只定義了一個Name屬性:
public class Person : Entity{  public virtual string Name { get; set; }}
在ABP架構中,有一個Entity基類,它有一個Id屬性。因為Task類繼承自Entity<long>,所以它有一個long類型的Id。Person類有一個int類型的Id,因為int類型是Entity基類Id的預設類型,沒有特別指定類型時,實體的Id就是int類型。

建立DbContext
使用EntityFramework需要先定義DbContext類,ABP的模板已經建立了DbContext檔案,我們只需要把Task和Person類添加到IDbSet,請看代碼:

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 :

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檔案,內容如下:

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");  }}

然後繼續在“封裝管理員控制台”執行“Update-Database”,會自動在資料庫建立相應的資料表:
PM> Update-Database

資料庫顯示如下:

(以後修改了實體,可以再次執行Add-Migration和Update-Database,就能很輕鬆的讓資料庫結構與實體類的同步)

定義倉儲介面
通過倉儲模式,可以更好把業務代碼與資料庫作業碼更好的分離,可以針對不同的資料庫有不同的實作類別,而業務代碼不需要修改。
定義倉儲介面的代碼寫到Core項目中,因為倉儲介面是領域層的一部分。
我們先定義Task的倉儲介面:

public interface ITaskRepository : IRepository<Task, long>{  List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state);}

它繼承自ABP架構中的IRepository泛型介面。
在IRepository中已經定義了常用的增刪改查方法:

所以ITaskRepository預設就有了上面那些方法。可以再加上它專屬的方法GetAllWithPeople(...)。

不需要為Person類建立一個倉儲類,因為預設的方法已經夠用了。ABP提供了一種注入通用倉儲的方式,將在後面“建立應用服務”一節的TaskAppService類中看到。

實現倉儲類
我們將在EntityFramework項目中實現上面定義的ITaskRepository倉儲介面。

通過模板建立的項目已經定義了一個倉儲基類:SimpleTaskSystemRepositoryBase(這是一種比較好的實踐,因為以後可以在這個基類中添加通用的方法)。

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的應用服務層的介面:

public interface ITaskAppService : IApplicationService{  GetTasksOutput GetTasks(GetTasksInput input);  void UpdateTask(UpdateTaskInput input);  void CreateTask(CreateTaskInput input);}

ITaskAppService繼承自IApplicationService,ABP自動為這個類提供一些功能特性(比如依賴注入和參數有效性驗證)。

然後,我們寫TaskAppService類來實現ITaskAppService介面:

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參數,定義如下:

public class CreateTaskInput : IInputDto{  public int? AssignedPersonId { get; set; }  [Required]  public string Description { get; set; }}

Description屬性通過註解指定它是必填項。也可以使用其他 Data Annotation 特性。

如果你想使用自訂驗證,你可以實現ICustomValidate 介面:

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調用。

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)。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.