• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

    ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解

    來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 22:35:18
    文檔

    ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解

    ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解:ABP是ASP.NET Boilerplate Project (ASP.NET樣板項目)的簡稱。 ASP.NET Boilerplate是一個用最佳實踐和流行技術(shù)開發(fā)現(xiàn)代WEB應(yīng)用程序的新起點,它旨在成為一個通用的WEB應(yīng)用程序框架和項目模板。 ABP的官方網(wǎng)站:http://www.aspnetb
    推薦度:
    導(dǎo)讀ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解:ABP是ASP.NET Boilerplate Project (ASP.NET樣板項目)的簡稱。 ASP.NET Boilerplate是一個用最佳實踐和流行技術(shù)開發(fā)現(xiàn)代WEB應(yīng)用程序的新起點,它旨在成為一個通用的WEB應(yīng)用程序框架和項目模板。 ABP的官方網(wǎng)站:http://www.aspnetb

    ABP是“ASP.NET Boilerplate Project (ASP.NET樣板項目)”的簡稱。

    ASP.NET Boilerplate是一個用最佳實踐和流行技術(shù)開發(fā)現(xiàn)代WEB應(yīng)用程序的新起點,它旨在成為一個通用的WEB應(yīng)用程序框架和項目模板。

    ABP的官方網(wǎng)站:http://www.aspnetboilerplate.com

    ABP在Github上的開源項目:https://github.com/aspnetboilerplate

    ABP 的由來

    “DRY——避免重復(fù)代碼”是一個優(yōu)秀的開發(fā)者在開發(fā)軟件時所具備的最重要的思想之一。我們在開發(fā)企業(yè)WEB應(yīng)用程序時都有一些類似的需求,例如:都需要登錄頁面、用戶/角色管理、權(quán)限驗證、數(shù)據(jù)有效性驗證、多語言/本地化等等。一個高品質(zhì)的大型軟件都會運用一些最佳實踐,例如分層體系結(jié)構(gòu)、領(lǐng)域驅(qū)動設(shè)計、依賴注入等。我們也可能會采用ORM、數(shù)據(jù)庫遷移(Database Migrations)、日志記錄(Logging)等工具。

    從零開始創(chuàng)建一個企業(yè)應(yīng)用程序是一件繁瑣的事,因為需要重復(fù)做很多常見的基礎(chǔ)工作。許多公司都在開發(fā)自己的應(yīng)用程序框架來重用于不同的項目,然后在框架的基礎(chǔ)上開發(fā)一些新的功能。但并不是每個公司都有這樣的實力。假如我們可以分享的更多,也許可以避免每個公司或每個項目的重復(fù)編寫類似的代碼。作者之所以把項目命名為“ASP.NET Boilerplate”,就是希望它能成為開發(fā)一般企業(yè)WEB應(yīng)用的新起點,直接把ABP作為項目模板。

    ABP是什么?

    ABP是為新的現(xiàn)代Web應(yīng)用程序使用最佳實踐和使用最流行工具的一個起點。可作為一般用途的應(yīng)用程序的基礎(chǔ)框架或項目模板。它的功能包括:

    服務(wù)器端:

  • 基于最新的.NET技術(shù) (目前是ASP.NET MVC 5、Web API 2、C# 5.0,在ASP.NET 5正式發(fā)布后會升級)
  • 實現(xiàn)領(lǐng)域驅(qū)動設(shè)計(實體、倉儲、領(lǐng)域服務(wù)、領(lǐng)域事件、應(yīng)用服務(wù)、數(shù)據(jù)傳輸對象,工作單元等等)
  • 實現(xiàn)分層體系結(jié)構(gòu)(領(lǐng)域?qū)樱瑧?yīng)用層,展現(xiàn)層和基礎(chǔ)設(shè)施層)提供了一個基礎(chǔ)架構(gòu)來開發(fā)可重用可配置的模塊集成一些最流行的開源框架/庫,也許有些是你正在使用的。
  • 提供了一個基礎(chǔ)架構(gòu)讓我們很方便地使用依賴注入(使用Castle Windsor作為依賴注入的容器)
  • 提供Repository倉儲模式支持不同的ORM(已實現(xiàn)Entity Framework 、NHibernate、MangoDb和內(nèi)存數(shù)據(jù)庫)
  • 支持并實現(xiàn)數(shù)據(jù)庫遷移(EF 的 Code first)模塊化開發(fā)(每個模塊有獨立的EF DbContext,可單獨指定數(shù)據(jù)庫)
  • 包括一個簡單的和靈活的多語言/本地化系統(tǒng)
  • 包括一個 EventBus來實現(xiàn)服務(wù)器端全局的領(lǐng)域事件統(tǒng)一的異常處理(應(yīng)用層幾乎不需要處理自己寫異常處理代碼)
  • 數(shù)據(jù)有效性驗證(Asp.NET MVC只能做到Action方法的參數(shù)驗證,ABP實現(xiàn)了Application層方法的參數(shù)有效性驗證)
  • 通過Application Services自動創(chuàng)建Web Api層(不需要寫ApiController層了)
  • 提供基類和幫助類讓我們方便地實現(xiàn)一些常見的任務(wù)
  • 使用“約定優(yōu)于配置原則”
  • 客戶端:

  • Bootstrap、Less、AngularJs、jQuery、Modernizr和其他JS庫: jQuery.validate、jQuery.form、jQuery.blockUI、json2等
  • 為單頁面應(yīng)用程序(AngularJs、Durandaljs)和多頁面應(yīng)用程序(Bootstrap+Jquery)提供了項目模板。
  • 自動創(chuàng)建Javascript 的代理層來更方便使用Web Api封裝一些Javascript 函數(shù),更方便地使用ajax、消息框、通知組件、忙狀態(tài)的遮罩層等等
  • 除ABP框架項目以外,還開發(fā)了名叫“Zero”的模塊,實現(xiàn)了以下功能:

  • 身份驗證與授權(quán)管理(通過ASP.NET Identity實現(xiàn)的)
  • 用戶&角色管理系統(tǒng)設(shè)置存取管理(系統(tǒng)級、租戶級、用戶級,作用范圍自動管理)
  • 審計日志(自動記錄每一次接口的調(diào)用者和參數(shù))
  • ABP不是什么?

    ABP提供了一個應(yīng)用程序開發(fā)模型用于最佳實踐。它擁有基礎(chǔ)類、接口和工具使我們?nèi)菀捉⑵鹂删S護的大規(guī)模的應(yīng)用程序。

    然而:

    它不是RAD工具之一,RAD工具的目的是無需編碼創(chuàng)建應(yīng)用程序。相反,ABP提供了一種編碼的最佳實踐。

    它不是一個代碼生成工具。在運行時雖然它有一些特性構(gòu)建動態(tài)代碼,但它不能生成代碼。

    它不是一個一體化的框架。相反,它使用流行的工具/庫來完成特定的任務(wù)(例如用EF做ORM,用Log4Net做日志記錄,使得Castle Windsor作為賴注入容器, AngularJs 用于SPA 框架)。

    就我使用了ABP幾個月的經(jīng)驗來看,雖然ABP不是RAD,但是用它開發(fā)項目絕對比傳統(tǒng)三層架構(gòu)要快很多。

    雖然ABP不是代碼生成工具,但因為有了它,使我們項目的代碼更簡潔規(guī)范,這有利于使用代碼生成工具。

    我自己使用VS2013的Scaffolder+T4開發(fā)的代碼生成器,可根據(jù)領(lǐng)域?qū)ο蟮腢ML類圖自動生成全部前后端代碼和數(shù)據(jù)庫,簡單的CURD模塊幾乎不需要編寫代碼,有復(fù)雜業(yè)務(wù)邏輯的模塊主要補充領(lǐng)域?qū)哟a即可。這樣就能把時間多花在領(lǐng)域模型的設(shè)計上,減少寫代碼的時間。

    下面通過原作者的“簡單任務(wù)系統(tǒng)”例子,演示如何運用ABP開發(fā)項目

    從模板創(chuàng)建空的web應(yīng)用程序

    ABP提供了一個啟動模板用于新建的項目(盡管你能手動地創(chuàng)建項目并且從nuget獲得ABP包,模板的方式更容易)。

    轉(zhuǎn)到www.aspnetboilerplate.com/Templates從模板創(chuàng)建你的應(yīng)用程序。

    你可以選擇SPA(AngularJs或DurandalJs)或者選擇MPA(經(jīng)典的多頁面應(yīng)用程序)項目。可以選擇Entity Framework或NHibernate作為ORM框架。

    這里我們選擇AngularJs和Entity Framework,填入項目名稱“SimpleTaskSystem”,點擊“CREATE MY PROJECT”按鈕可以下載一個zip壓縮包,解壓后得到VS2013的解決方案,使用的.NET版本是 4.5.1。

    每個項目里引用了Abp組件和其他第三方組件,需要從Nuget下載。

    黃色感嘆號圖標(biāo),表示這個組件在本地文件夾中不存在,需要從Nuget上還原。操作如下:

    要讓項目運行起來,還得創(chuàng)建一個數(shù)據(jù)庫。這個模板假設(shè)你正在使用SQL2008或者更新的版本。當(dāng)然也可以很方便地換成其他的關(guān)系型數(shù)據(jù)庫。

    打開Web.Config文件可以查看和配置鏈接字符串:

    <add name="Default" connectionString="Server=localhost; Database=SimpleTaskSystemDb; Trusted_Connection=True;" />

    (在后面用到EF的Code first數(shù)據(jù)遷移時,會自動在SQL Server數(shù)據(jù)庫中創(chuàng)建一個名為SimpleTaskSystemDb的數(shù)據(jù)庫。)

    就這樣,項目已經(jīng)準(zhǔn)備好運行了!打開VS2013并且按F5:

    下面將逐步實現(xiàn)這個簡單的任務(wù)系統(tǒng)程序

    創(chuàng)建實體

    把實體類寫在Core項目中,因為實體是領(lǐng)域?qū)拥囊徊糠帧?/p>

    一個簡單的應(yīng)用場景:創(chuàng)建一些任務(wù)(tasks)并分配給人。 我們需要Task和Person這兩個實體。

    Task實體有幾個屬性:描述(Description)、創(chuàng)建時間(CreationTime)、任務(wù)狀態(tài)(State),還有可選的導(dǎo)航屬性(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類型。

    創(chuàng)建DbContext

    使用EntityFramework需要先定義DbContext類,ABP的模板已經(jīng)創(chuàng)建了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創(chuàng)建數(shù)據(jù)庫表

    我們使用EntityFramework的Code First模式創(chuàng)建數(shù)據(jù)庫架構(gòu)。ABP模板生成的項目已經(jīng)默認開啟了數(shù)據(jù)遷移功能,我們修改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底部的“程序包管理器控制臺”窗口中,選擇默認項目并執(zhí)行命令“Add-Migration InitialCreate”

    會在Migrations文件夾下生成一個xxxx-InitialCreate.cs文件,內(nèi)容如下:

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

    然后繼續(xù)在“程序包管理器控制臺”執(zhí)行“Update-Database”,會自動在數(shù)據(jù)庫創(chuàng)建相應(yīng)的數(shù)據(jù)表:

    PM> Update-Database

    數(shù)據(jù)庫顯示如下:

    (以后修改了實體,可以再次執(zhí)行Add-Migration和Update-Database,就能很輕松的讓數(shù)據(jù)庫結(jié)構(gòu)與實體類的同步)

    定義倉儲接口

    通過倉儲模式,可以更好把業(yè)務(wù)代碼與數(shù)據(jù)庫操作代碼更好的分離,可以針對不同的數(shù)據(jù)庫有不同的實現(xiàn)類,而業(yè)務(wù)代碼不需要修改。

    定義倉儲接口的代碼寫到Core項目中,因為倉儲接口是領(lǐng)域?qū)拥囊徊糠帧?/p>

    我們先定義Task的倉儲接口:

    public interface ITaskRepository : IRepository<Task, long>
    {

    它繼承自ABP框架中的IRepository泛型接口。

    在IRepository中已經(jīng)定義了常用的增刪改查方法:

    所以ITaskRepository默認就有了上面那些方法。可以再加上它獨有的方法GetAllWithPeople(...)。

    不需要為Person類創(chuàng)建一個倉儲類,因為默認的方法已經(jīng)夠用了。ABP提供了一種注入通用倉儲的方式,將在后面“創(chuàng)建應(yīng)用服務(wù)”一節(jié)的TaskAppService類中看到。

    實現(xiàn)倉儲類

    我們將在EntityFramework項目中實現(xiàn)上面定義的ITaskRepository倉儲接口。

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

    public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository
    {
     public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state)
     {
     //在倉儲方法中,不用處理數(shù)據(jù)庫連接、DbContext和數(shù)據(jù)事務(wù),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并且實現(xiàn)了上面定義的ITaskRepository接口。

    創(chuàng)建應(yīng)用服務(wù)(Application Services)

    在Application項目中定義應(yīng)用服務(wù)。首先定義Task的應(yīng)用服務(wù)層的接口:

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

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

    然后,我們寫TaskAppService類來實現(xiàn)ITaskAppService接口:

    public class TaskAppService : ApplicationService, ITaskAppService
    {
     private readonly ITaskRepository _taskRepository;
     private readonly IRepository<Person> _personRepository;
     
     /// <summary>
     /// 構(gòu)造函數(shù)自動注入我們所需要的類或接口
     /// </summary>
     public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
     {
     _taskRepository = taskRepository;
     _personRepository = personRepository;
     }
     
     public GetTasksOutput GetTasks(GetTasksInput input)
     {
     //調(diào)用Task倉儲的特定方法GetAllWithPeople
     var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
    
     //用AutoMapper自動將List<Task>轉(zhuǎn)換成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);
     }
    
     //我們都不需要調(diào)用Update方法
     //因為應(yīng)用服務(wù)層的方法默認開啟了工作單元模式(Unit of Work)
     //ABP框架會工作單元完成時自動保存對實體的所有更改,除非有異常拋出。有異常時會自動回滾,因為工作單元默認開啟數(shù)據(jù)庫事務(wù)。
     }
    
     public void CreateTask(CreateTaskInput input)
     {
     Logger.Info("Creating a task for input: " + input);
    
     //通過輸入?yún)?shù),創(chuàng)建一個新的Task實體
     var task = new Task { Description = input.Description };
    
     if (input.AssignedPersonId.HasValue)
     {
     task.AssignedPersonId = input.AssignedPersonId.Value;
     }
    
     //調(diào)用倉儲基類的Insert方法把實體保存到數(shù)據(jù)庫中
     _taskRepository.Insert(task);
     }
    }

    TaskAppService使用倉儲進行數(shù)據(jù)庫操作,它通往構(gòu)造函數(shù)注入倉儲對象的引用。

    數(shù)據(jù)驗證

    如果應(yīng)用服務(wù)(Application Service)方法的參數(shù)對象實現(xiàn)了IInputDto或IValidate接口,ABP會自動進行參數(shù)有效性驗證。

    CreateTask方法有一個CreateTaskInput參數(shù),定義如下:

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

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

    如果你想使用自定義驗證,你可以實現(xiàn)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方法中寫自定義驗證的代碼。

    創(chuàng)建Web Api服務(wù)

    ABP可以非常輕松地把Application Service的public方法發(fā)布成Web Api接口,可以供客戶端通過ajax調(diào)用。

    DynamicApiControllerBuilder
     .ForAll<IApplicationService>(Assembly.GetAssembly(typeof (SimpleTaskSystemApplicationModule)), "tasksystem")
     .Build();

    SimpleTaskSystemApplicationModule這個程序集中所有繼承了IApplicationService接口的類,都會自動創(chuàng)建相應(yīng)的ApiController,其中的公開方法,就會轉(zhuǎn)換成WebApi接口方法。

    可以通過http://xxx/api/services/tasksystem/Task/GetTasks這樣的路由地址進行調(diào)用。

    通過上面的案例,大致介紹了領(lǐng)域?qū)印⒒A(chǔ)設(shè)施層、應(yīng)用服務(wù)層的用法。

    現(xiàn)在,可以在ASP.NET MVC的Controller的Action方法中直接調(diào)用Application Service的方法了。

    如果用SPA單頁編程,可以直接在客戶端通過ajax調(diào)用相應(yīng)的Application Service的方法了(通過創(chuàng)建了動態(tài)Web Api)。

    總結(jié)

    以上所述是小編給大家介紹的ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解

    ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解:ABP是ASP.NET Boilerplate Project (ASP.NET樣板項目)的簡稱。 ASP.NET Boilerplate是一個用最佳實踐和流行技術(shù)開發(fā)現(xiàn)代WEB應(yīng)用程序的新起點,它旨在成為一個通用的WEB應(yīng)用程序框架和項目模板。 ABP的官方網(wǎng)站:http://www.aspnetb
    推薦度:
    標(biāo)簽: 現(xiàn)代 框架 ASP.NET
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 精品免费久久久久国产一区| 无码日韩精品一区二区免费暖暖| 麻豆精品三级全部视频 | 欧美午夜精品久久久久久浪潮| 大伊香蕉精品视频在线导航| 中文精品久久久久人妻不卡| 精品日本一区二区三区在线观看| 久久亚洲欧美日本精品| 国产午夜无码精品免费看 | 国产精品久久久亚洲| 亚洲欧美日韩国产精品| 精品a在线观看| 国产精品亚洲专区无码WEB| 91久久精品电影| 欧美一区二区精品| 日本欧美韩国日本精品| 99re国产精品视频首页| 精品永久久福利一区二区| 亚洲欧美日韩国产精品专区| 亚洲国产成人精品久久久国产成人一区二区三区综 | 精品伦精品一区二区三区视频 | 欧美午夜精品久久久久久浪潮| 国产亚洲精品拍拍拍拍拍| 国产精品国产高清国产专区| 亚洲国产精品线在线观看| 久久精品视频免费| 性色精品视频网站在线观看| 久久99热精品| 精品综合久久久久久97超人| 国产精品第13页| 国产韩国精品一区二区三区久久 | 亚洲国产精品无码专区| 亚洲精品中文字幕无码蜜桃| 最新精品露脸国产在线| 无码精品第一页| 亚洲无码精品浪潮| 亚洲综合精品香蕉久久网| 少妇人妻无码精品视频| 久久丫精品国产亚洲av| 高清免费久久午夜精品| 99免费精品视频|