In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider (as it comes pre-configured with the startup template).
In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider (as it comes pre-configured with the [startup template](https://abp.io/Templates)).
This is the first part of the tutorial series. See all parts:
@ -174,14 +174,13 @@ namespace Acme.BookStore
}
````
* This DTO class is used to get book information from the user interface.
* Each property has a `[Display]` property which sets the label on the form for the related input. It's also integrated to the localization system.
* This DTO class is used to get book information from the user interface to create or update a book.
* It defines data annotation attributes (like `[Required]`) to define validations for the properties.
* Each property has a `[Display]` property which sets the label on UI forms for the related inputs. It's also integrated to the localization system. The same DTO will be used as View Model. That's why it defines that attribute. You may find incorrect to use DTOs as View Models. You could use a separated view model class, but we thought it's practical and makes the sample project less complex.
#### IBookAppService
First, define an interface named `IBookAppService` for the book application service:
Define an interface named `IBookAppService` for the book application service:
````C#
using System;
@ -205,7 +204,7 @@ namespace Acme.BookStore
````
* Defining interfaces for application services is <u>not required</u> by the framework. However, it's suggested as a good practice.
* `IAsyncCrudAppService` defines common CRUD methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead you could inherit from the empty `IApplicationService` interface and define your own methods.
* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead you could inherit from the empty `IApplicationService` interface and define your own methods.
* There are some variations of the `IAsyncCrudAppService` where you can use a single DTO or separated DTOs for each method.
#### BookAppService
@ -250,7 +249,7 @@ The startup template is configured to run the [swagger UI](https://swagger.io/to
You will see some built-in service endpoints as well as the `Book` service and its REST-style service endpoints.
You will see some built-in service endpoints as well as the `Book` service and its REST-style endpoints.
### Dynamic JavaScript Proxies
@ -290,7 +289,7 @@ You should see a message in the console something like that:
successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7
````
Check the `books` table in the database to see the new book row. You can also try `get`, `update` and `delete` functions.
Check the `books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions too.
### Create the Books Page
@ -311,7 +310,7 @@ Open the `Index.cshtml` and change the content as shown below:
<h2>Books</h2>
````
* This page inherits from the `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages.
* This page **inherits** from the `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages.
* This class is derived from the `BookStorePageModelBase` instead of standard `PageModel`. `BookStorePageModelBase` inherits the `PageModel` and adds some common properties/methods those can be used in your page model classes.
* `[BindProperty]` attribute on the `Book` property binds post request data to this property.
* This class simply injects the `IBookAppService` in its constructor and calls the `CreateAsync` method in the `OnPostAsync` handler.
@ -132,4 +133,237 @@ Now, you can **run the application** and add new books using the new modal form.
### Updating An Existing Book
TODO...
Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project:
Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code:
````C#
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Acme.BookStore.Pages.Books
{
public class EditModalModel : BookStorePageModelBase
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid Id { get; set; }
[BindProperty]
public CreateUpdateBookDto Book { get; set; }
private readonly IBookAppService _bookAppService;
public EditModalModel(IBookAppService bookAppService)
{
_bookAppService = bookAppService;
}
public async Task OnGetAsync()
{
var bookDto = await _bookAppService.GetAsync(Id);
Book = ObjectMapper.Map<BookDto,CreateUpdateBookDto>(bookDto);
}
public async Task<IActionResult> OnPostAsync()
{
await _bookAppService.UpdateAsync(Id, Book);
return NoContent();
}
}
}
````
* `HiddenInput` and `BindProperty` are standard ASP.NET Core MVC attributes. Used `SupportsGet` to be able to get Id value from query string parameter of the request.
* Mapped `BookDto` (received from the `BookAppService.GetAsync`) to `CreateUpdateBookDto` in the `GetAsync` method.
* The `OnPostAsync` simply uses `BookAppService.UpdateAsync` to update the entity.
#### CreateUpdateBookDto
In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, change the `CreateUpdateBookDto` class as shown below:
````C#
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
[AutoMapTo(typeof(Book))]
[AutoMapFrom(typeof(BookDto))]
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Type")]
public BookType Type { get; set; } = BookType.Undefined;
[Display(Name = "PublishDate")]
public DateTime PublishDate { get; set; }
[Display(Name = "Price")]
public float Price { get; set; }
}
}
````
* Just added the `[AutoMapFrom(typeof(BookDto))]` attribute to create the mapping.
#### EditModal.cshtml
Replace `EditModal.cshtml` content with the following content: