About this tutorial
In this tutorial series, you will build an application to manage a list of books and their authors. The Entity Framework Core (Core EF) will be used as ORM provider, because it is the default database provider.
This is the first chapter of this tutorial, all sections, here are all the chapters:
- Part I: Creating a project and a book list page (this chapter)
- Part II: create, edit, delete books
- Part III: Integration Testing
You can from GitHub repository to access the application code source. You can also watch this tutorial by community members ABP recorded video lessons .
Create a project
Create a file called Acme.BookStore
new project, create a database and follow the Getting Started documentation to run the application.
Structural solutions
The following picture shows a project from start to create a template of how hierarchical.
You can view the application template document to learn more about the solution structure. However, you will learn the basics of this tutorial.
Create a Book entity
Domain layer starting template is divided into two items:
Acme.BookStore.Domain
Include your entity , field service and other core domain objects.Acme.BookStore.Domain.Shared
It contains constants that can be shared with customers, enumeration, or other domain related objects.
In the solution domain layer ( Acme.BookStore.Domain
defined Project) entity main application is the entity. Book
In. Acme.BookStore.Domain
To create a project called Book
classes, as follows:
using System;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
- ABP provides a solid base two basic classes:
AggregateRoot
andEntity
. Aggregate Root is Domain Driven Design (DDD) one of the concepts and best practices For more information, please refer to. Physical document . Book
Entity inheritedAuditedAggregateRoot
,AuditedAggregateRoot
classAggregateRoot
adds some class on the basis of audit attributes (CreationTime
,CreatorId
,LastModificationTime
etc.).Guid
It is theBook
primary key of the entity type.- Using data annotations to EF Core Add Mapping. Or you can use EF Core comes with FLUENT Mapping API .
BookType Enumeration
The use of the above BookType
enumeration defined as follows:
namespace Acme.BookStore
{
public enum BookType
{
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry
}
}
Adding to the Book entity in DbContext
EF Core you will need to associate entities and DbContext easiest way is in. Acme.BookStore.EntityFrameworkCore
The project BookStoreDbContext
to add class DbSet
attributes as follows:
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
{
public DbSet<Book> Books { get; set; }
...
}
Book your configuration entity
In Acme.BookStore.EntityFrameworkCore
open project BookStoreDbContextModelCreatingExtensions.cs
file and add the following code to the ConfigureBookStore
end of the method is to configure the Book entity:
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
Add a new Migration and update the database
Use this template to start the EF Core Code First Migrations to create and maintain the database structure. Open the Package Manager Console (Package Penalty for Manager Console) (PMC) (Tools / Nuget Package Manager menu), select Acme.BookStore.EntityFrameworkCore.DbMigrations
as the default project and then do the following The command:
In this way we will Migrations
create a new migration class folder and then execute Update-Database
the command to update the database structure.
PM> Update-Database
Add sample data
Update-Database
In order to create a database AppBooks
table to open the database and enter a few example lines, in order to display them on the page:
Creating Application Services
The next step is to create the application services to manage (create, list, update, delete) book launch application layer template is divided into two items:
Acme.BookStore.Application.Contracts
DTO contains your main application and service interface.Acme.BookStore.Application
It contains implement application services.
BookDto
In Acme.BookStore.Application.Contracts
creating a project called BookDto
DTO class:
using System;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore
{
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
- DTO classes are used in the presentation layer and the application layer transmitted data . See DTO documentation for more information.
- In order to display the book information on the page,
BookDto
it is used to pass data to the presentation layer of books. BookDto
Inherited fromAuditedEntityDto<Guid>
. Defined above withBook
the same class have some audit attributes.
When the book is returned to the presentation layer, we need to Book
convert entities BookDto
subject. AutoMapper library can automatically perform this conversion when defining the correct mapping startup template configured AutoMapper, so you just in. Acme.BookStore.Application
The project BookStoreApplicationAutoMapperProfile
defined in the class mapping:
using AutoMapper;
namespace Acme.BookStore
{
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
}
}
}
CreateUpdateBookDto
In Acme.BookStore.Application.Contracts
creating a project called CreateUpdateBookDto
DTO class:
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
public DateTime PublishDate { get; set; }
[Required]
public float Price { get; set; }
}
}
- The DTO class is used to get book information from the user interface when creating or updating books.
- It defines the annotation data attributes (e.g.
[Required]
) to verify the defined attributes. ABP the DTO by the frame automatically verified .
Like the above BookDto
, as a creation from CreateUpdateBookDto
object to the Book
mapping of entities:
CreateMap<CreateUpdateBookDto, Book>();
IBookAppService
In Acme.BookStore.Application.Contracts
the project define a named IBookAppService
interface:
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService :
ICrudAppService< //定义了CRUD方法
BookDto, //用来展示书籍
Guid, //Book实体的主键
PagedAndSortedResultRequestDto, //获取书籍的时候用于分页和排序
CreateUpdateBookDto, //用于创建书籍
CreateUpdateBookDto> //用于更新书籍
{
}
}
- Interface Framework defines application service is not required . However, it is recommended as a best practice.
ICrudAppService
Defines the common CRUD methods:GetAsync
,GetListAsync
,CreateAsync
,UpdateAsync
andDeleteAsync
. You can start with an emptyIApplicationService
interface inheritance and manually define their own methods.ICrudAppService
There are some variations, you can use a separate DTO in each method, may be respectively separately specified.
BookAppService
In Acme.BookStore.Application
the project realization called BookAppService
the IBookAppService
:
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
}
}
}
BookAppService
InheritedCrudAppService<...>
. It implements CRUD method defined above.BookAppService
InjectionIRepository <Book,Guid>
, which is theBook
default storage entity. ABP automatically creates default storage for each aggregate root (or entity). See warehousing documentationBookAppService
UseIObjectMapper
toBook
convert an object toBookDto
object,CreateUpdateBookDto
convert the object toBook
object. Start template AutoMapper library as the object mapping provider. Before you define the mapping, so it will work as expected.
Auto-generated API Controllers
You usually create Controller to the application service exposed as HTTP API endpoint, thus allowing the browser or through third-party client AJAX call them. ABP can automatically follow the conventions of your application service is configured MVC API controller.
Swagger UI
Start template configured to use Swashbuckle.AspNetCore run Swagger the UI . Run the application and enter in your browser https://localhost:XXXX/swagger/
(replace with your own port XXXX) as the URL.
You will see some of the interface and built-in Book
interfaces, they are REST-style:
Swagger has a nice UI to test the API. You can try the [GET] /api/app/book
API to get a list of books.
Dynamic JavaScript proxy
In Javascript end by way of calling AJAX HTTP API interfaces are very common, you can use $.ajax
or other tool to call interface. Of course, ABP provides a better way.
ABP automatically creates a JavaScript API interface for all agents . So, as you can call the JavaScript function the same as to call any interface.
In the browser's developer console test interface
You can use your favorite browser developer console . Easily test agent running JavaScript programs, and open the browser developer tool (shortcut: F12), switch to the Console tab, enter the following code and press Enter:
acme.bookStore.book.getList({}).done(function (result) { console.log(result); });
acme.bookStore
Is theBookAppService
namespace, converted hump named .book
It is theBookAppService
name (and to turn into camelCase addition AppService suffix) after conversion.getList
It is defined in theAsyncCrudAppService
base classGetListAsync
name (and to turn into camelCasing addition Async suffix) the method of conversion.{}
Parameters used to send null object to theGetListAsync
method, which usually requires a typePagedAndSortedResultRequestDto
of object that is used to send paging and sorting options to the server (all properties are optional, so you can send an empty object).getList
Method returns apromise
result, you can pass a callback function todone
(orthen
) method to get the result returned by the service.
We use the create
method to create a new book :
acme.bookStore.book.create({
name: 'Foundation',
type: 7,
publishDate: '1951-05-24',
price: 21.5
}).done(function(result){
console.log('successfullycreatedthebookwithid: '+result.id);
});
You will see the console displays output like this:
successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7
Check the database Books
table to see the new books. You can try it yourself get
, update
and delete
function.
Create a book page
Now let's create some things visible and available to replace the classic MVC, we recommend the use of Microsoft's Razor Pages and the the UI .
In Acme.BookStore.Web
the project Pages
to create a new file in the folder called Books
and add a file called Index.cshtml
the Razor Page.
Open Index.cshtml
and modify the contents such as the following:
@page
@using Acme.BookStore.Web.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model IndexModel
<h2>Books</h2>
- This code changes the default inheritance Razor View Page Model, so it from the
BookStorePage
class (and notPageModel
) inheritance. Start the supplied templateBookStorePage
class that provides a number of shared properties / methods used by all the pages. - Ensure
IndexModel
(Index.cshtml.cs) has aAcme.BookStore.Pages.Books
namespace, orIndex.cshtml
update it in.
Adding Books page to the main menu
Open Menus
a folder BookStoreMenuContributor
class, the ConfigureMainMenuAsync
add the following code at the bottom of the method:
context.Menu.AddItem(
new ApplicationMenuItem("BooksStore", l["Menu:BookStore"])
.AddItem(new ApplicationMenuItem("BooksStore.Books", l["Menu:Books"], url: "/Books"))
);
Localization menu
Localized text located in Acme.BookStore.Domain.Shared
the project Localization/BookStore
folder:
Open the en.json
file, Menu:BookStore
and Menu:Books
localized text keys are added to the end of the file:
{
"culture": "en",
"texts": {
"Menu:BookStore": "Book Store",
"Menu:Books": "Books"
}
}
- ABP localization capabilities are built on ASP.NET Core's standard localization above and adds some extensions. View localized documentation .
- Localization key is arbitrary. You can set any names. We prefer to add menu items
Menu:
prefix to distinguish them from other texts. If the text is not defined in the localization file, it will return to a localization key (ASP.NET Core standards of behavior).
Run the application, see the new menu item has been added to the top of the bar:
Click the Books menu item will jump to new pages of a book.
List of Books
We will use Datatables.net JQuery plugin to display a list of tables on the page. Data Sheet can work entirely via AJAX, fast, and provide a good user experience. Datatables plugin configuration in the startup template, so you can directly on any page use it, but need to reference the style and script files in the page.
Index.cshtml
Will be Pages/Books/Index.cshtml
changed to look like this:
@page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model Acme.BookStore.Web.Pages.Books.IndexModel
@section scripts
{
<abp-script src="/Pages/Books/index.js" />
}
<abp-card>
<abp-card-header>
<h2>@L["Books"]</h2>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="BooksTable">
<thead>
<tr>
<th>@L["Name"]</th>
<th>@L["Type"]</th>
<th>@L["PublishDate"]</th>
<th>@L["Price"]</th>
<th>@L["CreationTime"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>
abp-script
tag helper for external script added to the page. It is more than a standardscript
a lot more extra features label. It can handle minimize and version View bundling & compression documentation for more information.abp-card
Andabp-table
for the Twitter Bootstrap's card component package Tag helpers .ABP have a lot of tag helpers, you can easily use most of bootstrap components. You can also use native HTML tags instead of the tag helpers. Use tag helper can be smart tips and compile-time type checking to prevent errors and reduce HTML code. check tag helpers document .- You can localize menu like the one above as localized column names.
Adding a script file
In Pages/Books/
creating folders in the index.js
file
index.js
It reads as follows:
$(function () {
var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({
ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList),
columnDefs: [
{ data: "name" },
{ data: "type" },
{ data: "publishDate" },
{ data: "price" },
{ data: "creationTime" }
]
}));
});
abp.libs.datatables.createAjax
ABP is to help the helper dynamic JavaScript API proxy with Datatable format compatible.abp.libs.datatables.normalizeConfiguration
Another helper method. Is not necessary, but it simplifies the configuration by providing a conventional data table values for the missing option.acme.bookStore.book.getList
The method is to obtain a list of books (above has been introduced)- View Datatable documentation for more configuration items.
The next chapter
Click to see the next chapter introduction.