ABP開發(fā)框架前后端開發(fā)系列---(2)框架的初步介紹

在前面隨筆《ABP開發(fā)框架前后端開發(fā)系列---(1)框架的總體介紹》大概介紹了這個(gè)ABP框架的主要特點(diǎn),以及介紹了我對(duì)這框架的Web API應(yīng)用優(yōu)先的一些看法氛雪,本篇繼續(xù)探討ABP框架的初步使用房匆,也就是我們下載到的ABP框架項(xiàng)目(基于ABP基礎(chǔ)項(xiàng)目的擴(kuò)展項(xiàng)目),如果理解各個(gè)組件模塊报亩,以及如何使用浴鸿。

1)ABP框架應(yīng)用項(xiàng)目的介紹

整個(gè)基礎(chǔ)的ABP框架看似非常龐大,其實(shí)很多項(xiàng)目也很少內(nèi)容弦追,主要是獨(dú)立封裝不同的組件進(jìn)行使用岳链,如Automaper、SignalR劲件、MongoDB掸哑、Quartz。零远。苗分。等等內(nèi)容,基本上我們主要關(guān)注的內(nèi)容就是Abp這個(gè)主要的項(xiàng)目里面牵辣,其他的是針對(duì)不同的組件應(yīng)用做的封裝摔癣。

image

而基于基礎(chǔ)ABP框架擴(kuò)展出來(lái)的ABP應(yīng)用項(xiàng)目,則簡(jiǎn)單很多纬向,我們也是在需要用到不同組件的時(shí)候择浊,才考慮引入對(duì)應(yīng)的基礎(chǔ)模塊進(jìn)行使用,一般來(lái)說逾条,主要還是基于倉(cāng)儲(chǔ)管理實(shí)現(xiàn)基于數(shù)據(jù)庫(kù)的應(yīng)用琢岩,因此我們主要對(duì)微軟的實(shí)體框架的相關(guān)內(nèi)容了解清楚即可。

image

這個(gè)項(xiàng)目是一個(gè)除了包含基礎(chǔ)的人員师脂、角色担孔、權(quán)限江锨、認(rèn)證、配置信息的基礎(chǔ)項(xiàng)目外攒磨,而如果你從這里開始泳桦,對(duì)于其中的一些繼承關(guān)系的了解,會(huì)增加很多困難娩缰,因?yàn)樗鼈兓A(chǔ)的用戶灸撰、角色等對(duì)象關(guān)系實(shí)在是很復(fù)雜。

我建議從一個(gè)簡(jiǎn)單的項(xiàng)目開始拼坎,也就是基于一兩個(gè)特定的應(yīng)用表開始的項(xiàng)目浮毯,因此可以參考案例項(xiàng)目:eventcloud 或者 sample-blog-module 項(xiàng)目,我們?nèi)腴T理解起來(lái)可能更加清楚泰鸡。這里我以eventcloud項(xiàng)目來(lái)進(jìn)行分析項(xiàng)目中各個(gè)層的類之間的關(guān)系债蓝。

image

我們先從一個(gè)關(guān)系圖來(lái)了解下框架下的領(lǐng)域驅(qū)動(dòng)模塊中的各個(gè)類之間的關(guān)系。

image

先以領(lǐng)域?qū)邮⒘洌簿褪琼?xiàng)目中的EventCloud.Core里面的內(nèi)容進(jìn)行分析饰迹。

2)領(lǐng)域?qū)ο髮拥拇a分析

首先,我們需要了解領(lǐng)域?qū)ο蠛蛿?shù)據(jù)庫(kù)之間的關(guān)系的類余舶,也就是領(lǐng)域?qū)嶓w信息啊鸭,這個(gè)類非常關(guān)鍵,它是構(gòu)建倉(cāng)儲(chǔ)模式和數(shù)據(jù)庫(kù)表之間的關(guān)系的匿值。

    [Table("AppEvents")]
    public class Event : FullAuditedEntity<Guid>, IMustHaveTenant
    {
        public virtual int TenantId { get; set; }

        [Required]
        [StringLength(MaxTitleLength)]
        public virtual string Title { get; protected set; }

        [StringLength(MaxDescriptionLength)]
        public virtual string Description { get; protected set; }

        public virtual DateTime Date { get; protected set; }

        public virtual bool IsCancelled { get; protected set; }
     
        ......
   }

這個(gè)里面定義了領(lǐng)域?qū)嶓w和表名之間的關(guān)系赠制,其他屬性也就是對(duì)應(yīng)數(shù)據(jù)庫(kù)的字段了

[Table("AppEvents")]

然后在EventCloud.EntityFrameworkCore項(xiàng)目里面,加入這個(gè)表的DbSet對(duì)象挟憔,如下代碼所示钟些。

namespace EventCloud.EntityFrameworkCore
{
    public class EventCloudDbContext : AbpZeroDbContext<Tenant, Role, User, EventCloudDbContext>
    {
        public virtual DbSet<Event> Events { get; set; }

        public virtual DbSet<EventRegistration> EventRegistrations { get; set; }

        public EventCloudDbContext(DbContextOptions<EventCloudDbContext> options)
            : base(options)
        {
        }
    }
}

簡(jiǎn)單的話,倉(cāng)儲(chǔ)模式就可以跑起來(lái)了绊谭,我們利用 IRepository<Event, Guid> 接口就可以獲取對(duì)應(yīng)表的很多處理接口政恍,包括增刪改查、分頁(yè)等等接口达传,不過為了進(jìn)行業(yè)務(wù)邏輯的隔離篙耗,我們引入了Application Service應(yīng)用層,同時(shí)也引入了DTO(數(shù)據(jù)傳輸對(duì)象)的概念趟大,以便向應(yīng)用層隱藏我們的領(lǐng)域?qū)ο笮畔ⅲ瑢?shí)現(xiàn)更加彈性化的處理铣焊。一般和領(lǐng)域?qū)ο髮?duì)應(yīng)的DTO對(duì)象定義如下所示逊朽。

    [AutoMapFrom(typeof(Event))]
    public class EventListDto : FullAuditedEntityDto<Guid>
    {
        public string Title { get; set; }

        public string Description { get; set; }

        public DateTime Date { get; set; }

        public bool IsCancelled { get; set; }

        public virtual int MaxRegistrationCount { get; protected set; }

        public int RegistrationsCount { get; set; }
    }

其中我們需要注意實(shí)體類繼承自FullAuditedEntityDto<Guid>,它標(biāo)記這個(gè)領(lǐng)域?qū)ο髸?huì)記錄創(chuàng)建曲伊、修改叽讳、刪除的標(biāo)記追他、時(shí)間和人員信息,如果需要深入了解這個(gè)部分岛蚤,可以參考下ABP官網(wǎng)關(guān)于領(lǐng)域?qū)嶓w對(duì)象的介紹內(nèi)容(Entities)邑狸。

通過在類增加標(biāo)記性的特性處理,我們可以從Event領(lǐng)域?qū)ο蟮紼ventListDto的對(duì)象實(shí)現(xiàn)了自動(dòng)化的映射涤妒。這樣的定義處理单雾,一般來(lái)說沒有什么問題,但是如果我們需要把DTO(如EventListDto)隔離和領(lǐng)域?qū)ο螅ㄈ鏓vent)的關(guān)系她紫,把DTO單獨(dú)抽取來(lái)方便公用硅堆,那么我們可以在應(yīng)用服務(wù)層定義一個(gè)領(lǐng)域?qū)ο蟮挠成湮募?lái)替代這種聲明式的映射關(guān)系,AutoMaper的映射文件定義如下所示贿讹。

    public class EventMapProfile : Profile
    {
        public EventMapProfile()
        {
            CreateMap<EventListDto, Event>();
            CreateMap<EventDetailOutput, Event>();
            CreateMap<EventRegistrationDto, EventRegistration>();
        }
    }

這樣抽取獨(dú)立的映射文件渐逃,可以為我們單獨(dú)抽取DTO對(duì)象和應(yīng)用層接口作為一個(gè)獨(dú)立項(xiàng)目提供方便,因?yàn)椴恍枰蕾囶I(lǐng)域?qū)嶓w民褂。如我改造項(xiàng)目的DTO層實(shí)例如下所示茄菊。

image

剛才介紹了領(lǐng)域?qū)嶓w和DTO對(duì)象的映射關(guān)系,就是為了給應(yīng)用服務(wù)層提供數(shù)據(jù)的承載赊堪。

如果領(lǐng)域?qū)ο蟮倪壿嬏幚肀容^復(fù)雜一些面殖,還可以定義一個(gè)類似業(yè)務(wù)邏輯類(類似我們說說的BLL),一般ABP框架里面以Manager結(jié)尾的就是這個(gè)概念雹食,如對(duì)于案例里面畜普,業(yè)務(wù)邏輯接口和邏輯類定義如下所示,這里注意接口繼承自IDomainService接口群叶。


    /// <summary>
    /// Event的業(yè)務(wù)邏輯類
    /// </summary>
    public interface IEventManager: IDomainService
    {
        Task<Event> GetAsync(Guid id);
        Task CreateAsync(Event @event);
        void Cancel(Event @event);
        Task<EventRegistration> RegisterAsync(Event @event, User user);
        Task CancelRegistrationAsync(Event @event, User user);
        Task<IReadOnlyList<User>> GetRegisteredUsersAsync(Event @event);
    }

業(yè)務(wù)邏輯類的實(shí)現(xiàn)如下所示吃挑。

image

我們看到這個(gè)類的構(gòu)造函數(shù)里面,帶入了幾個(gè)接口對(duì)象的參數(shù)街立,這個(gè)就是DI舶衬,依賴注入的概念,這些通過IOC容易進(jìn)行構(gòu)造函數(shù)的注入赎离,我們只需要知道逛犹,在模塊啟動(dòng)后,這些接口都可以使用就可以了梁剔,如果需要了解更深入的虽画,可以參考ABP官網(wǎng)對(duì)于依賴注入的內(nèi)容介紹(Dependency Injection)。

這樣我們對(duì)應(yīng)的Application Service里面荣病,對(duì)于Event的應(yīng)用服務(wù)層的類EventAppService 码撰,如下所示。

    [AbpAuthorize]
    public class EventAppService : EventCloudAppServiceBase, IEventAppService
    {
        private readonly IEventManager _eventManager;
        private readonly IRepository<Event, Guid> _eventRepository;

        public EventAppService(
            IEventManager eventManager,
            IRepository<Event, Guid> eventRepository)
        {
            _eventManager = eventManager;
            _eventRepository = eventRepository;
        }

        ......

這里的服務(wù)層類提供了兩個(gè)接口注入个盆,一個(gè)是自定義的事件業(yè)務(wù)對(duì)象類脖岛,一個(gè)是標(biāo)準(zhǔn)的倉(cāng)儲(chǔ)對(duì)象朵栖。

大多數(shù)情況下如果是基于Web API的架構(gòu)下,如果是基于數(shù)據(jù)庫(kù)表的處理柴梆,我覺得領(lǐng)域的業(yè)務(wù)管理類也是不必要的陨溅,直接使用倉(cāng)儲(chǔ)的標(biāo)準(zhǔn)對(duì)象處理,已經(jīng)可以滿足大多數(shù)的需要了绍在,一些邏輯我們可以在Application Service里面實(shí)現(xiàn)以下即可门扇。

3)字典模塊業(yè)務(wù)類的簡(jiǎn)化

我們以字典模塊的字典類型表來(lái)介紹。
領(lǐng)域業(yè)務(wù)對(duì)象接口層定義如下所示(類似IBLL)

    /// <summary>
    /// 領(lǐng)域業(yè)務(wù)管理接口
    /// </summary>
    public interface IDictTypeManager : IDomainService
    {
        /// <summary>
        /// 獲取所有字典類型的列表集合(Key為名稱揣苏,Value為ID值)
        /// </summary>
        /// <param name="dictTypeId">字典類型ID悯嗓,為空則返回所有</param>
        /// <returns></returns>
        Task<Dictionary<string, string>> GetAllType(string dictTypeId);

    }

領(lǐng)域業(yè)務(wù)對(duì)象管理類(類似BLL)

    /// <summary>
    /// 領(lǐng)域業(yè)務(wù)管理類實(shí)現(xiàn)
    /// </summary>
    public class DictTypeManager : DomainService, IDictTypeManager
    {
        private readonly IRepository<DictType, string> _dictTypeRepository;

        public DictTypeManager(IRepository<DictType, string> dictTypeRepository)
        {
            this._dictTypeRepository = dictTypeRepository;
        }

        /// <summary>
        /// 獲取所有字典類型的列表集合(Key為名稱,Value為ID值)
        /// </summary>
        /// <param name="dictTypeId">字典類型ID卸察,為空則返回所有</param>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
        {
            IList<DictType> list = null;
            if (!string.IsNullOrWhiteSpace(dictTypeId))
            {
                list = await _dictTypeRepository.GetAllListAsync(p => p.PID == dictTypeId);
            }
            else
            {
                list = await _dictTypeRepository.GetAllListAsync();
            }

            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (var info in list)
            {
                if (!dict.ContainsKey(info.Name))
                {
                    dict.Add(info.Name, info.Id);
                }
            }
            return dict;
        }
    }

