diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
index aaa217a041..51f2dd550a 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
@@ -817,6 +817,8 @@
"Other": "Other",
"WhereDidYouHearAboutUs_explain": "Specify ...",
"DeletingMemberWarningMessage": "\"{0}\" will be removed from the developer list. If you want, you can assign this empty seat to another developer later.",
- "AdditionalInfo": "If the developer seats are above your requirements, you can reduce them. You can email info@abp.io to remove some of your developer seats. Clearing unused developer seats will reduce the license renewal cost. If you want, you can re-purchase additional developer seats within your active license period. Note that, since there are {0} developers in this license package, you cannot reduce this number."
+ "AdditionalInfo": "If the developer seats are above your requirements, you can reduce them. You can email at info@abp.io to remove some of your developer seats. Clearing unused developer seats will reduce the license renewal cost. If you want, you can re-purchase additional developer seats within your active license period. Note that, since there are {0} developers in this license package, you cannot reduce this number.",
+ "LinkExpiredErrorMessage": "The link you are trying to access is expired.",
+ "ExpirationDate": "Expiration Date"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
index d693b60ac6..751c5b6481 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
@@ -409,9 +409,9 @@
"SeeTheScreenshot": "See the screenshot",
"ApplicationModuleExplanation1": "Creates a reusable, fully layered application module solution.",
"ApplicationModuleExplanation2": "You can use this option to create modules for your modular application.",
- "Expert": "Expert",
+ "Expert": "ABP Expert",
"Expert_": "Expert",
- "Partner": "Partner",
+ "Partner": "ABP Partner",
"Partner_": "Partnership",
"WebSite": "Web Site",
"Industry": "Industry",
@@ -428,6 +428,7 @@
"Date": "Date",
"Activity": "Activity",
"Type": "Type",
- "Contribution": "Contribution"
+ "Contribution": "Contribution",
+ "Info": "Info"
}
}
diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md
index 0e09804ed5..0b50992274 100644
--- a/docs/en/Tutorials/Part-1.md
+++ b/docs/en/Tutorials/Part-1.md
@@ -63,18 +63,17 @@ The main entity of the application is the `Book`. Create a `Books` folder (names
using System;
using Volo.Abp.Domain.Entities.Auditing;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class Book : AuditedAggregateRoot
{
- public class Book : AuditedAggregateRoot
- {
- 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:
````csharp
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public enum BookType
{
- public enum BookType
- {
- Undefined,
- Adventure,
- Biography,
- Dystopia,
- Fantastic,
- Horror,
- Science,
- ScienceFiction,
- Poetry
- }
+ Undefined,
+ Adventure,
+ Biography,
+ Dystopia,
+ Fantastic,
+ Horror,
+ Science,
+ ScienceFiction,
+ Poetry
}
````
@@ -150,34 +148,33 @@ Navigate to the `OnModelCreating` method in the `BookStoreDbContext` class and a
using Acme.BookStore.Books;
...
-namespace Acme.BookStore.EntityFrameworkCore
+namespace Acme.BookStore.EntityFrameworkCore;
+
+public class BookStoreDbContext :
+ AbpDbContext,
+ IIdentityDbContext,
+ ITenantManagementDbContext
{
- public class BookStoreDbContext :
- AbpDbContext,
- IIdentityDbContext,
- ITenantManagementDbContext
- {
- ...
+ ...
- protected override void OnModelCreating(ModelBuilder builder)
- {
- base.OnModelCreating(builder);
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
- /* Include modules to your migration db context */
+ /* Include modules to your migration db context */
- builder.ConfigurePermissionManagement();
- ...
+ builder.ConfigurePermissionManagement();
+ ...
- /* Configure your own tables/entities inside here */
+ /* Configure your own tables/entities inside here */
- builder.Entity(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);
- });
- }
+ builder.Entity(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);
+ });
}
}
````
@@ -217,44 +214,43 @@ using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore
+namespace Acme.BookStore;
+
+public class BookStoreDataSeederContributor
+ : IDataSeedContributor, ITransientDependency
{
- public class BookStoreDataSeederContributor
- : IDataSeedContributor, ITransientDependency
- {
- private readonly IRepository _bookRepository;
+ private readonly IRepository _bookRepository;
- public BookStoreDataSeederContributor(IRepository bookRepository)
- {
- _bookRepository = bookRepository;
- }
+ public BookStoreDataSeederContributor(IRepository bookRepository)
+ {
+ _bookRepository = bookRepository;
+ }
- public async Task SeedAsync(DataSeedContext context)
+ public async Task SeedAsync(DataSeedContext context)
+ {
+ if (await _bookRepository.GetCountAsync() <= 0)
{
- 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
- );
- }
+ 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
+ );
}
}
}
@@ -287,18 +283,17 @@ In this section, you will create an application service to get, create, update a
using System;
using Volo.Abp.Application.Dtos;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class BookDto : AuditedEntityDto
{
- public class BookDto : AuditedEntityDto
- {
- 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; }
}
````
@@ -312,14 +307,13 @@ It will be needed to map the `Book` entities to the `BookDto` objects while retu
using Acme.BookStore.Books;
using AutoMapper;
-namespace Acme.BookStore
+namespace Acme.BookStore;
+
+public class BookStoreApplicationAutoMapperProfile : Profile
{
- public class BookStoreApplicationAutoMapperProfile : Profile
+ public BookStoreApplicationAutoMapperProfile()
{
- public BookStoreApplicationAutoMapperProfile()
- {
- CreateMap();
- }
+ CreateMap();
}
}
````
@@ -334,24 +328,23 @@ Create a `CreateUpdateBookDto` class in the `Books` folder (namespace) of the `A
using System;
using System.ComponentModel.DataAnnotations;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class CreateUpdateBookDto
{
- public class CreateUpdateBookDto
- {
- [Required]
- [StringLength(128)]
- public string Name { get; set; }
+ [Required]
+ [StringLength(128)]
+ public string Name { get; set; }
- [Required]
- public BookType Type { get; set; } = BookType.Undefined;
+ [Required]
+ public BookType Type { get; set; } = BookType.Undefined;
- [Required]
- [DataType(DataType.Date)]
- public DateTime PublishDate { get; set; } = DateTime.Now;
+ [Required]
+ [DataType(DataType.Date)]
+ public DateTime PublishDate { get; set; } = DateTime.Now;
- [Required]
- public float Price { get; set; }
- }
+ [Required]
+ public float Price { get; set; }
}
````
@@ -364,15 +357,14 @@ As done to the `BookDto` above, we should define the mapping from the `CreateUpd
using Acme.BookStore.Books;
using AutoMapper;
-namespace Acme.BookStore
+namespace Acme.BookStore;
+
+public class BookStoreApplicationAutoMapperProfile : Profile
{
- public class BookStoreApplicationAutoMapperProfile : Profile
+ public BookStoreApplicationAutoMapperProfile()
{
- public BookStoreApplicationAutoMapperProfile()
- {
- CreateMap();
- CreateMap();
- }
+ CreateMap();
+ CreateMap();
}
}
````
@@ -386,17 +378,16 @@ using System;
using Volo.Abp.Application.Dtos;
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
{
- 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
- {
- }
}
````
@@ -414,22 +405,21 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class BookAppService :
+ CrudAppService<
+ Book, //The Book entity
+ BookDto, //Used to show books
+ Guid, //Primary key of the book entity
+ PagedAndSortedResultRequestDto, //Used for paging/sorting
+ CreateUpdateBookDto>, //Used to create/update a book
+ IBookAppService //implement the IBookAppService
{
- public class BookAppService :
- CrudAppService<
- Book, //The Book entity
- BookDto, //Used to show books
- Guid, //Primary key of the book entity
- PagedAndSortedResultRequestDto, //Used for paging/sorting
- CreateUpdateBookDto>, //Used to create/update a book
- IBookAppService //implement the IBookAppService
+ public BookAppService(IRepository repository)
+ : base(repository)
{
- public BookAppService(IRepository repository)
- : base(repository)
- {
- }
}
}
````
diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md
index b2ce526bd5..d72505b309 100644
--- a/docs/en/Tutorials/Part-10.md
+++ b/docs/en/Tutorials/Part-10.md
@@ -141,72 +141,71 @@ using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore
+namespace Acme.BookStore;
+
+public class BookStoreDataSeederContributor
+ : IDataSeedContributor, ITransientDependency
{
- public class BookStoreDataSeederContributor
- : IDataSeedContributor, ITransientDependency
+ private readonly IRepository _bookRepository;
+ private readonly IAuthorRepository _authorRepository;
+ private readonly AuthorManager _authorManager;
+
+ public BookStoreDataSeederContributor(
+ IRepository bookRepository,
+ IAuthorRepository authorRepository,
+ AuthorManager authorManager)
+ {
+ _bookRepository = bookRepository;
+ _authorRepository = authorRepository;
+ _authorManager = authorManager;
+ }
+
+ public async Task SeedAsync(DataSeedContext context)
{
- private readonly IRepository _bookRepository;
- private readonly IAuthorRepository _authorRepository;
- private readonly AuthorManager _authorManager;
-
- public BookStoreDataSeederContributor(
- IRepository bookRepository,
- IAuthorRepository authorRepository,
- AuthorManager authorManager)
+ if (await _bookRepository.GetCountAsync() > 0)
{
- _bookRepository = bookRepository;
- _authorRepository = authorRepository;
- _authorManager = authorManager;
+ return;
}
- public async Task SeedAsync(DataSeedContext context)
- {
- if (await _bookRepository.GetCountAsync() > 0)
+ var orwell = 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)."
+ )
+ );
+
+ 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;
- }
-
- var orwell = 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)."
- )
- );
-
- 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
- {
- 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
- );
- }
+ 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 Volo.Abp.Application.Dtos;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class BookDto : AuditedEntityDto
{
- public class BookDto : AuditedEntityDto
- {
- 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 Volo.Abp.Application.Dtos;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class AuthorLookupDto : EntityDto
{
- public class AuthorLookupDto : EntityDto
- {
- 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.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
{
- 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
- {
- // ADD the NEW METHOD
- Task> GetAuthorLookupAsync();
- }
+ // ADD the NEW METHOD
+ Task> GetAuthorLookupAsync();
}
````
@@ -341,119 +337,118 @@ using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+[Authorize(BookStorePermissions.Books.Default)]
+public class BookAppService :
+ CrudAppService<
+ Book, //The Book entity
+ BookDto, //Used to show books
+ Guid, //Primary key of the book entity
+ PagedAndSortedResultRequestDto, //Used for paging/sorting
+ CreateUpdateBookDto>, //Used to create/update a book
+ IBookAppService //implement the IBookAppService
{
- [Authorize(BookStorePermissions.Books.Default)]
- public class BookAppService :
- CrudAppService<
- Book, //The Book entity
- BookDto, //Used to show books
- Guid, //Primary key of the book entity
- PagedAndSortedResultRequestDto, //Used for paging/sorting
- CreateUpdateBookDto>, //Used to create/update a book
- IBookAppService //implement the IBookAppService
+ private readonly IAuthorRepository _authorRepository;
+
+ public BookAppService(
+ IRepository repository,
+ IAuthorRepository authorRepository)
+ : base(repository)
{
- private readonly IAuthorRepository _authorRepository;
+ _authorRepository = authorRepository;
+ GetPolicyName = BookStorePermissions.Books.Default;
+ GetListPolicyName = BookStorePermissions.Books.Default;
+ CreatePolicyName = BookStorePermissions.Books.Create;
+ UpdatePolicyName = BookStorePermissions.Books.Edit;
+ DeletePolicyName = BookStorePermissions.Books.Create;
+ }
- public BookAppService(
- IRepository repository,
- IAuthorRepository authorRepository)
- : base(repository)
+ public override async Task GetAsync(Guid id)
+ {
+ //Get the IQueryable from the repository
+ var queryable = await Repository.GetQueryableAsync();
+
+ //Prepare a query to join books and authors
+ var query = from book in queryable
+ join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
+ where book.Id == id
+ select new { book, author };
+
+ //Execute the query and get the book with author
+ var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query);
+ if (queryResult == null)
{
- _authorRepository = authorRepository;
- GetPolicyName = BookStorePermissions.Books.Default;
- GetListPolicyName = BookStorePermissions.Books.Default;
- CreatePolicyName = BookStorePermissions.Books.Create;
- UpdatePolicyName = BookStorePermissions.Books.Edit;
- DeletePolicyName = BookStorePermissions.Books.Create;
+ throw new EntityNotFoundException(typeof(Book), id);
}
- public override async Task GetAsync(Guid id)
- {
- //Get the IQueryable from the repository
- var queryable = await Repository.GetQueryableAsync();
-
- //Prepare a query to join books and authors
- var query = from book in queryable
- join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
- where book.Id == id
- select new { book, author };
-
- //Execute the query and get the book with author
- var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query);
- if (queryResult == null)
- {
- throw new EntityNotFoundException(typeof(Book), id);
- }
+ var bookDto = ObjectMapper.Map(queryResult.book);
+ bookDto.AuthorName = queryResult.author.Name;
+ return bookDto;
+ }
- var bookDto = ObjectMapper.Map(queryResult.book);
- bookDto.AuthorName = queryResult.author.Name;
- return bookDto;
- }
+ public override async Task> GetListAsync(PagedAndSortedResultRequestDto input)
+ {
+ //Get the IQueryable from the repository
+ var queryable = await Repository.GetQueryableAsync();
- public override async Task> GetListAsync(PagedAndSortedResultRequestDto input)
- {
- //Get the IQueryable from the repository
- var queryable = await Repository.GetQueryableAsync();
+ //Prepare a query to join books and authors
+ var query = from book in queryable
+ join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
+ select new {book, author};
- //Prepare a query to join books and authors
- var query = from book in queryable
- join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
- select new {book, author};
+ //Paging
+ query = query
+ .OrderBy(NormalizeSorting(input.Sorting))
+ .Skip(input.SkipCount)
+ .Take(input.MaxResultCount);
- //Paging
- query = query
- .OrderBy(NormalizeSorting(input.Sorting))
- .Skip(input.SkipCount)
- .Take(input.MaxResultCount);
+ //Execute the query and get a list
+ var queryResult = await AsyncExecuter.ToListAsync(query);
- //Execute the query and get a list
- var queryResult = await AsyncExecuter.ToListAsync(query);
+ //Convert the query result to a list of BookDto objects
+ var bookDtos = queryResult.Select(x =>
+ {
+ var bookDto = ObjectMapper.Map(x.book);
+ bookDto.AuthorName = x.author.Name;
+ return bookDto;
+ }).ToList();
- //Convert the query result to a list of BookDto objects
- var bookDtos = queryResult.Select(x =>
- {
- var bookDto = ObjectMapper.Map(x.book);
- bookDto.AuthorName = x.author.Name;
- return bookDto;
- }).ToList();
+ //Get the total count with another query
+ var totalCount = await Repository.GetCountAsync();
- //Get the total count with another query
- var totalCount = await Repository.GetCountAsync();
+ return new PagedResultDto(
+ totalCount,
+ bookDtos
+ );
+ }
- return new PagedResultDto(
- totalCount,
- bookDtos
- );
- }
+ public async Task> GetAuthorLookupAsync()
+ {
+ var authors = await _authorRepository.GetListAsync();
- public async Task> GetAuthorLookupAsync()
- {
- var authors = await _authorRepository.GetListAsync();
+ return new ListResultDto(
+ ObjectMapper.Map, List>(authors)
+ );
+ }
- return new ListResultDto(
- ObjectMapper.Map, List>(authors)
- );
+ private static string NormalizeSorting(string sorting)
+ {
+ if (sorting.IsNullOrEmpty())
+ {
+ return $"book.{nameof(Book.Name)}";
}
- private static string NormalizeSorting(string sorting)
+ if (sorting.Contains("authorName", StringComparison.OrdinalIgnoreCase))
{
- if (sorting.IsNullOrEmpty())
- {
- return $"book.{nameof(Book.Name)}";
- }
-
- if (sorting.Contains("authorName", StringComparison.OrdinalIgnoreCase))
- {
- return sorting.Replace(
- "authorName",
- "author.Name",
- StringComparison.OrdinalIgnoreCase
- );
- }
-
- return $"book.{sorting}";
+ return sorting.Replace(
+ "authorName",
+ "author.Name",
+ StringComparison.OrdinalIgnoreCase
+ );
}
+
+ return $"book.{sorting}";
}
}
```
@@ -485,108 +480,107 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+[Authorize(BookStorePermissions.Books.Default)]
+public class BookAppService :
+ CrudAppService<
+ Book, //The Book entity
+ BookDto, //Used to show books
+ Guid, //Primary key of the book entity
+ PagedAndSortedResultRequestDto, //Used for paging/sorting
+ CreateUpdateBookDto>, //Used to create/update a book
+ IBookAppService //implement the IBookAppService
{
- [Authorize(BookStorePermissions.Books.Default)]
- public class BookAppService :
- CrudAppService<
- Book, //The Book entity
- BookDto, //Used to show books
- Guid, //Primary key of the book entity
- PagedAndSortedResultRequestDto, //Used for paging/sorting
- CreateUpdateBookDto>, //Used to create/update a book
- IBookAppService //implement the IBookAppService
- {
- private readonly IAuthorRepository _authorRepository;
+ private readonly IAuthorRepository _authorRepository;
- public BookAppService(
- IRepository repository,
- IAuthorRepository authorRepository)
- : base(repository)
- {
- _authorRepository = authorRepository;
- GetPolicyName = BookStorePermissions.Books.Default;
- GetListPolicyName = BookStorePermissions.Books.Default;
- CreatePolicyName = BookStorePermissions.Books.Create;
- UpdatePolicyName = BookStorePermissions.Books.Edit;
- DeletePolicyName = BookStorePermissions.Books.Create;
- }
+ public BookAppService(
+ IRepository repository,
+ IAuthorRepository authorRepository)
+ : base(repository)
+ {
+ _authorRepository = authorRepository;
+ GetPolicyName = BookStorePermissions.Books.Default;
+ GetListPolicyName = BookStorePermissions.Books.Default;
+ CreatePolicyName = BookStorePermissions.Books.Create;
+ UpdatePolicyName = BookStorePermissions.Books.Edit;
+ DeletePolicyName = BookStorePermissions.Books.Create;
+ }
- public async override Task GetAsync(Guid id)
- {
- var book = await Repository.GetAsync(id);
- var bookDto = ObjectMapper.Map(book);
+ public async override Task GetAsync(Guid id)
+ {
+ var book = await Repository.GetAsync(id);
+ var bookDto = ObjectMapper.Map(book);
- var author = await _authorRepository.GetAsync(book.AuthorId);
- bookDto.AuthorName = author.Name;
+ var author = await _authorRepository.GetAsync(book.AuthorId);
+ bookDto.AuthorName = author.Name;
- return bookDto;
- }
+ return bookDto;
+ }
- public async override Task>
- GetListAsync(PagedAndSortedResultRequestDto input)
+ public async override Task>
+ GetListAsync(PagedAndSortedResultRequestDto input)
+ {
+ //Set a default sorting, if not provided
+ if (input.Sorting.IsNullOrWhiteSpace())
{
- //Set a default sorting, if not provided
- if (input.Sorting.IsNullOrWhiteSpace())
- {
- input.Sorting = nameof(Book.Name);
- }
+ input.Sorting = nameof(Book.Name);
+ }
- //Get the IQueryable from the repository
- var queryable = await Repository.GetQueryableAsync();
+ //Get the IQueryable from the repository
+ var queryable = await Repository.GetQueryableAsync();
- //Get the books
- var books = await AsyncExecuter.ToListAsync(
- queryable
- .OrderBy(input.Sorting)
- .Skip(input.SkipCount)
- .Take(input.MaxResultCount)
- );
+ //Get the books
+ var books = await AsyncExecuter.ToListAsync(
+ queryable
+ .OrderBy(input.Sorting)
+ .Skip(input.SkipCount)
+ .Take(input.MaxResultCount)
+ );
- //Convert to DTOs
- var bookDtos = ObjectMapper.Map, List>(books);
+ //Convert to DTOs
+ var bookDtos = ObjectMapper.Map, List>(books);
- //Get a lookup dictionary for the related authors
- var authorDictionary = await GetAuthorDictionaryAsync(books);
+ //Get a lookup dictionary for the related authors
+ var authorDictionary = await GetAuthorDictionaryAsync(books);
- //Set AuthorName for the DTOs
- bookDtos.ForEach(bookDto => bookDto.AuthorName =
- authorDictionary[bookDto.AuthorId].Name);
+ //Set AuthorName for the DTOs
+ bookDtos.ForEach(bookDto => bookDto.AuthorName =
+ authorDictionary[bookDto.AuthorId].Name);
- //Get the total count with another query (required for the paging)
- var totalCount = await Repository.GetCountAsync();
+ //Get the total count with another query (required for the paging)
+ var totalCount = await Repository.GetCountAsync();
- return new PagedResultDto(
- totalCount,
- bookDtos
- );
- }
+ return new PagedResultDto(
+ totalCount,
+ bookDtos
+ );
+ }
- public async Task> GetAuthorLookupAsync()
- {
- var authors = await _authorRepository.GetListAsync();
+ public async Task> GetAuthorLookupAsync()
+ {
+ var authors = await _authorRepository.GetListAsync();
- return new ListResultDto(
- ObjectMapper.Map, List>(authors)
- );
- }
+ return new ListResultDto(
+ ObjectMapper.Map, List>(authors)
+ );
+ }
- private async Task>
- GetAuthorDictionaryAsync(List books)
- {
- var authorIds = books
- .Select(b => b.AuthorId)
- .Distinct()
- .ToArray();
+ private async Task>
+ GetAuthorDictionaryAsync(List books)
+ {
+ var authorIds = books
+ .Select(b => b.AuthorId)
+ .Distinct()
+ .ToArray();
- var queryable = await _authorRepository.GetQueryableAsync();
+ var queryable = await _authorRepository.GetQueryableAsync();
- var authors = await AsyncExecuter.ToListAsync(
- queryable.Where(a => authorIds.Contains(a.Id))
- );
+ var authors = await AsyncExecuter.ToListAsync(
+ queryable.Where(a => authorIds.Contains(a.Id))
+ );
- return authors.ToDictionary(x => x.Id, x => x);
- }
+ return authors.ToDictionary(x => x.Id, x => x);
}
}
```
@@ -623,76 +617,76 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
-namespace Acme.BookStore.Books
-{ {{if DB=="Mongo"}}
- [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
- public class BookAppService_Tests : BookStoreApplicationTestBase
+namespace Acme.BookStore.Books;
+
+ {{if DB=="Mongo"}}
+[Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
+public class BookAppService_Tests : BookStoreApplicationTestBase
+{
+ private readonly IBookAppService _bookAppService;
+ private readonly IAuthorAppService _authorAppService;
+
+ public BookAppService_Tests()
{
- private readonly IBookAppService _bookAppService;
- private readonly IAuthorAppService _authorAppService;
+ _bookAppService = GetRequiredService();
+ _authorAppService = GetRequiredService();
+ }
- public BookAppService_Tests()
- {
- _bookAppService = GetRequiredService();
- _authorAppService = GetRequiredService();
- }
+ [Fact]
+ public async Task Should_Get_List_Of_Books()
+ {
+ //Act
+ var result = await _bookAppService.GetListAsync(
+ new PagedAndSortedResultRequestDto()
+ );
+
+ //Assert
+ result.TotalCount.ShouldBeGreaterThan(0);
+ result.Items.ShouldContain(b => b.Name == "1984" &&
+ b.AuthorName == "George Orwell");
+ }
- [Fact]
- public async Task Should_Get_List_Of_Books()
- {
- //Act
- var result = await _bookAppService.GetListAsync(
- new PagedAndSortedResultRequestDto()
- );
+ [Fact]
+ public async Task Should_Create_A_Valid_Book()
+ {
+ var authors = await _authorAppService.GetListAsync(new GetAuthorListDto());
+ var firstAuthor = authors.Items.First();
- //Assert
- result.TotalCount.ShouldBeGreaterThan(0);
- result.Items.ShouldContain(b => b.Name == "1984" &&
- b.AuthorName == "George Orwell");
- }
+ //Act
+ var result = await _bookAppService.CreateAsync(
+ new CreateUpdateBookDto
+ {
+ AuthorId = firstAuthor.Id,
+ Name = "New test book 42",
+ Price = 10,
+ PublishDate = System.DateTime.Now,
+ Type = BookType.ScienceFiction
+ }
+ );
- [Fact]
- public async Task Should_Create_A_Valid_Book()
- {
- var authors = await _authorAppService.GetListAsync(new GetAuthorListDto());
- var firstAuthor = authors.Items.First();
+ //Assert
+ result.Id.ShouldNotBe(Guid.Empty);
+ result.Name.ShouldBe("New test book 42");
+ }
- //Act
- var result = await _bookAppService.CreateAsync(
+ [Fact]
+ public async Task Should_Not_Create_A_Book_Without_Name()
+ {
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
- AuthorId = firstAuthor.Id,
- Name = "New test book 42",
+ Name = "",
Price = 10,
- PublishDate = System.DateTime.Now,
+ PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
+ });
- //Assert
- result.Id.ShouldNotBe(Guid.Empty);
- result.Name.ShouldBe("New test book 42");
- }
-
- [Fact]
- public async Task Should_Not_Create_A_Book_Without_Name()
- {
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await _bookAppService.CreateAsync(
- new CreateUpdateBookDto
- {
- Name = "",
- Price = 10,
- PublishDate = DateTime.Now,
- Type = BookType.ScienceFiction
- }
- );
- });
-
- exception.ValidationErrors
- .ShouldContain(err => err.MemberNames.Any(m => m == "Name"));
- }
+ exception.ValidationErrors
+ .ShouldContain(err => err.MemberNames.Any(m => m == "Name"));
}
}
```
@@ -751,61 +745,60 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
-namespace Acme.BookStore.Web.Pages.Books
+namespace Acme.BookStore.Web.Pages.Books;
+
+public class CreateModalModel : BookStorePageModel
{
- public class CreateModalModel : BookStorePageModel
- {
- [BindProperty]
- public CreateBookViewModel Book { get; set; }
+ [BindProperty]
+ public CreateBookViewModel Book { get; set; }
- public List Authors { get; set; }
+ public List Authors { get; set; }
- private readonly IBookAppService _bookAppService;
+ private readonly IBookAppService _bookAppService;
- public CreateModalModel(
- IBookAppService bookAppService)
- {
- _bookAppService = bookAppService;
- }
+ public CreateModalModel(
+ IBookAppService bookAppService)
+ {
+ _bookAppService = bookAppService;
+ }
- public async Task OnGetAsync()
- {
- Book = new CreateBookViewModel();
+ public async Task OnGetAsync()
+ {
+ Book = new CreateBookViewModel();
- var authorLookup = await _bookAppService.GetAuthorLookupAsync();
- Authors = authorLookup.Items
- .Select(x => new SelectListItem(x.Name, x.Id.ToString()))
- .ToList();
- }
+ var authorLookup = await _bookAppService.GetAuthorLookupAsync();
+ Authors = authorLookup.Items
+ .Select(x => new SelectListItem(x.Name, x.Id.ToString()))
+ .ToList();
+ }
- public async Task OnPostAsync()
- {
- await _bookAppService.CreateAsync(
- ObjectMapper.Map(Book)
- );
- return NoContent();
- }
+ public async Task OnPostAsync()
+ {
+ await _bookAppService.CreateAsync(
+ ObjectMapper.Map(Book)
+ );
+ return NoContent();
+ }
- public class CreateBookViewModel
- {
- [SelectItems(nameof(Authors))]
- [DisplayName("Author")]
- public Guid AuthorId { get; set; }
+ public class CreateBookViewModel
+ {
+ [SelectItems(nameof(Authors))]
+ [DisplayName("Author")]
+ public Guid AuthorId { get; set; }
- [Required]
- [StringLength(128)]
- public string Name { get; set; }
+ [Required]
+ [StringLength(128)]
+ public string Name { get; set; }
- [Required]
- public BookType Type { get; set; } = BookType.Undefined;
+ [Required]
+ public BookType Type { get; set; } = BookType.Undefined;
- [Required]
- [DataType(DataType.Date)]
- public DateTime PublishDate { get; set; } = DateTime.Now;
+ [Required]
+ [DataType(DataType.Date)]
+ public DateTime PublishDate { get; set; } = DateTime.Now;
- [Required]
- public float Price { get; set; }
- }
+ [Required]
+ public float Price { get; set; }
}
}
```
@@ -830,66 +823,65 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
-namespace Acme.BookStore.Web.Pages.Books
+namespace Acme.BookStore.Web.Pages.Books;
+
+public class EditModalModel : BookStorePageModel
{
- public class EditModalModel : BookStorePageModel
- {
- [BindProperty]
- public EditBookViewModel Book { get; set; }
+ [BindProperty]
+ public EditBookViewModel Book { get; set; }
- public List Authors { get; set; }
+ public List Authors { get; set; }
- private readonly IBookAppService _bookAppService;
+ private readonly IBookAppService _bookAppService;
- public EditModalModel(IBookAppService bookAppService)
- {
- _bookAppService = bookAppService;
- }
+ public EditModalModel(IBookAppService bookAppService)
+ {
+ _bookAppService = bookAppService;
+ }
- public async Task OnGetAsync(Guid id)
- {
- var bookDto = await _bookAppService.GetAsync(id);
- Book = ObjectMapper.Map(bookDto);
+ public async Task OnGetAsync(Guid id)
+ {
+ var bookDto = await _bookAppService.GetAsync(id);
+ Book = ObjectMapper.Map(bookDto);
- var authorLookup = await _bookAppService.GetAuthorLookupAsync();
- Authors = authorLookup.Items
- .Select(x => new SelectListItem(x.Name, x.Id.ToString()))
- .ToList();
- }
+ var authorLookup = await _bookAppService.GetAuthorLookupAsync();
+ Authors = authorLookup.Items
+ .Select(x => new SelectListItem(x.Name, x.Id.ToString()))
+ .ToList();
+ }
- public async Task OnPostAsync()
- {
- await _bookAppService.UpdateAsync(
- Book.Id,
- ObjectMapper.Map(Book)
- );
+ public async Task OnPostAsync()
+ {
+ await _bookAppService.UpdateAsync(
+ Book.Id,
+ ObjectMapper.Map(Book)
+ );
- return NoContent();
- }
+ return NoContent();
+ }
- public class EditBookViewModel
- {
- [HiddenInput]
- public Guid Id { get; set; }
+ public class EditBookViewModel
+ {
+ [HiddenInput]
+ public Guid Id { get; set; }
- [SelectItems(nameof(Authors))]
- [DisplayName("Author")]
- public Guid AuthorId { get; set; }
+ [SelectItems(nameof(Authors))]
+ [DisplayName("Author")]
+ public Guid AuthorId { get; set; }
- [Required]
- [StringLength(128)]
- public string Name { get; set; }
+ [Required]
+ [StringLength(128)]
+ public string Name { get; set; }
- [Required]
- public BookType Type { get; set; } = BookType.Undefined;
+ [Required]
+ public BookType Type { get; set; } = BookType.Undefined;
- [Required]
- [DataType(DataType.Date)]
- public DateTime PublishDate { get; set; } = DateTime.Now;
+ [Required]
+ [DataType(DataType.Date)]
+ public DateTime PublishDate { get; set; } = DateTime.Now;
- [Required]
- public float Price { get; set; }
- }
+ [Required]
+ public float Price { get; set; }
}
}
```
@@ -964,13 +956,13 @@ Book list page change is trivial. Open the `/src/app/book/book.component.html` a
When you run the application, you can see the *Author* column on the table:
-
+
### Create/Edit Forms
The next step is to add an Author selection (dropdown) to the create/edit forms. The final UI will look like the one shown below:
-
+
Added the Author dropdown as the first element in the form.
@@ -1112,7 +1104,7 @@ It is very easy to show the *Author Name* in the book list. Open the `/Pages/Boo
When you run the application, you can see the *Author* column on the table:
-
+
### Create Book Modal
@@ -1183,7 +1175,7 @@ This requires to add a new localization key to the `en.json` file:
You can run the application to see the *Author Selection* while creating a new book:
-
+
### Edit Book Modal
diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md
index 91b0453b53..d22e4b5062 100644
--- a/docs/en/Tutorials/Part-2.md
+++ b/docs/en/Tutorials/Part-2.md
@@ -178,14 +178,13 @@ Open the `Index.cshtml` and change the whole content as shown below:
```csharp
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:
-
+
{{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:
-
+
When you click on the Books menu item under the Book Store parent, you will be redirected to the new empty Books Page.
@@ -621,7 +620,7 @@ Open the `Books.razor` and replace the content as the following:
Field="@nameof(BookDto.Type)"
Caption="@L["Type"]">
- @L[$"Enum:BookType.{Enum.GetName(context.Type)}"]
+ @L[$"Enum:BookType.{context.Type}"]
(bookDto);
- }
+ public async Task OnGetAsync()
+ {
+ var bookDto = await _bookAppService.GetAsync(Id);
+ Book = ObjectMapper.Map(bookDto);
+ }
- public async Task OnPostAsync()
- {
- await _bookAppService.UpdateAsync(Id, Book);
- return NoContent();
- }
+ public async Task OnPostAsync()
+ {
+ await _bookAppService.UpdateAsync(Id, Book);
+ return NoContent();
}
}
````
@@ -327,14 +326,13 @@ To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mappin
````csharp
using AutoMapper;
-namespace Acme.BookStore.Web
+namespace Acme.BookStore.Web;
+
+public class BookStoreWebAutoMapperProfile : Profile
{
- public class BookStoreWebAutoMapperProfile : Profile
+ public BookStoreWebAutoMapperProfile()
{
- public BookStoreWebAutoMapperProfile()
- {
- CreateMap();
- }
+ CreateMap();
}
}
````
@@ -689,7 +687,7 @@ Open `/src/app/book/book.component.html` and make the following changes:
You can open your browser and click the **New book** button to see the new modal.
-
+
### Create a Reactive Form
@@ -926,7 +924,7 @@ export class BookComponent implements OnInit {
Now, you can open your browser to see the changes:
-
+
## Updating a Book
@@ -1054,7 +1052,7 @@ Open `/src/app/book/book.component.html` and add the following `ngx-datatable-
Added an "Actions" dropdown as the first column of the table that is shown below:
-
+
Also, change the `ng-template #abpHeader` section as shown below:
@@ -1118,11 +1116,11 @@ Open `/src/app/book/book.component.html` and modify the `ngbDropdownMenu` to add
The final actions dropdown UI looks like below:
-
+
Clicking the "Delete" action calls the `delete` method which then shows a confirmation popup as shown below:
-
+
{{end}}
@@ -1152,7 +1150,7 @@ Open the `Books.razor` and replace the `` section with the following
This will change the card header by adding a "New book" button to the right side:
-
+
Now, we can add a modal that will be opened when we click the button.
@@ -1187,7 +1185,7 @@ Open the `Books.razor` and add the following code to the end of the page:
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
- @L[$"Enum:BookType.{Enum.GetName((BookType)bookTypeValue)}"]
+ @L[$"Enum:BookType.{bookTypeValue}"]
}
@@ -1226,7 +1224,7 @@ This code requires a service; Inject the `AbpBlazorMessageLocalizerHelper` at
That's all. Run the application and try to add a new book:
-
+
## Updating a Book
@@ -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:
-
+
### Edit Modal
@@ -1285,7 +1283,7 @@ We can now define a modal to edit the book. Add the following code to the end of
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
- @L[$"Enum:BookType.{Enum.GetName((BookType)bookTypeValue)}"]
+ @L[$"Enum:BookType.{bookTypeValue}"]
}
@@ -1323,14 +1321,13 @@ Open the `BookStoreBlazorAutoMapperProfile` inside the `Acme.BookStore.Blazor` p
using Acme.BookStore.Books;
using AutoMapper;
-namespace Acme.BookStore.Blazor
+namespace Acme.BookStore.Blazor;
+
+public class BookStoreBlazorAutoMapperProfile : Profile
{
- public class BookStoreBlazorAutoMapperProfile : Profile
+ public BookStoreBlazorAutoMapperProfile()
{
- public BookStoreBlazorAutoMapperProfile()
- {
- CreateMap();
- }
+ CreateMap();
}
}
````
@@ -1341,7 +1338,7 @@ namespace Acme.BookStore.Blazor
You can now run the application and try to edit a book.
-
+
> Tip: Try to leave the *Name* field empty and submit the form to show the validation error message.
@@ -1362,7 +1359,7 @@ Open the `Books.razor` page and add the following `EntityAction` code under the
The "Actions" button becomes a dropdown since it has two actions now:
-
+
Run the application and try to delete a book.
@@ -1422,7 +1419,7 @@ Here's the complete code to create the book management CRUD page, that has been
Field="@nameof(BookDto.Type)"
Caption="@L["Type"]">
- @L[$"Enum:BookType.{Enum.GetName(context.Type)}"]
+ @L[$"Enum:BookType.{context.Type}"]
- @L[$"Enum:BookType.{Enum.GetName((BookType)bookTypeValue)}"]
+ @L[$"Enum:BookType.{bookTypeValue}"]
}
@@ -1527,7 +1524,7 @@ Here's the complete code to create the book management CRUD page, that has been
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
- @L[$"Enum:BookType.{Enum.GetName((BookType)bookTypeValue)}"]
+ @L[$"Enum:BookType.{bookTypeValue}"]
}
diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md
index d956a299db..072392e82b 100644
--- a/docs/en/Tutorials/Part-4.md
+++ b/docs/en/Tutorials/Part-4.md
@@ -85,30 +85,31 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
-namespace Acme.BookStore.Books
-{ {{if DB=="Mongo"}}
- [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
- public class BookAppService_Tests : BookStoreApplicationTestBase
- {
- private readonly IBookAppService _bookAppService;
+namespace Acme.BookStore.Books;
- public BookAppService_Tests()
- {
- _bookAppService = GetRequiredService();
- }
+{{if DB=="Mongo"}}
+[Collection(BookStoreTestConsts.CollectionDefinitionName)]
+{{end}}
+public class BookAppService_Tests : BookStoreApplicationTestBase
+{
+ private readonly IBookAppService _bookAppService;
- [Fact]
- public async Task Should_Get_List_Of_Books()
- {
- //Act
- var result = await _bookAppService.GetListAsync(
- new PagedAndSortedResultRequestDto()
- );
+ public BookAppService_Tests()
+ {
+ _bookAppService = GetRequiredService();
+ }
- //Assert
- result.TotalCount.ShouldBeGreaterThan(0);
- result.Items.ShouldContain(b => b.Name == "1984");
- }
+ [Fact]
+ public async Task Should_Get_List_Of_Books()
+ {
+ //Act
+ var result = await _bookAppService.GetListAsync(
+ new PagedAndSortedResultRequestDto()
+ );
+
+ //Assert
+ result.TotalCount.ShouldBeGreaterThan(0);
+ result.Items.ShouldContain(b => b.Name == "1984");
}
}
````
@@ -176,69 +177,70 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
-namespace Acme.BookStore.Books
-{ {{if DB=="Mongo"}}
- [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
- public class BookAppService_Tests : BookStoreApplicationTestBase
+namespace Acme.BookStore.Books;
+
+{{if DB=="Mongo"}}
+[Collection(BookStoreTestConsts.CollectionDefinitionName)]
+{{end}}
+public class BookAppService_Tests : BookStoreApplicationTestBase
+{
+ private readonly IBookAppService _bookAppService;
+
+ public BookAppService_Tests()
{
- private readonly IBookAppService _bookAppService;
+ _bookAppService = GetRequiredService();
+ }
- public BookAppService_Tests()
- {
- _bookAppService = GetRequiredService();
- }
+ [Fact]
+ public async Task Should_Get_List_Of_Books()
+ {
+ //Act
+ var result = await _bookAppService.GetListAsync(
+ new PagedAndSortedResultRequestDto()
+ );
- [Fact]
- public async Task Should_Get_List_Of_Books()
- {
- //Act
- var result = await _bookAppService.GetListAsync(
- new PagedAndSortedResultRequestDto()
- );
+ //Assert
+ result.TotalCount.ShouldBeGreaterThan(0);
+ result.Items.ShouldContain(b => b.Name == "1984");
+ }
+
+ [Fact]
+ public async Task Should_Create_A_Valid_Book()
+ {
+ //Act
+ var result = await _bookAppService.CreateAsync(
+ new CreateUpdateBookDto
+ {
+ Name = "New test book 42",
+ Price = 10,
+ PublishDate = DateTime.Now,
+ Type = BookType.ScienceFiction
+ }
+ );
- //Assert
- result.TotalCount.ShouldBeGreaterThan(0);
- result.Items.ShouldContain(b => b.Name == "1984");
- }
-
- [Fact]
- public async Task Should_Create_A_Valid_Book()
+ //Assert
+ result.Id.ShouldNotBe(Guid.Empty);
+ result.Name.ShouldBe("New test book 42");
+ }
+
+ [Fact]
+ public async Task Should_Not_Create_A_Book_Without_Name()
+ {
+ var exception = await Assert.ThrowsAsync(async () =>
{
- //Act
- var result = await _bookAppService.CreateAsync(
+ await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
- Name = "New test book 42",
+ Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
+ });
- //Assert
- result.Id.ShouldNotBe(Guid.Empty);
- result.Name.ShouldBe("New test book 42");
- }
-
- [Fact]
- public async Task Should_Not_Create_A_Book_Without_Name()
- {
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await _bookAppService.CreateAsync(
- new CreateUpdateBookDto
- {
- Name = "",
- Price = 10,
- PublishDate = DateTime.Now,
- Type = BookType.ScienceFiction
- }
- );
- });
-
- exception.ValidationErrors
- .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
- }
+ exception.ValidationErrors
+ .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
}
````
diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md
index 970f46b43c..4a6bebe958 100644
--- a/docs/en/Tutorials/Part-5.md
+++ b/docs/en/Tutorials/Part-5.md
@@ -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:
````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 static class Books
- {
- public const string Default = GroupName + ".Books";
- public const string Create = Default + ".Create";
- 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.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 booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));
- booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create"));
- booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit"));
- booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete"));
- }
-
- private static LocalizableString L(string name)
- {
- return LocalizableString.Create(name);
- }
+ var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore"));
+
+ var booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));
+ booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create"));
+ booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit"));
+ booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete"));
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
}
}
````
@@ -148,26 +146,25 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore.Books
+namespace Acme.BookStore.Books;
+
+public class BookAppService :
+ CrudAppService<
+ Book, //The Book entity
+ BookDto, //Used to show books
+ Guid, //Primary key of the book entity
+ PagedAndSortedResultRequestDto, //Used for paging/sorting
+ CreateUpdateBookDto>, //Used to create/update a book
+ IBookAppService //implement the IBookAppService
{
- public class BookAppService :
- CrudAppService<
- Book, //The Book entity
- BookDto, //Used to show books
- Guid, //Primary key of the book entity
- PagedAndSortedResultRequestDto, //Used for paging/sorting
- CreateUpdateBookDto>, //Used to create/update a book
- IBookAppService //implement the IBookAppService
+ public BookAppService(IRepository repository)
+ : base(repository)
{
- public BookAppService(IRepository repository)
- : base(repository)
- {
- GetPolicyName = BookStorePermissions.Books.Default;
- GetListPolicyName = BookStorePermissions.Books.Default;
- CreatePolicyName = BookStorePermissions.Books.Create;
- UpdatePolicyName = BookStorePermissions.Books.Edit;
- DeletePolicyName = BookStorePermissions.Books.Delete;
- }
+ GetPolicyName = BookStorePermissions.Books.Default;
+ GetListPolicyName = BookStorePermissions.Books.Default;
+ CreatePolicyName = BookStorePermissions.Books.Create;
+ UpdatePolicyName = BookStorePermissions.Books.Edit;
+ DeletePolicyName = BookStorePermissions.Books.Delete;
}
}
````
diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md
index 36f1bc6d0a..40b334808c 100644
--- a/docs/en/Tutorials/Part-6.md
+++ b/docs/en/Tutorials/Part-6.md
@@ -60,45 +60,44 @@ using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class Author : FullAuditedAggregateRoot
{
- public class Author : FullAuditedAggregateRoot
- {
- public string Name { get; private set; }
- public DateTime BirthDate { get; set; }
- public string ShortBio { get; set; }
+ public string Name { get; private set; }
+ public DateTime BirthDate { get; set; }
+ public string ShortBio { get; set; }
- private Author()
- {
- /* This constructor is for deserialization / ORM purpose */
- }
+ private Author()
+ {
+ /* This constructor is for deserialization / ORM purpose */
+ }
- internal Author(
- Guid id,
- [NotNull] string name,
- DateTime birthDate,
- [CanBeNull] string shortBio = null)
- : base(id)
- {
- SetName(name);
- BirthDate = birthDate;
- ShortBio = shortBio;
- }
+ internal Author(
+ Guid id,
+ [NotNull] string name,
+ DateTime birthDate,
+ [CanBeNull] string shortBio = null)
+ : base(id)
+ {
+ SetName(name);
+ BirthDate = birthDate;
+ ShortBio = shortBio;
+ }
- internal Author ChangeName([NotNull] string name)
- {
- SetName(name);
- return this;
- }
+ internal Author ChangeName([NotNull] string name)
+ {
+ SetName(name);
+ return this;
+ }
- private void SetName([NotNull] string name)
- {
- Name = Check.NotNullOrWhiteSpace(
- name,
- nameof(name),
- maxLength: AuthorConsts.MaxNameLength
- );
- }
+ private void SetName([NotNull] string name)
+ {
+ Name = Check.NotNullOrWhiteSpace(
+ name,
+ nameof(name),
+ maxLength: AuthorConsts.MaxNameLength
+ );
}
}
````
@@ -113,13 +112,13 @@ namespace Acme.BookStore.Authors
`AuthorConsts` is a simple class that is located under the `Authors` namespace (folder) of the `Acme.BookStore.Domain.Shared` project:
````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.
@@ -135,53 +134,52 @@ using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Services;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class AuthorManager : DomainService
{
- public class AuthorManager : DomainService
+ private readonly IAuthorRepository _authorRepository;
+
+ public AuthorManager(IAuthorRepository authorRepository)
{
- private readonly IAuthorRepository _authorRepository;
+ _authorRepository = authorRepository;
+ }
- public AuthorManager(IAuthorRepository authorRepository)
- {
- _authorRepository = authorRepository;
- }
+ public async Task CreateAsync(
+ [NotNull] string name,
+ DateTime birthDate,
+ [CanBeNull] string shortBio = null)
+ {
+ Check.NotNullOrWhiteSpace(name, nameof(name));
- public async Task CreateAsync(
- [NotNull] string name,
- DateTime birthDate,
- [CanBeNull] string shortBio = null)
+ var existingAuthor = await _authorRepository.FindByNameAsync(name);
+ if (existingAuthor != null)
{
- Check.NotNullOrWhiteSpace(name, nameof(name));
-
- var existingAuthor = await _authorRepository.FindByNameAsync(name);
- if (existingAuthor != null)
- {
- throw new AuthorAlreadyExistsException(name);
- }
-
- return new Author(
- GuidGenerator.Create(),
- name,
- birthDate,
- shortBio
- );
+ throw new AuthorAlreadyExistsException(name);
}
- public async Task ChangeNameAsync(
- [NotNull] Author author,
- [NotNull] string newName)
- {
- Check.NotNull(author, nameof(author));
- Check.NotNullOrWhiteSpace(newName, nameof(newName));
+ return new Author(
+ GuidGenerator.Create(),
+ name,
+ birthDate,
+ shortBio
+ );
+ }
- var existingAuthor = await _authorRepository.FindByNameAsync(newName);
- if (existingAuthor != null && existingAuthor.Id != author.Id)
- {
- throw new AuthorAlreadyExistsException(newName);
- }
+ public async Task ChangeNameAsync(
+ [NotNull] Author author,
+ [NotNull] string newName)
+ {
+ Check.NotNull(author, nameof(author));
+ Check.NotNullOrWhiteSpace(newName, nameof(newName));
- author.ChangeName(newName);
+ var existingAuthor = await _authorRepository.FindByNameAsync(newName);
+ if (existingAuthor != null && existingAuthor.Id != author.Id)
+ {
+ throw new AuthorAlreadyExistsException(newName);
}
+
+ author.ChangeName(newName);
}
}
````
@@ -195,15 +193,14 @@ Both methods checks if there is already an author with the given name and throws
````csharp
using Volo.Abp;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class AuthorAlreadyExistsException : BusinessException
{
- public class AuthorAlreadyExistsException : BusinessException
+ public AuthorAlreadyExistsException(string name)
+ : base(BookStoreDomainErrorCodes.AuthorAlreadyExists)
{
- public AuthorAlreadyExistsException(string name)
- : base(BookStoreDomainErrorCodes.AuthorAlreadyExists)
- {
- WithData("name", name);
- }
+ WithData("name", name);
}
}
````
@@ -213,12 +210,11 @@ namespace Acme.BookStore.Authors
Open the `BookStoreDomainErrorCodes` in the `Acme.BookStore.Domain.Shared` project and change as shown below:
````csharp
-namespace Acme.BookStore
+namespace Acme.BookStore;
+
+public static class BookStoreDomainErrorCodes
{
- public static class BookStoreDomainErrorCodes
- {
- public const string AuthorAlreadyExists = "BookStore:00001";
- }
+ public const string AuthorAlreadyExists = "BookStore:00001";
}
````
@@ -240,19 +236,18 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore.Authors
-{
- public interface IAuthorRepository : IRepository
- {
- Task FindByNameAsync(string name);
+namespace Acme.BookStore.Authors;
- Task> GetListAsync(
- int skipCount,
- int maxResultCount,
- string sorting,
- string filter = null
- );
- }
+public interface IAuthorRepository : IRepository
+{
+ Task FindByNameAsync(string name);
+
+ Task> GetListAsync(
+ int skipCount,
+ int maxResultCount,
+ string sorting,
+ string filter = null
+ );
}
````
diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md
index d60d030f13..82f01bf963 100644
--- a/docs/en/Tutorials/Part-7.md
+++ b/docs/en/Tutorials/Part-7.md
@@ -121,41 +121,40 @@ using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class EfCoreAuthorRepository
+ : EfCoreRepository,
+ IAuthorRepository
{
- public class EfCoreAuthorRepository
- : EfCoreRepository,
- IAuthorRepository
+ public EfCoreAuthorRepository(
+ IDbContextProvider dbContextProvider)
+ : base(dbContextProvider)
+ {
+ }
+
+ public async Task FindByNameAsync(string name)
{
- public EfCoreAuthorRepository(
- IDbContextProvider dbContextProvider)
- : base(dbContextProvider)
- {
- }
-
- public async Task FindByNameAsync(string name)
- {
- var dbSet = await GetDbSetAsync();
- return await dbSet.FirstOrDefaultAsync(author => author.Name == name);
- }
-
- public async Task> GetListAsync(
- int skipCount,
- int maxResultCount,
- string sorting,
- string filter = null)
- {
- var dbSet = await GetDbSetAsync();
- return await dbSet
- .WhereIf(
- !filter.IsNullOrWhiteSpace(),
- author => author.Name.Contains(filter)
- )
- .OrderBy(sorting)
- .Skip(skipCount)
- .Take(maxResultCount)
- .ToListAsync();
- }
+ var dbSet = await GetDbSetAsync();
+ return await dbSet.FirstOrDefaultAsync(author => author.Name == name);
+ }
+
+ public async Task> GetListAsync(
+ int skipCount,
+ int maxResultCount,
+ string sorting,
+ string filter = null)
+ {
+ var dbSet = await GetDbSetAsync();
+ return await dbSet
+ .WhereIf(
+ !filter.IsNullOrWhiteSpace(),
+ author => author.Name.Contains(filter)
+ )
+ .OrderBy(sorting)
+ .Skip(skipCount)
+ .Take(maxResultCount)
+ .ToListAsync();
}
}
````
@@ -182,42 +181,41 @@ using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class MongoDbAuthorRepository
+ : MongoDbRepository,
+ IAuthorRepository
{
- public class MongoDbAuthorRepository
- : MongoDbRepository,
- IAuthorRepository
+ public MongoDbAuthorRepository(
+ IMongoDbContextProvider dbContextProvider
+ ) : base(dbContextProvider)
{
- public MongoDbAuthorRepository(
- IMongoDbContextProvider dbContextProvider
- ) : base(dbContextProvider)
- {
- }
-
- public async Task FindByNameAsync(string name)
- {
- var queryable = await GetMongoQueryableAsync();
- return await queryable.FirstOrDefaultAsync(author => author.Name == name);
- }
-
- public async Task> GetListAsync(
- int skipCount,
- int maxResultCount,
- string sorting,
- string filter = null)
- {
- var queryable = await GetMongoQueryableAsync();
- return await queryable
- .WhereIf>(
- !filter.IsNullOrWhiteSpace(),
- author => author.Name.Contains(filter)
- )
- .OrderBy(sorting)
- .As>()
- .Skip(skipCount)
- .Take(maxResultCount)
- .ToListAsync();
- }
+ }
+
+ public async Task FindByNameAsync(string name)
+ {
+ var queryable = await GetMongoQueryableAsync();
+ return await queryable.FirstOrDefaultAsync(author => author.Name == name);
+ }
+
+ public async Task> GetListAsync(
+ int skipCount,
+ int maxResultCount,
+ string sorting,
+ string filter = null)
+ {
+ var queryable = await GetMongoQueryableAsync();
+ return await queryable
+ .WhereIf>(
+ !filter.IsNullOrWhiteSpace(),
+ author => author.Name.Contains(filter)
+ )
+ .OrderBy(sorting)
+ .As>()
+ .Skip(skipCount)
+ .Take(maxResultCount)
+ .ToListAsync();
}
}
```
diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md
index cdde623a08..24dafffdb9 100644
--- a/docs/en/Tutorials/Part-8.md
+++ b/docs/en/Tutorials/Part-8.md
@@ -50,20 +50,19 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public interface IAuthorAppService : IApplicationService
{
- public interface IAuthorAppService : IApplicationService
- {
- Task GetAsync(Guid id);
+ Task GetAsync(Guid id);
- Task> GetListAsync(GetAuthorListDto input);
+ Task> GetListAsync(GetAuthorListDto input);
- Task CreateAsync(CreateAuthorDto input);
+ Task CreateAsync(CreateAuthorDto input);
- Task UpdateAsync(Guid id, UpdateAuthorDto input);
+ Task UpdateAsync(Guid id, UpdateAuthorDto input);
- Task DeleteAsync(Guid id);
- }
+ Task DeleteAsync(Guid id);
}
````
@@ -80,16 +79,15 @@ This interface is using the DTOs defined below (create them for your project).
using System;
using Volo.Abp.Application.Dtos;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class AuthorDto : EntityDto
{
- public class AuthorDto : EntityDto
- {
- public string Name { get; set; }
+ public string Name { get; set; }
- public DateTime BirthDate { get; set; }
+ public DateTime BirthDate { get; set; }
- public string ShortBio { get; set; }
- }
+ public string ShortBio { get; set; }
}
````
@@ -100,12 +98,11 @@ namespace Acme.BookStore.Authors
````csharp
using Volo.Abp.Application.Dtos;
-namespace Acme.BookStore.Authors
+namespace Acme.BookStore.Authors;
+
+public class GetAuthorListDto : PagedAndSortedResultRequestDto
{
- public class GetAuthorListDto : PagedAndSortedResultRequestDto
- {
- public string Filter { get; set; }
- }
+ public string Filter { get; set; }
}
````
@@ -120,19 +117,18 @@ namespace Acme.BookStore.Authors
using System;
using System.ComponentModel.DataAnnotations;
-namespace Acme.BookStore.Authors
-{
- public class CreateAuthorDto
- {
- [Required]
- [StringLength(AuthorConsts.MaxNameLength)]
- public string Name { get; set; }
+namespace Acme.BookStore.Authors;
- [Required]
- public DateTime BirthDate { get; set; }
-
- public string ShortBio { get; set; }
- }
+public class CreateAuthorDto
+{
+ [Required]
+ [StringLength(AuthorConsts.MaxNameLength)]
+ public string Name { get; set; }
+
+ [Required]
+ public DateTime BirthDate { get; set; }
+
+ public string ShortBio { get; set; }
}
````
@@ -144,19 +140,18 @@ Data annotation attributes can be used to validate the DTO. See the [validation
using System;
using System.ComponentModel.DataAnnotations;
-namespace Acme.BookStore.Authors
-{
- public class UpdateAuthorDto
- {
- [Required]
- [StringLength(AuthorConsts.MaxNameLength)]
- public string Name { get; set; }
+namespace Acme.BookStore.Authors;
- [Required]
- public DateTime BirthDate { get; set; }
-
- public string ShortBio { get; set; }
- }
+public class UpdateAuthorDto
+{
+ [Required]
+ [StringLength(AuthorConsts.MaxNameLength)]
+ public string Name { get; set; }
+
+ [Required]
+ public DateTime BirthDate { get; set; }
+
+ public string ShortBio { get; set; }
}
````
@@ -176,24 +171,23 @@ using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore.Authors
-{
- [Authorize(BookStorePermissions.Authors.Default)]
- public class AuthorAppService : BookStoreAppService, IAuthorAppService
- {
- private readonly IAuthorRepository _authorRepository;
- private readonly AuthorManager _authorManager;
+namespace Acme.BookStore.Authors;
- public AuthorAppService(
- IAuthorRepository authorRepository,
- AuthorManager authorManager)
- {
- _authorRepository = authorRepository;
- _authorManager = authorManager;
- }
+[Authorize(BookStorePermissions.Authors.Default)]
+public class AuthorAppService : BookStoreAppService, IAuthorAppService
+{
+ private readonly IAuthorRepository _authorRepository;
+ private readonly AuthorManager _authorManager;
- //...SERVICE METHODS WILL COME HERE...
+ public AuthorAppService(
+ IAuthorRepository authorRepository,
+ AuthorManager authorManager)
+ {
+ _authorRepository = authorRepository;
+ _authorManager = authorManager;
}
+
+ //...SERVICE METHODS WILL COME HERE...
}
````
@@ -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:
````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 const string Default = GroupName + ".Books";
- public const string Create = Default + ".Create";
- public const string Edit = Default + ".Edit";
- public const string Delete = Default + ".Delete";
- }
-
- // *** ADDED a NEW NESTED CLASS ***
- public static class Authors
- {
- public const string Default = GroupName + ".Authors";
- public const string Create = Default + ".Create";
- public const string Edit = Default + ".Edit";
- public const string Delete = Default + ".Delete";
- }
+ public static class Books
+ {
+ public const string Default = GroupName + ".Books";
+ public const string Create = Default + ".Create";
+ public const string Edit = Default + ".Edit";
+ public const string Delete = Default + ".Delete";
+ }
+
+ // *** ADDED a NEW NESTED CLASS ***
+ public static class Authors
+ {
+ public const string Default = GroupName + ".Authors";
+ public const string Create = Default + ".Create";
+ public const string Edit = Default + ".Edit";
+ public const string Delete = Default + ".Delete";
}
}
````
@@ -400,72 +393,71 @@ using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
-namespace Acme.BookStore
+namespace Acme.BookStore;
+
+public class BookStoreDataSeederContributor
+ : IDataSeedContributor, ITransientDependency
{
- public class BookStoreDataSeederContributor
- : IDataSeedContributor, ITransientDependency
+ private readonly IRepository _bookRepository;
+ private readonly IAuthorRepository _authorRepository;
+ private readonly AuthorManager _authorManager;
+
+ public BookStoreDataSeederContributor(
+ IRepository bookRepository,
+ IAuthorRepository authorRepository,
+ AuthorManager authorManager)
{
- private readonly IRepository _bookRepository;
- private readonly IAuthorRepository _authorRepository;
- private readonly AuthorManager _authorManager;
-
- public BookStoreDataSeederContributor(
- IRepository bookRepository,
- IAuthorRepository authorRepository,
- AuthorManager authorManager)
+ _bookRepository = bookRepository;
+ _authorRepository = authorRepository;
+ _authorManager = authorManager;
+ }
+
+ public async Task SeedAsync(DataSeedContext context)
+ {
+ if (await _bookRepository.GetCountAsync() <= 0)
{
- _bookRepository = bookRepository;
- _authorRepository = authorRepository;
- _authorManager = authorManager;
+ 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
+ );
}
- public async Task SeedAsync(DataSeedContext context)
- {
- 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
- // 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(
- "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)."
- )
- );
-
- 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 _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;
using Shouldly;
using Xunit;
-namespace Acme.BookStore.Authors
-{ {{if DB=="Mongo"}}
- [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
- public class AuthorAppService_Tests : BookStoreApplicationTestBase
+namespace Acme.BookStore.Authors;
+
+ {{if DB=="Mongo"}}
+[Collection(BookStoreTestConsts.CollectionDefinitionName)]
+{{end}}
+public class AuthorAppService_Tests : BookStoreApplicationTestBase
+{
+ private readonly IAuthorAppService _authorAppService;
+
+ public AuthorAppService_Tests()
{
- private readonly IAuthorAppService _authorAppService;
+ _authorAppService = GetRequiredService();
+ }
- public AuthorAppService_Tests()
- {
- _authorAppService = GetRequiredService();
- }
+ [Fact]
+ public async Task Should_Get_All_Authors_Without_Any_Filter()
+ {
+ var result = await _authorAppService.GetListAsync(new GetAuthorListDto());
- [Fact]
- public async Task Should_Get_All_Authors_Without_Any_Filter()
- {
- var result = await _authorAppService.GetListAsync(new GetAuthorListDto());
+ result.TotalCount.ShouldBeGreaterThanOrEqualTo(2);
+ result.Items.ShouldContain(author => author.Name == "George Orwell");
+ result.Items.ShouldContain(author => author.Name == "Douglas Adams");
+ }
- result.TotalCount.ShouldBeGreaterThanOrEqualTo(2);
- result.Items.ShouldContain(author => author.Name == "George Orwell");
- result.Items.ShouldContain(author => author.Name == "Douglas Adams");
- }
+ [Fact]
+ public async Task Should_Get_Filtered_Authors()
+ {
+ var result = await _authorAppService.GetListAsync(
+ new GetAuthorListDto {Filter = "George"});
- [Fact]
- public async Task Should_Get_Filtered_Authors()
- {
- var result = await _authorAppService.GetListAsync(
- new GetAuthorListDto {Filter = "George"});
+ result.TotalCount.ShouldBeGreaterThanOrEqualTo(1);
+ result.Items.ShouldContain(author => author.Name == "George Orwell");
+ result.Items.ShouldNotContain(author => author.Name == "Douglas Adams");
+ }
- result.TotalCount.ShouldBeGreaterThanOrEqualTo(1);
- result.Items.ShouldContain(author => author.Name == "George Orwell");
- result.Items.ShouldNotContain(author => author.Name == "Douglas Adams");
- }
+ [Fact]
+ public async Task Should_Create_A_New_Author()
+ {
+ var authorDto = await _authorAppService.CreateAsync(
+ new CreateAuthorDto
+ {
+ Name = "Edward Bellamy",
+ BirthDate = new DateTime(1850, 05, 22),
+ ShortBio = "Edward Bellamy was an American author..."
+ }
+ );
+
+ authorDto.Id.ShouldNotBe(Guid.Empty);
+ authorDto.Name.ShouldBe("Edward Bellamy");
+ }
- [Fact]
- public async Task Should_Create_A_New_Author()
+ [Fact]
+ public async Task Should_Not_Allow_To_Create_Duplicate_Author()
+ {
+ await Assert.ThrowsAsync(async () =>
{
- var authorDto = await _authorAppService.CreateAsync(
+ await _authorAppService.CreateAsync(
new CreateAuthorDto
{
- Name = "Edward Bellamy",
- BirthDate = new DateTime(1850, 05, 22),
- ShortBio = "Edward Bellamy was an American author..."
+ Name = "Douglas Adams",
+ BirthDate = DateTime.Now,
+ ShortBio = "..."
}
);
-
- authorDto.Id.ShouldNotBe(Guid.Empty);
- authorDto.Name.ShouldBe("Edward Bellamy");
- }
-
- [Fact]
- public async Task Should_Not_Allow_To_Create_Duplicate_Author()
- {
- await Assert.ThrowsAsync(async () =>
- {
- await _authorAppService.CreateAsync(
- new CreateAuthorDto
- {
- Name = "Douglas Adams",
- BirthDate = DateTime.Now,
- ShortBio = "..."
- }
- );
- });
- }
-
- //TODO: Test other methods...
+ });
}
+
+ //TODO: Test other methods...
}
````
diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md
index e45602fd5f..4e27655d64 100644
--- a/docs/en/Tutorials/Part-9.md
+++ b/docs/en/Tutorials/Part-9.md
@@ -95,14 +95,13 @@ This is a simple page similar to the Books page we had created before. It import
````csharp
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**:
-
+
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**:
-
+
The page is fully working except *New author* and *Actions/Edit* since we haven't implemented them yet.
@@ -302,45 +301,44 @@ using Acme.BookStore.Authors;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
-namespace Acme.BookStore.Web.Pages.Authors
+namespace Acme.BookStore.Web.Pages.Authors;
+
+public class CreateModalModel : BookStorePageModel
{
- public class CreateModalModel : BookStorePageModel
- {
- [BindProperty]
- public CreateAuthorViewModel Author { get; set; }
+ [BindProperty]
+ public CreateAuthorViewModel Author { get; set; }
- private readonly IAuthorAppService _authorAppService;
+ private readonly IAuthorAppService _authorAppService;
- public CreateModalModel(IAuthorAppService authorAppService)
- {
- _authorAppService = authorAppService;
- }
+ public CreateModalModel(IAuthorAppService authorAppService)
+ {
+ _authorAppService = authorAppService;
+ }
- public void OnGet()
- {
- Author = new CreateAuthorViewModel();
- }
+ public void OnGet()
+ {
+ Author = new CreateAuthorViewModel();
+ }
- public async Task OnPostAsync()
- {
- var dto = ObjectMapper.Map(Author);
- await _authorAppService.CreateAsync(dto);
- return NoContent();
- }
+ public async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(Author);
+ await _authorAppService.CreateAsync(dto);
+ return NoContent();
+ }
- public class CreateAuthorViewModel
- {
- [Required]
- [StringLength(AuthorConsts.MaxNameLength)]
- public string Name { get; set; }
+ public class CreateAuthorViewModel
+ {
+ [Required]
+ [StringLength(AuthorConsts.MaxNameLength)]
+ public string Name { get; set; }
- [Required]
- [DataType(DataType.Date)]
- public DateTime BirthDate { get; set; }
+ [Required]
+ [DataType(DataType.Date)]
+ public DateTime BirthDate { get; set; }
- [TextArea]
- public string ShortBio { get; set; }
- }
+ [TextArea]
+ public string ShortBio { get; set; }
}
}
```
@@ -359,18 +357,17 @@ using Acme.BookStore.Authors; // ADDED NAMESPACE IMPORT
using Acme.BookStore.Books;
using AutoMapper;
-namespace Acme.BookStore.Web
+namespace Acme.BookStore.Web;
+
+public class BookStoreWebAutoMapperProfile : Profile
{
- public class BookStoreWebAutoMapperProfile : Profile
+ public BookStoreWebAutoMapperProfile()
{
- public BookStoreWebAutoMapperProfile()
- {
- CreateMap();
+ CreateMap();
- // ADD a NEW MAPPING
- CreateMap();
- }
+ // ADD a NEW MAPPING
+ CreateMap();
}
}
````
@@ -420,52 +417,51 @@ using Acme.BookStore.Authors;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
-namespace Acme.BookStore.Web.Pages.Authors
+namespace Acme.BookStore.Web.Pages.Authors;
+
+public class EditModalModel : BookStorePageModel
{
- public class EditModalModel : BookStorePageModel
- {
- [BindProperty]
- public EditAuthorViewModel Author { get; set; }
+ [BindProperty]
+ public EditAuthorViewModel Author { get; set; }
- private readonly IAuthorAppService _authorAppService;
+ private readonly IAuthorAppService _authorAppService;
- public EditModalModel(IAuthorAppService authorAppService)
- {
- _authorAppService = authorAppService;
- }
+ public EditModalModel(IAuthorAppService authorAppService)
+ {
+ _authorAppService = authorAppService;
+ }
- public async Task OnGetAsync(Guid id)
- {
- var authorDto = await _authorAppService.GetAsync(id);
- Author = ObjectMapper.Map(authorDto);
- }
+ public async Task OnGetAsync(Guid id)
+ {
+ var authorDto = await _authorAppService.GetAsync(id);
+ Author = ObjectMapper.Map(authorDto);
+ }
- public async Task OnPostAsync()
- {
- await _authorAppService.UpdateAsync(
- Author.Id,
- ObjectMapper.Map(Author)
- );
+ public async Task OnPostAsync()
+ {
+ await _authorAppService.UpdateAsync(
+ Author.Id,
+ ObjectMapper.Map(Author)
+ );
- return NoContent();
- }
+ return NoContent();
+ }
- public class EditAuthorViewModel
- {
- [HiddenInput]
- public Guid Id { get; set; }
+ public class EditAuthorViewModel
+ {
+ [HiddenInput]
+ public Guid Id { get; set; }
- [Required]
- [StringLength(AuthorConsts.MaxNameLength)]
- public string Name { get; set; }
+ [Required]
+ [StringLength(AuthorConsts.MaxNameLength)]
+ public string Name { get; set; }
- [Required]
- [DataType(DataType.Date)]
- public DateTime BirthDate { get; set; }
+ [Required]
+ [DataType(DataType.Date)]
+ public DateTime BirthDate { get; set; }
- [TextArea]
- public string ShortBio { get; set; }
- }
+ [TextArea]
+ public string ShortBio { get; set; }
}
}
```
@@ -482,22 +478,21 @@ using Acme.BookStore.Authors;
using Acme.BookStore.Books;
using AutoMapper;
-namespace Acme.BookStore.Web
+namespace Acme.BookStore.Web;
+
+public class BookStoreWebAutoMapperProfile : Profile
{
- public class BookStoreWebAutoMapperProfile : Profile
+ public BookStoreWebAutoMapperProfile()
{
- public BookStoreWebAutoMapperProfile()
- {
- CreateMap();
+ CreateMap();
- CreateMap();
+ CreateMap();
- // ADD THESE NEW MAPPINGS
- CreateMap();
- CreateMap();
- }
+ // ADD THESE NEW MAPPINGS
+ CreateMap();
+ CreateMap();
}
}
```
@@ -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**:
-
+
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**:
-
+
That's all! This is a fully working CRUD page, you can create, edit and delete authors.
@@ -863,12 +858,11 @@ Create a new Razor Component Page, `/Pages/Authors.razor`, in the `Acme.BookStor
@inject AbpBlazorMessageLocalizerHelper LH
-
-
+
+
@L["Authors"]
-
-
+
@if (CanCreateAuthor)
{
@@ -1054,142 +1047,141 @@ using Blazorise.DataGrid;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
-namespace Acme.BookStore.Blazor.Pages
+namespace Acme.BookStore.Blazor.Pages;
+
+public partial class Authors
{
- public partial class Authors
- {
- private IReadOnlyList AuthorList { get; set; }
+ private IReadOnlyList AuthorList { get; set; }
- private int PageSize { get; } = LimitedResultRequestDto.DefaultMaxResultCount;
- private int CurrentPage { get; set; }
- private string CurrentSorting { get; set; }
- private int TotalCount { get; set; }
+ private int PageSize { get; } = LimitedResultRequestDto.DefaultMaxResultCount;
+ private int CurrentPage { get; set; }
+ private string CurrentSorting { get; set; }
+ private int TotalCount { get; set; }
- private bool CanCreateAuthor { get; set; }
- private bool CanEditAuthor { get; set; }
- private bool CanDeleteAuthor { get; set; }
+ private bool CanCreateAuthor { get; set; }
+ private bool CanEditAuthor { get; set; }
+ private bool CanDeleteAuthor { get; set; }
- private CreateAuthorDto NewAuthor { get; set; }
+ private CreateAuthorDto NewAuthor { get; set; }
- private Guid EditingAuthorId { get; set; }
- private UpdateAuthorDto EditingAuthor { get; set; }
+ private Guid EditingAuthorId { get; set; }
+ private UpdateAuthorDto EditingAuthor { get; set; }
- private Modal CreateAuthorModal { get; set; }
- private Modal EditAuthorModal { get; set; }
+ private Modal CreateAuthorModal { get; set; }
+ private Modal EditAuthorModal { get; set; }
- private Validations CreateValidationsRef;
-
- private Validations EditValidationsRef;
-
- public Authors()
- {
- NewAuthor = new CreateAuthorDto();
- EditingAuthor = new UpdateAuthorDto();
- }
+ private Validations CreateValidationsRef;
+
+ private Validations EditValidationsRef;
+
+ public Authors()
+ {
+ NewAuthor = new CreateAuthorDto();
+ EditingAuthor = new UpdateAuthorDto();
+ }
- protected override async Task OnInitializedAsync()
- {
- await SetPermissionsAsync();
- await GetAuthorsAsync();
- }
+ protected override async Task OnInitializedAsync()
+ {
+ await SetPermissionsAsync();
+ await GetAuthorsAsync();
+ }
- private async Task SetPermissionsAsync()
- {
- CanCreateAuthor = await AuthorizationService
- .IsGrantedAsync(BookStorePermissions.Authors.Create);
+ private async Task SetPermissionsAsync()
+ {
+ CanCreateAuthor = await AuthorizationService
+ .IsGrantedAsync(BookStorePermissions.Authors.Create);
- CanEditAuthor = await AuthorizationService
- .IsGrantedAsync(BookStorePermissions.Authors.Edit);
+ CanEditAuthor = await AuthorizationService
+ .IsGrantedAsync(BookStorePermissions.Authors.Edit);
- CanDeleteAuthor = await AuthorizationService
- .IsGrantedAsync(BookStorePermissions.Authors.Delete);
- }
+ CanDeleteAuthor = await AuthorizationService
+ .IsGrantedAsync(BookStorePermissions.Authors.Delete);
+ }
- private async Task GetAuthorsAsync()
- {
- var result = await AuthorAppService.GetListAsync(
- new GetAuthorListDto
- {
- MaxResultCount = PageSize,
- SkipCount = CurrentPage * PageSize,
- Sorting = CurrentSorting
- }
- );
+ private async Task GetAuthorsAsync()
+ {
+ var result = await AuthorAppService.GetListAsync(
+ new GetAuthorListDto
+ {
+ MaxResultCount = PageSize,
+ SkipCount = CurrentPage * PageSize,
+ Sorting = CurrentSorting
+ }
+ );
- AuthorList = result.Items;
- TotalCount = (int)result.TotalCount;
- }
+ AuthorList = result.Items;
+ TotalCount = (int)result.TotalCount;
+ }
- private async Task OnDataGridReadAsync(DataGridReadDataEventArgs e)
- {
- CurrentSorting = e.Columns
- .Where(c => c.SortDirection != SortDirection.Default)
- .Select(c => c.Field + (c.SortDirection == SortDirection.Descending ? " DESC" : ""))
- .JoinAsString(",");
- CurrentPage = e.Page - 1;
+ private async Task OnDataGridReadAsync(DataGridReadDataEventArgs e)
+ {
+ CurrentSorting = e.Columns
+ .Where(c => c.SortDirection != SortDirection.Default)
+ .Select(c => c.Field + (c.SortDirection == SortDirection.Descending ? " DESC" : ""))
+ .JoinAsString(",");
+ CurrentPage = e.Page - 1;
- await GetAuthorsAsync();
+ await GetAuthorsAsync();
- await InvokeAsync(StateHasChanged);
- }
+ await InvokeAsync(StateHasChanged);
+ }
- private void OpenCreateAuthorModal()
- {
- CreateValidationsRef.ClearAll();
-
- NewAuthor = new CreateAuthorDto();
- CreateAuthorModal.Show();
- }
+ private void OpenCreateAuthorModal()
+ {
+ CreateValidationsRef.ClearAll();
+
+ NewAuthor = new CreateAuthorDto();
+ CreateAuthorModal.Show();
+ }
- private void CloseCreateAuthorModal()
- {
- CreateAuthorModal.Hide();
- }
+ private void CloseCreateAuthorModal()
+ {
+ CreateAuthorModal.Hide();
+ }
- private void OpenEditAuthorModal(AuthorDto author)
- {
- EditValidationsRef.ClearAll();
-
- EditingAuthorId = author.Id;
- EditingAuthor = ObjectMapper.Map(author);
- EditAuthorModal.Show();
- }
+ private void OpenEditAuthorModal(AuthorDto author)
+ {
+ EditValidationsRef.ClearAll();
+
+ EditingAuthorId = author.Id;
+ EditingAuthor = ObjectMapper.Map(author);
+ EditAuthorModal.Show();
+ }
- private async Task DeleteAuthorAsync(AuthorDto author)
+ private async Task DeleteAuthorAsync(AuthorDto author)
+ {
+ var confirmMessage = L["AuthorDeletionConfirmationMessage", author.Name];
+ if (!await Message.Confirm(confirmMessage))
{
- var confirmMessage = L["AuthorDeletionConfirmationMessage", author.Name];
- if (!await Message.Confirm(confirmMessage))
- {
- return;
- }
-
- await AuthorAppService.DeleteAsync(author.Id);
- await GetAuthorsAsync();
+ return;
}
- private void CloseEditAuthorModal()
- {
- EditAuthorModal.Hide();
- }
+ await AuthorAppService.DeleteAsync(author.Id);
+ await GetAuthorsAsync();
+ }
+
+ private void CloseEditAuthorModal()
+ {
+ EditAuthorModal.Hide();
+ }
- private async Task CreateAuthorAsync()
+ private async Task CreateAuthorAsync()
+ {
+ if (await CreateValidationsRef.ValidateAll())
{
- if (await CreateValidationsRef.ValidateAll())
- {
- await AuthorAppService.CreateAsync(NewAuthor);
- await GetAuthorsAsync();
- CreateAuthorModal.Hide();
- }
+ await AuthorAppService.CreateAsync(NewAuthor);
+ await GetAuthorsAsync();
+ CreateAuthorModal.Hide();
}
+ }
- private async Task UpdateAuthorAsync()
+ private async Task UpdateAuthorAsync()
+ {
+ if (await EditValidationsRef.ValidateAll())
{
- if (await EditValidationsRef.ValidateAll())
- {
- await AuthorAppService.UpdateAsync(EditingAuthorId, EditingAuthor);
- await GetAuthorsAsync();
- EditAuthorModal.Hide();
- }
+ await AuthorAppService.UpdateAsync(EditingAuthorId, EditingAuthor);
+ await GetAuthorsAsync();
+ EditAuthorModal.Hide();
}
}
}
@@ -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**:
-
+
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**:
-
+
That's all! This is a fully working CRUD page, you can create, edit and delete the authors.
diff --git a/docs/en/Tutorials/images/blazor-add-book-button-2.png b/docs/en/Tutorials/images/blazor-add-book-button-2.png
new file mode 100644
index 0000000000..e6760b7405
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-add-book-button-2.png differ
diff --git a/docs/en/Tutorials/images/blazor-bookstore-book-list-2.png b/docs/en/Tutorials/images/blazor-bookstore-book-list-2.png
new file mode 100644
index 0000000000..2dda8471dc
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-bookstore-book-list-2.png differ
diff --git a/docs/en/Tutorials/images/blazor-bookstore-book-list-with-authors-2.png b/docs/en/Tutorials/images/blazor-bookstore-book-list-with-authors-2.png
new file mode 100644
index 0000000000..3bc399ca17
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-bookstore-book-list-with-authors-2.png differ
diff --git a/docs/en/Tutorials/images/blazor-delete-book-action-2.png b/docs/en/Tutorials/images/blazor-delete-book-action-2.png
new file mode 100644
index 0000000000..5e1e144efa
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-delete-book-action-2.png differ
diff --git a/docs/en/Tutorials/images/blazor-edit-book-action-3.png b/docs/en/Tutorials/images/blazor-edit-book-action-3.png
new file mode 100644
index 0000000000..7052be4a33
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-edit-book-action-3.png differ
diff --git a/docs/en/Tutorials/images/blazor-edit-book-modal-2.png b/docs/en/Tutorials/images/blazor-edit-book-modal-2.png
new file mode 100644
index 0000000000..863c283254
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-edit-book-modal-2.png differ
diff --git a/docs/en/Tutorials/images/blazor-new-book-modal-2.png b/docs/en/Tutorials/images/blazor-new-book-modal-2.png
new file mode 100644
index 0000000000..31cd576e3a
Binary files /dev/null and b/docs/en/Tutorials/images/blazor-new-book-modal-2.png differ
diff --git a/docs/en/Tutorials/images/book-create-modal-with-author-2.png b/docs/en/Tutorials/images/book-create-modal-with-author-2.png
new file mode 100644
index 0000000000..cebba7f048
Binary files /dev/null and b/docs/en/Tutorials/images/book-create-modal-with-author-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-actions-buttons-2.png b/docs/en/Tutorials/images/bookstore-actions-buttons-2.png
new file mode 100644
index 0000000000..a67dabe995
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-actions-buttons-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-angular-author-selection-2.png b/docs/en/Tutorials/images/bookstore-angular-author-selection-2.png
new file mode 100644
index 0000000000..b8fa40f224
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-angular-author-selection-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-angular-authors-page-2.png b/docs/en/Tutorials/images/bookstore-angular-authors-page-2.png
new file mode 100644
index 0000000000..7c6f9ce888
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-angular-authors-page-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-author-permissions-3.png b/docs/en/Tutorials/images/bookstore-author-permissions-3.png
new file mode 100644
index 0000000000..6b53bea4ed
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-author-permissions-3.png differ
diff --git a/docs/en/Tutorials/images/bookstore-authors-page-3.png b/docs/en/Tutorials/images/bookstore-authors-page-3.png
new file mode 100644
index 0000000000..3db547f7f9
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-authors-page-3.png differ
diff --git a/docs/en/Tutorials/images/bookstore-book-list-angular.png b/docs/en/Tutorials/images/bookstore-book-list-angular.png
new file mode 100644
index 0000000000..d308e1ca95
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-book-list-angular.png differ
diff --git a/docs/en/Tutorials/images/bookstore-books-with-authorname-angular-2.png b/docs/en/Tutorials/images/bookstore-books-with-authorname-angular-2.png
new file mode 100644
index 0000000000..324568ad32
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-books-with-authorname-angular-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-confirmation-popup-2.png b/docs/en/Tutorials/images/bookstore-confirmation-popup-2.png
new file mode 100644
index 0000000000..f6156d7952
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-confirmation-popup-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-empty-new-book-modal-2.png b/docs/en/Tutorials/images/bookstore-empty-new-book-modal-2.png
new file mode 100644
index 0000000000..d04819407d
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-empty-new-book-modal-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-final-actions-dropdown-2.png b/docs/en/Tutorials/images/bookstore-final-actions-dropdown-2.png
new file mode 100644
index 0000000000..0631e3aeab
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-final-actions-dropdown-2.png differ
diff --git a/docs/en/Tutorials/images/bookstore-new-book-form-v3.png b/docs/en/Tutorials/images/bookstore-new-book-form-v3.png
new file mode 100644
index 0000000000..cb462cbc0d
Binary files /dev/null and b/docs/en/Tutorials/images/bookstore-new-book-form-v3.png differ
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
index c2016a4ba2..dd134c64fd 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
@@ -507,7 +507,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase