@ -63,18 +63,17 @@ The main entity of the application is the `Book`. Create a `Books` folder (names
using System;
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore.Books
namespace Acme.BookStore.Books;
public class Book : AuditedAggregateRoot<Guid>
{
{
public class Book : AuditedAggregateRoot<Guid>
public string Name { get; set; }
{
public string Name { get; set; }
public BookType Type { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
public float Price { get; set; }
}
}
}
````
````
@ -89,20 +88,19 @@ namespace Acme.BookStore.Books
The `Book` entity uses the `BookType` enum. Create a `Books` folder (namespace) in the `Acme.BookStore.Domain.Shared` project and add a `BookType` inside it:
The `Book` entity uses the `BookType` enum. Create a `Books` folder (namespace) in the `Acme.BookStore.Domain.Shared` project and add a `BookType` inside it:
````csharp
````csharp
namespace Acme.BookStore.Books
namespace Acme.BookStore.Books;
public enum BookType
{
{
public enum BookType
Undefined,
{
Adventure,
Undefined,
Biography,
Adventure,
Dystopia,
Biography,
Fantastic,
Dystopia,
Horror,
Fantastic,
Science,
Horror,
ScienceFiction,
Science,
Poetry
ScienceFiction,
Poetry
}
}
}
````
````
@ -150,34 +148,33 @@ Navigate to the `OnModelCreating` method in the `BookStoreDbContext` class and a
public async Task SeedAsync(DataSeedContext context)
var orwell = await _authorRepository.InsertAsync(
{
await _authorManager.CreateAsync(
if (await _bookRepository.GetCountAsync() > 0)
"George Orwell",
new DateTime(1903, 06, 25),
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
)
);
var douglas = await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"Douglas Adams",
new DateTime(1952, 03, 11),
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
)
);
await _bookRepository.InsertAsync(
new Book
{
{
return;
AuthorId = orwell.Id, // SET THE AUTHOR
}
Name = "1984",
Type = BookType.Dystopia,
var orwell = await _authorRepository.InsertAsync(
PublishDate = new DateTime(1949, 6, 8),
await _authorManager.CreateAsync(
Price = 19.84f
"George Orwell",
},
new DateTime(1903, 06, 25),
autoSave: true
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
);
)
);
await _bookRepository.InsertAsync(
new Book
var douglas = await _authorRepository.InsertAsync(
{
await _authorManager.CreateAsync(
AuthorId = douglas.Id, // SET THE AUTHOR
"Douglas Adams",
Name = "The Hitchhiker's Guide to the Galaxy",
new DateTime(1952, 03, 11),
Type = BookType.ScienceFiction,
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
PublishDate = new DateTime(1995, 9, 27),
)
Price = 42.0f
);
},
autoSave: true
await _bookRepository.InsertAsync(
);
new Book
{
AuthorId = orwell.Id, // SET THE AUTHOR
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
AuthorId = douglas.Id, // SET THE AUTHOR
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
}
}
}
}
````
````
@ -248,22 +247,21 @@ The final `BookDto` class should be following:
using System;
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Books
namespace Acme.BookStore.Books;
public class BookDto : AuditedEntityDto<Guid>
{
{
public class BookDto : AuditedEntityDto<Guid>
public Guid AuthorId { get; set; }
{
public Guid AuthorId { get; set; }
public string AuthorName { get; set; }
public string AuthorName { get; set; }
public string Name { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
public float Price { get; set; }
}
}
}
```
```
@ -283,12 +281,11 @@ Create a new class, `AuthorLookupDto`, inside the `Books` folder of the `Acme.Bo
using System;
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Books
namespace Acme.BookStore.Books;
public class AuthorLookupDto : EntityDto<Guid>
{
{
public class AuthorLookupDto : EntityDto<Guid>
public string Name { get; set; }
{
public string Name { get; set; }
}
}
}
````
````
@ -304,18 +301,17 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.Books
namespace Acme.BookStore.Books;
public interface IBookAppService :
ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto> //Used to create/update a book
@ -178,14 +178,13 @@ Open the `Index.cshtml` and change the whole content as shown below:
```csharp
```csharp
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Acme.BookStore.Web.Pages.Books
namespace Acme.BookStore.Web.Pages.Books;
public class IndexModel : PageModel
{
{
public class IndexModel : PageModel
public void OnGet()
{
{
public void OnGet()
{
}
}
}
}
}
```
```
@ -537,7 +536,7 @@ Open the `/src/app/book/book.component.html` and replace the content as shown be
Now you can see the final result on your browser:
Now you can see the final result on your browser:


{{else if UI == "Blazor" || UI == "BlazorServer"}}
{{else if UI == "Blazor" || UI == "BlazorServer"}}
@ -581,7 +580,7 @@ context.Menu.AddItem(
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see that the new menu item has been added to the main menu:
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see that the new menu item has been added to the main menu:
@ -1252,7 +1250,7 @@ Open the `Books.razor` and add the following `DataGridEntityActionsColumn` secti
The `DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. The `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
The `DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. The `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
@ -55,19 +55,18 @@ A permission must have a unique name (a `string`). The best way is to define it
Open the `BookStorePermissions` class inside the `Acme.BookStore.Application.Contracts` project (in the `Permissions` folder) and change the content as shown below:
Open the `BookStorePermissions` class inside the `Acme.BookStore.Application.Contracts` project (in the `Permissions` folder) and change the content as shown below:
````csharp
````csharp
namespace Acme.BookStore.Permissions
namespace Acme.BookStore.Permissions;
public static class BookStorePermissions
{
{
public static class BookStorePermissions
public const string GroupName = "BookStore";
public static class Books
{
{
public const string GroupName = "BookStore";
public const string Default = GroupName + ".Books";
public const string Create = Default + ".Create";
public static class Books
public const string Edit = Default + ".Edit";
{
public const string Delete = Default + ".Delete";
public const string Default = GroupName + ".Books";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
}
}
}
}
````
````
@ -85,24 +84,23 @@ using Acme.BookStore.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.Localization;
namespace Acme.BookStore.Permissions
namespace Acme.BookStore.Permissions;
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
{
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
public override void Define(IPermissionDefinitionContext context)
{
{
public override void Define(IPermissionDefinitionContext context)
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore"));
{
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore"));
var booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));
`AuthorConsts` is a simple class that is located under the `Authors` namespace (folder) of the `Acme.BookStore.Domain.Shared` project:
`AuthorConsts` is a simple class that is located under the `Authors` namespace (folder) of the `Acme.BookStore.Domain.Shared` project:
````csharp
````csharp
namespace Acme.BookStore.Authors
namespace Acme.BookStore.Authors;
public static class AuthorConsts
{
{
public static class AuthorConsts
public const int MaxNameLength = 64;
{
public const int MaxNameLength = 64;
}
}
}
````
````
Created this class inside the `Acme.BookStore.Domain.Shared` project since we will re-use it on the [Data Transfer Objects](../Data-Transfer-Objects.md) (DTOs) later.
Created this class inside the `Acme.BookStore.Domain.Shared` project since we will re-use it on the [Data Transfer Objects](../Data-Transfer-Objects.md) (DTOs) later.
@ -327,28 +321,27 @@ You can't compile the code since it is expecting some constants declared in the
Open the `BookStorePermissions` class inside the `Acme.BookStore.Application.Contracts` project (in the `Permissions` folder) and change the content as shown below:
Open the `BookStorePermissions` class inside the `Acme.BookStore.Application.Contracts` project (in the `Permissions` folder) and change the content as shown below:
````csharp
````csharp
namespace Acme.BookStore.Permissions
namespace Acme.BookStore.Permissions;
public static class BookStorePermissions
{
{
public static class BookStorePermissions
public const string GroupName = "BookStore";
{
public const string GroupName = "BookStore";
public static class Books
public static class Books
{
{
public const string Default = GroupName + ".Books";
public const string Default = GroupName + ".Books";
public const string Create = Default + ".Create";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
public const string Delete = Default + ".Delete";
}
}
// *** ADDED a NEW NESTED CLASS ***
// *** ADDED a NEW NESTED CLASS ***
public static class Authors
public static class Authors
{
{
public const string Default = GroupName + ".Authors";
public const string Default = GroupName + ".Authors";
public async Task SeedAsync(DataSeedContext context)
IAuthorRepository authorRepository,
{
AuthorManager authorManager)
if (await _bookRepository.GetCountAsync() <= 0)
{
{
_bookRepository = bookRepository;
await _bookRepository.InsertAsync(
_authorRepository = authorRepository;
new Book
_authorManager = authorManager;
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
}
public async Task SeedAsync(DataSeedContext context)
// ADDED SEED DATA FOR AUTHORS
{
if (await _bookRepository.GetCountAsync() <= 0)
{
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
// ADDED SEED DATA FOR AUTHORS
if (await _authorRepository.GetCountAsync() <= 0)
{
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"George Orwell",
new DateTime(1903, 06, 25),
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
)
);
if (await _authorRepository.GetCountAsync() <= 0)
await _authorRepository.InsertAsync(
{
await _authorManager.CreateAsync(
await _authorRepository.InsertAsync(
"Douglas Adams",
await _authorManager.CreateAsync(
new DateTime(1952, 03, 11),
"George Orwell",
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
new DateTime(1903, 06, 25),
)
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
);
)
);
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"Douglas Adams",
new DateTime(1952, 03, 11),
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
)
);
}
}
}
}
}
}
}
@ -491,73 +483,74 @@ using System.Threading.Tasks;
@ -95,14 +95,13 @@ This is a simple page similar to the Books page we had created before. It import
````csharp
````csharp
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Acme.BookStore.Web.Pages.Authors
namespace Acme.BookStore.Web.Pages.Authors;
public class IndexModel : PageModel
{
{
public class IndexModel : PageModel
public void OnGet()
{
{
public void OnGet()
{
}
}
}
}
}
````
````
@ -248,11 +247,11 @@ context.Menu.AddItem(
Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `Identity/Roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `Identity/Roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
@ -833,11 +828,11 @@ This page uses some localization keys we need to declare. Open the `en.json` fil
Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
@ -1240,11 +1232,11 @@ We should complete the localizations we've used above. Open the `en.json` file u
Run and login to the application. **If you don't see the Authors menu item under the Book Store menu, that means you don't have the permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
Run and login to the application. **If you don't see the Authors menu item under the Book Store menu, that means you don't have the permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**: