古镇东邪

博客园 首页 新随笔 联系 订阅 管理

适用Zero版本:ASP.NET Core & Angular 2+ (aspnet-zero-core-3.1.0)。

该版本官方有两个solution文件夹:Angular(前端) 和 aspnet-core(后台服务)。

在开始以下步骤之前需要能够成功发布程序,对于后台服务只要能运行即可,如有报错可根据提示安装需要的插件。Angular 则比较麻烦,装的东西较多,官方建议用yarn,这个要下载,顺藤摸瓜都安装完即可。

我没有改解决方案名称,仍用的默认solution的名称MyCompanyName.AbpZeroTemplate,所以下面有的文件名跟官网的phonebook示例文档有区别。

修改1版将aspnet-core后台服务project和Angular前端project的步骤分类,这样不需要跳跃性的操作,阅读操作效率更高。本文仅谈实操,不详细解释理论性的东西,因为本文观点认为,实操没问题了,初学者的信心就有了,理解仅仅是时间问题。

 

一、aspnet-core后台Project步骤如下:

1、 src/***.core/***CoreModule.cs文件中临时禁用多租户。

[DependsOn(typeof(AbpZeroCoreModule))]
public class PhoneBookCoreModule : AbpModule
{
    public override void PreInitialize()
    {
        //some other code...
        
        //Enable this line to create a multi-tenant application.
        Configuration.MultiTenancy.IsEnabled = false;
        
        //some other code...        
    }
}

2、src/***.core/Localization/AbpZeroTemplate/AbpZeroTemplate.xml (默认的英文字体)中加入代码。如有对应中文,可在对应的中文文件中加入中文名称。其他语言中没加的都默认用英文的。

<text name="PhoneBook">Phone Book</text> 

3、创建实体类person。在.Core内新建文件夹People,然后在People文件夹内新建如下Person.cs类。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Abp.Domain.Entities.Auditing;

namespace Acme.PhoneBook.People
{
    [Table("PbPersons")]
    public class Person : FullAuditedEntity
    {
        public const int MaxNameLength = 32;
        public const int MaxSurnameLength = 32;
        public const int MaxEmailAddressLength = 255;

        [Required]
        [MaxLength(MaxNameLength)]
        public virtual string Name { get; set; }

        [Required]
        [MaxLength(MaxSurnameLength)]
        public virtual string Surname { get; set; }

        [MaxLength(MaxEmailAddressLength)]
        public virtual string EmailAddress { get; set; }
    }
}

 

4、在.EntityFramework内的****DbContext.cs文件增加如下黄色标记代码。

public class ******DbContext : AbpZeroDbContext<Tenant, Role, User>
{
    public virtual IDbSet<Person> Persons { get; set; }
        
    //...other code
}

5、用EntityFramework的code first迁移功能更新数据库创建PdPersons表。

在windows command prompt 命令行工具定位到.EntityFramework文件夹。输入:“dotnet ef migrations add "Added_Persons_Table”并回车。

这会在Migrations文件夹中增加一个***Added_Persons_Table.cs类。然后在命令行中输入:“dotnet ef database update”命令,即可在数据库中生成新表。

我在执行这步的时候报错了。提示C:\Program Files\dotnet\shared\Microsoft.NETCore.App 目录里没有1.1.0版本的。原来是我没安装command line版的

.net core 1.1 sdk.官网直接下载即可下载地址:https://www.microsoft.com/net/core#windowscmd 。安装好即可成功更新数据库。

 

6、为新创建的PdPersons表造点初始数据。

在.EntityFramework空间内的Migrations/seed/host 内新建InitialPeopleCreator.cs类

 

 类代码:

using System.Linq;
using Acme.PhoneBook.EntityFramework;
using Acme.PhoneBook.People;

namespace Acme.PhoneBook.Migrations.Seed.Host
{
    public class InitialPeopleCreator
    {
        private readonly PhoneBookDbContext _context;

        public InitialPeopleCreator(PhoneBookDbContext context)
        {
            _context = context;
        }

        public void Create()
        {
            var douglas = _context.Persons.FirstOrDefault(p => p.EmailAddress == "douglas.adams@fortytwo.com");
            if (douglas == null)
            {
                _context.Persons.Add(
                    new Person
                    {
                        Name = "Douglas",
                        Surname = "Adams",
                        EmailAddress = "douglas.adams@fortytwo.com"
                    });
            }

            var asimov = _context.Persons.FirstOrDefault(p => p.EmailAddress == "isaac.asimov@foundation.org");
            if (asimov == null)
            {
                _context.Persons.Add(
                    new Person
                    {
                        Name = "Isaac",
                        Surname = "Asimov",
                        EmailAddress = "isaac.asimov@foundation.org"
                    });
            }
        }
    }
}  

7、在.EntityFramework空间内的Migrations/seed/host 内的InitialHostDbBuilder.cs类里新增如下黄色标记代码。

public class InitialHostDbBuilder
{
    //existing codes...
    
    public void Create()
    {
        //existing code...
        new InitialPeopleCreator(_context).Create();

        _context.SaveChanges();
    }
}

 然后在命令行中执行代码:“dotnet ef database update ” 即可。查看PdPersons表里已有数据。

8、创建person应用程序服务-----------新建一个接口类。

 应用程序服务对应.Application空间,在MyCompanyName.AbpZeroTemplate.Application下面建立文件夹People。

然后在People文件夹中新建IPersonAppService.cs接口类.代码如下:

using Abp.Application.Services;
using Abp.Application.Services.Dto;

namespace Acme.PhoneBook.People
{
    public interface IPersonAppService : IApplicationService
    {
        ListResultDto<PersonListDto> GetPeople(GetPeopleInput input);
    }
}



9、在People文件夹下面再建立一个文件夹Dto,然后在此新建PersonListDto.cs文件。代码如下:

using Abp.Application.Services.Dto;
using Abp.AutoMapper;


namespace MyCompanyName.AbpZeroTemplate.People.Dto
{
    [AutoMapFrom(typeof(Person))]
    public class PersonListDto : FullAuditedEntityDto
    {
        public string Name { get; set; }

        public string Surname { get; set; }

        public string EmailAddress { get; set; }

    }


    public class GetPeopleInput
    {
        public string Filter { get; set; }
    }


}

 

 

10、在Pepole文件夹下再建立一个PersonAppService.cs文件。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Linq.Extensions;
using MyCompanyName.AbpZeroTemplate.People.Dto;

namespace MyCompanyName.AbpZeroTemplate.People
{
    public class PersonAppService:AbpZeroTemplateAppServiceBase,IPersonAppService
    {
        private readonly IRepository<Person> _personRepository;

        public PersonAppService(IRepository<Person> personRepository)
        {
            _personRepository = personRepository;
        }

        public async Task CreatePerson(CreatePersonInput input)
        {
            var person = input.MapTo<Person>();
            await _personRepository.InsertAsync(person);
        }

        public ListResultDto<PersonListDto> GetPeople(GetPeopleInput input)
        {
            var persons = _personRepository
                .GetAll()
                .WhereIf(
                    !input.Filter.IsNullOrEmpty(),
                    p => p.Name.Contains(input.Filter) ||
                         p.Surname.Contains(input.Filter) ||
                         p.EmailAddress.Contains(input.Filter)
                )
                .OrderBy(p => p.Name)
                .ThenBy(p => p.Surname)
                .ToList();

            return new ListResultDto<PersonListDto>(persons.MapTo<List<PersonListDto>>());
        }
    }
    
}

 

 

11、增加Person。

首先再IpersonAppService.cs文件里新增如下代码:

Task CreatePerson(CreatePersonInput input);

然后在Dto文件内新增类CreatePersonInput.cs类,代码如下:

using Abp.AutoMapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace MyCompanyName.AbpZeroTemplate.People.Dto
{    
    [AutoMapTo(typeof(Person))]
    public class CreatePersonInput
    {
        [Required]
        [MaxLength(Person.MaxNameLength)]
        public string Name { get; set; }

        [Required]
        [MaxLength(Person.MaxSurnameLength)]
        public string Surname { get; set; }

        [EmailAddress]
        [MaxLength(Person.MaxEmailAddressLength)]
        public string EmailAddress { get; set; }
    }



}

 

 

12、允许创建person对象的授权。

首先加入允许进入phonebook页的授权。在src/***.core/Authorization/AppPermissions.cs文件中加入常量定义:

public const string Pages_Tenant_PhoneBook = "Pages.Tenant.PhoneBook";

在src/MyCompanyName.AbpZeroTemplate.Application/People里的PersonAppService.cs里加入如下黄色标记代码:

[AbpAuthorize(AppPermissions.Pages_Tenant_PhoneBook)]
public class PersonAppService : PhoneBookAppServiceBase, IPersonAppService
{
    //...
}

 

在src/***.core/Authorization/AppAuthorizationProvider.cs类中定义许可,代码如下:

var phoneBook = pages.CreateChildPermission(AppPermissions.Pages_Tenant_PhoneBook, L("PhoneBook"), multiTenancySides: MultiTenancySides.Tenant);
phoneBook.CreateChildPermission(AppPermissions.Pages_Tenant_PhoneBook_CreatePerson, L("CreateNewPerson"), multiTenancySides: MultiTenancySides.Tenant);

 

在同目录下的AppPermissions.cs文件中加入常量定义:

public const string Pages_Tenant_PhoneBook_CreatePerson = "Pages.Tenant.PhoneBook.CreatePerson";

 

13、增加ABP授权属性。

在src/MyCompanyName.AbpZeroTemplate.Application/People里的PersonAppService.cs文件中增加属性标记代码如下

[AbpAuthorize(AppPermissions.Pages_Tenant_PhoneBook_CreatePerson)]
public async Task CreatePerson(CreatePersonInput input)
{
    //...
}

 

14、删除person实体对象。

在IPersonAppService.cs文件中增加代码:

Task DeletePerson(EntityDto input);

 

在PersonAppService.cs文件中增加代码:

[AbpAuthorize(AppPermissions.Pages_Tenant_PhoneBook_DeletePerson)]
public async Task DeletePerson(EntityDto input)
{
    await _personRepository.DeleteAsync(input.Id);
}

另外提醒一下, 要跟创建person的权限设置一样在AppPermissions.cs文件中定义常量Pages_Tenant_PhoneBook_DeletePerson,

且在src/***.core/Authorization/AppAuthorizationProvider.cs类中定义许可,代码如下:


phoneBook.DeleteChildPermission(AppPermissions.Pages_Tenant_PhoneBook_DeletePerson, L("DeletePerson"), multiTenancySides: MultiTenancySides.Tenant);

 以后这类增删查改不再赘述权限功能的实现。

 

15、创建Phone 实体。

在src/*******.core下创建Phone.cs类代码如下

[Table("PbPhones")]
public class Phone : CreationAuditedEntity<long>
{
    public const int MaxNumberLength = 16;

    [ForeignKey("PersonId")]
    public virtual Person Person { get; set; }
    public virtual int PersonId { get; set; }

    [Required]
    public virtual PhoneType Type { get; set; }

    [Required]
    [MaxLength(MaxNumberLength)]
    public virtual string Number { get; set; }
}

 

 

16、在Person.cs中增加phones字段。

[Table("PbPersons")]
public class Person : FullAuditedEntity
{
    //...other properties

    public virtual ICollection<Phone> Phones { get; set; }
}

 

17、在.core下增加PhoneType.cs类

public enum PhoneType : byte
{
    Mobile,
    Home,
    Business
}

 

18、在Entity Framework下的AbpZeroTemplateDbContext.cs增加代码

public virtual IDbSet<Phone> Phones { get; set; }

且用命令行命令:dotnet ef migrations add "Added_Phone"  增加数据库phone表的迁移类。

然后更新数据库,该部分类似操作在上文已有介绍,不赘述。

 

该部分未完成待续。。。

 

19、

 

20、

 

二、Angular前端Project步骤如下:


1、增加一个新的菜单项。app\shared\layout\side-bar.component.ts (展开side-bar.component.html文件)在dashboard下面加入如下代码
  new SideBarMenuItem("PhoneBook", null, "icon-notebook", "/app/main/phonebook")

2、app\main\main-routing.module.ts  文件中加路由

{ path: 'dashboard', component: DashboardComponent, data: { permission: 'Pages.Tenant.Dashboard' } },

{ path: 'phonebook', component: PhoneBookComponent }

此时phoneBookComponent报错,先忽略不管。

 

3、在app/main中新建一个phonebook文件夹并创建phonebook.component.ts文件,代码如下:

import { Component, Injector } from '@angular/core';
import { AppComponentBase } from '@shared/common/app-component-base';
import { appModuleAnimation } from '@shared/animations/routerTransition';

@Component({
    templateUrl: './phonebook.component.html',
    animations: [appModuleAnimation()]
})
export class PhoneBookComponent extends AppComponentBase {

    constructor(
        injector: Injector
    ) {
        super(injector);
    }

}

4、解决第2步的报错问题。在main-routing.module.ts加入import代码:

import { PhoneBookComponent } from './phonebook/phonebook.component';

5、在app/main/phonebook中新建 phonebook.component.html文件,代码如下:

<div [@routerTransition]>
    <div class="row margin-bottom-5">
        <div class="col-xs-12">
            <div class="page-head">
                <div class="page-title">
                    <h1>
                        <span>{{l("PhoneBook")}}</span>
                    </h1>
                </div>
            </div>
        </div>
    </div>
   
    <div class="portlet light margin-bottom-0">
        <div class="portlet-body">
            
            <p>PHONE BOOK CONTENT COMES HERE!</p>

        </div>
    </div>
</div>

 6、在app/main/main.module.ts文件中添加下面有黄色标记的代码。 

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { ModalModule, TabsModule, TooltipModule } from 'ng2-bootstrap';

import { UtilsModule } from '@shared/utils/utils.module'
import { AppCommonModule } from '@app/shared/common/app-common.module'

import { MainRoutingModule } from './main-routing.module'
import { MainComponent } from './main.component'
import { DashboardComponent } from './dashboard/dashboard.component';
import { PhoneBookComponent } from './phonebook/phonebook.component';

@NgModule({
    imports: [
        BrowserModule,
        CommonModule,
        FormsModule,

        ModalModule.forRoot(),
        TabsModule.forRoot(),
        TooltipModule.forRoot(),

        UtilsModule,
        AppCommonModule,

        MainRoutingModule
    ],
    declarations: [
        MainComponent,
        DashboardComponent,
        PhoneBookComponent
    ]
})
export class MainModule { }

  保存后刷新前端页面点击phone book后可出来如下页面。

 

7、更新服务端后台服务(后台新增功能代码时执行)。 在dos命令行工具里先定位到angular所在目录路径,然后输入命令:nswag/refresh.bat 执行更新命令。

 

8、获取person列表。

首先在 shared/service-proxies/service-proxy.module.ts文件中的对应地方加入如下代码:

ApiServiceProxies.PersonServiceProxy

 

修改phonebook.component.ts里的代码如下面的黄色标记处所示:

import { Component, Injector, OnInit } from '@angular/core';
import { AppComponentBase } from '@shared/common/app-component-base';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import { PersonServiceProxy, PersonListDto, ListResultDtoOfPersonListDto } from '@shared/service-proxies/service-proxies';

@Component({
    templateUrl: './phonebook.component.html',
    animations: [appModuleAnimation()]
})
export class PhoneBookComponent extends AppComponentBase implements OnInit {

    people: PersonListDto[] = [];
    filter: string = '';

    constructor(
        injector: Injector,
        private _personService: PersonServiceProxy
    ) {
        super(injector);
    }

    ngOnInit(): void {
        this.getPeople();
    }

    getPeople(): void {
        this._personService.getPeople(this.filter).subscribe((result) => {
            this.people = result.items;
        });
    }
}

再替换phonebook.component.html的 <p>PHONE BOOK CONTENT COMES HERE!</p>代码为下面的黄色标记代码:

<div [@routerTransition]>
    <div class="row margin-bottom-5">
        <div class="col-xs-12">
            <div class="page-head">
                <div class="page-title">
                    <h1>
                        <span>{{l("PhoneBook")}}</span>
                    </h1>
                </div>
            </div>
        </div>
    </div>
   
    <div class="portlet light margin-bottom-0">
        <div class="portlet-body">
            
            <h3>{{l("AllPeople")}}</h3>

            <div class="list-group">
                <a *ngFor="let person of people" href="javascript:;" class="list-group-item">
                    <h4 class="list-group-item-heading">
                        {{person.name + ' ' + person.surname}}
                    </h4>
                    <p class="list-group-item-text">
                        {{person.emailAddress}}
                    </p>
                </a>
            </div>

        </div>
    </div>
</div>

 

刷新页面

 

9、新建一个person对象。

首先在phonebook文件夹内创建一个组件create-person-modal.component.ts文件。代码如下:

import { Component, ViewChild, Injector, ElementRef, Output, EventEmitter } from '@angular/core';
import { ModalDirective } from 'ng2-bootstrap';
import { PersonServiceProxy, CreatePersonInput } from '@shared/service-proxies/service-proxies';
import { AppComponentBase } from '@shared/common/app-component-base';

@Component({
    selector: 'createPersonModal',
    templateUrl: './create-person-modal.component.html'
})
export class CreatePersonModalComponent extends AppComponentBase {

    @Output() modalSave: EventEmitter<any> = new EventEmitter<any>();

    @ViewChild('modal') modal: ModalDirective;
    @ViewChild('nameInput') nameInput: ElementRef;

    person: CreatePersonInput;

    active: boolean = false;
    saving: boolean = false;

    constructor(
        injector: Injector,
        private _personService: PersonServiceProxy
    ) {
        super(injector);
    }

    show(): void {
        this.active = true;
        this.person = new CreatePersonInput();
        this.modal.show();
    }

    onShown(): void {
        $(this.nameInput.nativeElement).focus();
    }

    save(): void {
        this.saving = true;
        this._personService.createPerson(this.person)
            .finally(() => this.saving = false)
            .subscribe(() => {
                this.notify.info(this.l('SavedSuccessfully'));
                this.close();
                this.modalSave.emit(this.person);
            });
    }

    close(): void {
        this.modal.hide();
        this.active = false;
    }
}

然后创建同名的html文件 create-person-modal.component.html,代码如下:

<div bsModal #modal="bs-modal" (onShown)="onShown()" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true" [config]="{backdrop: 'static'}">
    <div class="modal-dialog">
        <div class="modal-content">
            <form *ngIf="active" #personForm="ngForm" novalidate (ngSubmit)="save()">
                <div class="modal-header">
                    <button type="button" class="close" (click)="close()" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                    <h4 class="modal-title">
                        <span>{{l("CreateNewPerson")}}</span>
                    </h4>
                </div>
                <div class="modal-body">
                    
                    <div class="form-group form-md-line-input form-md-floating-label no-hint">
                        <input #nameInput class="form-control" type="text" name="name" [(ngModel)]="person.name" required maxlength="32">
                        <label>{{l("Name")}}</label>
                    </div>

                    <div class="form-group form-md-line-input form-md-floating-label no-hint">
                        <input class="form-control" type="email" name="surname" [(ngModel)]="person.surname" required maxlength="32">
                        <label>{{l("Surname")}}</label>
                    </div>

                    <div class="form-group form-md-line-input form-md-floating-label no-hint">
                        <input class="form-control" type="email" name="emailAddress" [(ngModel)]="person.emailAddress" required maxlength="255" pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,})+$">                        <label>{{l("EmailAddress")}}</label>
                    </div>

                </div>
                <div class="modal-footer">
                    <button [disabled]="saving" type="button" class="btn btn-default" (click)="close()">{{l("Cancel")}}</button>
                    <button type="submit" class="btn btn-primary blue" [disabled]="!personForm.form.valid" [buttonBusy]="saving" [busyText]="l('SavingWithThreeDot')"><i class="fa fa-save"></i> <span>{{l("Save")}}</span></button>
                </div>
            </form>
        </div>
    </div>
</div>

 

接着在main.module.ts类里加入如下黄色标记代码:

...previous imports
import { CreatePersonModalComponent } from './phonebook/create-person-modal.component';

@NgModule({
    imports: [
        ...existing module imports...
    ],
    declarations: [
        MainComponent,
        DashboardComponent,
        PhoneBookComponent,
        CreatePersonModalComponent
    ]
})
export class MainModule { }

 

最后,在phonebook.component.html文件里加入如下黄色标记代码:

<div [@routerTransition]>
    <div class="row margin-bottom-5">
        <div class="col-xs-6">
            <div class="page-head">
                <div class="page-title">
                    <h1>
                        <span>{{l("PhoneBook")}}</span>
                    </h1>
                </div>
            </div>
        </div>
        <div class="col-xs-6 text-right">
            <button class="btn btn-primary blue" (click)="createPersonModal.show()"><i class="fa fa-plus"></i> {{l("CreateNewPerson")}}</button>
        </div>
    </div>

    <div class="portlet light margin-bottom-0">
        <div class="portlet-body">

            <h3>{{l("AllPeople")}}</h3>

            <div class="list-group">
                <a *ngFor="let person of people" href="javascript:;" class="list-group-item">
                    <h4 class="list-group-item-heading">
                        {{person.name + ' ' + person.surname}}
                    </h4>
                    <p class="list-group-item-text">
                        {{person.emailAddress}}
                    </p>
                </a>
            </div>

        </div>
    </div>
    <createPersonModal #createPersonModal (modalSave)="getPeople()"></createPersonModal>
</div>

  

10、隐藏未授权的button按钮。

 打开phonebook.component.html文件。在button处加入下面的黄色标记代码:

<button *ngIf="isGranted('Pages.Tenant.PhoneBook.CreatePerson')" class="btn btn-primary blue" (click)="createPersonModal.show()"><i class="fa fa-plus"></i> {{l("CreateNewPerson")}}</button>

 

 

11、删除peson对象。

 修改phonebook.component.html文件,增加如下黄色标记处代码:

...
<h3>{{l("AllPeople")}}</h3>

<div class="list-group">
    <a *ngFor="let person of people" href="javascript:;" class="list-group-item">
        <h4 class="list-group-item-heading">
            {{person.name + ' ' + person.surname}}
            <button (click)="deletePerson(person)" title="{{l('Delete')}}" class="btn btn-circle btn-icon-only red delete-person" href="javascript:;">
                <i class="icon-trash"></i>
            </button>
        </h4>
        <p class="list-group-item-text">
            {{person.emailAddress}}
        </p>
    </a>
</div>
...

 

12、使用LESS样式将按钮移至右边。

在phonebook文件夹下创建phonebook.component.less样式文件,代码如下:

.list-group-item-heading {
    button.delete-person {
        float: right;
    }
}

在 phonebook.component.ts 文件中引入样式文件,代码如黄色标记:

@Component({
    templateUrl: './phonebook.component.html',
    styleUrls: ['./phonebook.component.less'],
    animations: [appModuleAnimation()]
})

 

13、在phonebook.component.ts内添加删除功能代码:

deletePerson(person: PersonListDto): void {
    this.message.confirm(
        this.l('AreYouSureToDeleteThePerson', person.name),
        isConfirmed => {
            if (isConfirmed) {
                this._personService.deletePerson(person.id).subscribe(() => {
                    this.notify.info(this.l('SuccessfullyDeleted'));
                    _.remove(this.people, person);
                });
            }
        }
    );
} 

 

并且在@Component标记前增加引入代码:

import * as _ from 'lodash';

 

 

14、

 

15、

 

16、

 

 

 

 

 

 

 

 

 

 未完待续。。。

 

posted on 2017-03-06 09:40  古镇东邪  阅读(1324)  评论(0编辑  收藏  举报