然后領(lǐng)域?qū)ο蟮膽?yīng)用服務(wù)層接口實(shí)現(xiàn)如下所示

    [AbpAuthorize]
    public class DictTypeAppService : MyAsyncServiceBase<DictType, DictTypeDto, string, PagedResultRequestDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService
    {
        private readonly IDictTypeManager _manager;
        private readonly IRepository<DictType, string> _repository;

        public DictTypeAppService(
            IRepository<DictType, string> repository, 
            IDictTypeManager manager) : base(repository)
        {
            _repository = repository;
            _manager = manager;
        }

        /// <summary>
        /// 獲取所有字典類型的列表集合(Key為名稱脯厨,Value為ID值)
        /// </summary>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
        {
            var result = await _manager.GetAllType(dictTypeId);
            return result;
        }
......

這樣就在應(yīng)用服務(wù)層里面,就整合了業(yè)務(wù)邏輯類的處理坑质,不過這樣的做法合武,對(duì)于常規(guī)數(shù)據(jù)庫(kù)的處理來(lái)說,顯得有點(diǎn)累贅涡扼,還需要多定義一個(gè)業(yè)務(wù)對(duì)象接口和一個(gè)業(yè)務(wù)對(duì)象實(shí)現(xiàn)稼跳,同時(shí)在應(yīng)用層接口里面,也需要多增加一個(gè)接口參數(shù)吃沪,總體感覺有點(diǎn)多余汤善,因此我把它改為使用標(biāo)準(zhǔn)的倉(cāng)儲(chǔ)對(duì)象來(lái)處理就可以達(dá)到同樣的目的了。

在項(xiàng)目其中對(duì)應(yīng)位置票彪,刪除字典類型的一個(gè)業(yè)務(wù)對(duì)象接口和一個(gè)業(yè)務(wù)對(duì)象實(shí)現(xiàn)红淡,改為標(biāo)準(zhǔn)倉(cāng)儲(chǔ)對(duì)象的接口處理,相當(dāng)于把業(yè)務(wù)邏輯里面的代碼提出來(lái)放在服務(wù)層而已降铸,那么在應(yīng)用服務(wù)層的處理代碼如下所示在旱。

    [AbpAuthorize]
    public class DictTypeAppService : MyAsyncServiceBase<DictType, DictTypeDto, string, PagedResultRequestDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService
    {
        private readonly IRepository<DictType, string> _repository;

        public DictTypeAppService(
            IRepository<DictType, string> repository) : base(repository)
        {
            _repository = repository;
        }

        /// <summary>
        /// 獲取所有字典類型的列表集合(Key為名稱,Value為ID值)
        /// </summary>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
        {
            IList<DictType> list = null;
            if (!string.IsNullOrWhiteSpace(dictTypeId))
            {
                list = await Repository.GetAllListAsync(p => p.PID == dictTypeId);
            }
            else
            {
                list = await Repository.GetAllListAsync();
            }

            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (var info in list)
            {
                if (!dict.ContainsKey(info.Name))
                {
                    dict.Add(info.Name, info.Id);
                }
            }
            return dict;
        }

......

這樣我們少定義兩個(gè)文件推掸,以及減少協(xié)調(diào)業(yè)務(wù)類的代碼桶蝎,代碼更加簡(jiǎn)潔和容易理解,反正最終實(shí)現(xiàn)都是基于倉(cāng)儲(chǔ)對(duì)象的接口調(diào)用谅畅。

另外登渣,我們繼續(xù)了解項(xiàng)目,知道在Web.Host項(xiàng)目是我們Web API層啟動(dòng)毡泻,且動(dòng)態(tài)構(gòu)建Web API層的服務(wù)層胜茧。它整合了Swagger對(duì)接口的測(cè)試使用。

            // Swagger - Enable this line and the related lines in Configure method to enable swagger UI
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new Info { Title = "MyProject API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);

                // Define the BearerAuth scheme that's in use
                options.AddSecurityDefinition("bearerAuth", new ApiKeyScheme()
                {
                    Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
                    Name = "Authorization",
                    In = "header",
                    Type = "apiKey"
                });
                // Assign scope requirements to operations based on AuthorizeAttribute
                options.OperationFilter<SecurityRequirementsOperationFilter>();
            });

啟動(dòng)項(xiàng)目牙捉,我們可以看到Swagger的管理界面如下所示竹揍。

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市邪铲,隨后出現(xiàn)的幾起案子芬位,更是在濱河造成了極大的恐慌,老刑警劉巖带到,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昧碉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡揽惹,警方通過查閱死者的電腦和手機(jī)被饿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)搪搏,“玉大人狭握,你說我怎么就攤上這事》枘纾” “怎么了论颅?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)囱嫩。 經(jīng)常有香客問我恃疯,道長(zhǎng),這世上最難降的妖魔是什么墨闲? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任今妄,我火速辦了婚禮,結(jié)果婚禮上鸳碧,老公的妹妹穿的比我還像新娘盾鳞。我一直安慰自己,他們只是感情好杆兵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布雁仲。 她就那樣靜靜地躺著,像睡著了一般琐脏。 火紅的嫁衣襯著肌膚如雪攒砖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天日裙,我揣著相機(jī)與錄音吹艇,去河邊找鬼。 笑死昂拂,一個(gè)胖子當(dāng)著我的面吹牛受神,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播格侯,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼鼻听,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼财著!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起撑碴,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤撑教,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后醉拓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伟姐,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年亿卤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了愤兵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡排吴,死狀恐怖秆乳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钻哩,我是刑警寧澤矫夷,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站憋槐,受9級(jí)特大地震影響双藕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阳仔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一忧陪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧近范,春花似錦嘶摊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至斥杜,卻和暖如春虱颗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔗喂。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工忘渔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缰儿。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓畦粮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宣赔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容