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: -![bookstore-books-with-authorname-angular](images/bookstore-books-with-authorname-angular.png) +![bookstore-books-with-authorname-angular](images/bookstore-books-with-authorname-angular-2.png) ### 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: -![bookstore-angular-author-selection](images/bookstore-angular-author-selection.png) +![bookstore-angular-author-selection](images/bookstore-angular-author-selection-2.png) 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: -![blazor-bookstore-book-list-with-authors](images/blazor-bookstore-book-list-with-authors.png) +![blazor-bookstore-book-list-with-authors](images/blazor-bookstore-book-list-with-authors-2.png) ### 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: -![book-create-modal-with-author](images/book-create-modal-with-author.png) +![book-create-modal-with-author](images/book-create-modal-with-author-2.png) ### 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: -![Book list final result](images/bookstore-book-list.png) +![Book list final result](images/bookstore-book-list-angular.png) {{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: -![blazor-menu-bookstore](images/blazor-menu-bookstore.png) +![blazor-menu-bookstore](images/bookstore-new-menu-item-2.png) 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. -![Empty modal for new book](images/bookstore-empty-new-book-modal.png) +![Empty modal for new book](images/bookstore-empty-new-book-modal-2.png) ### Create a Reactive Form @@ -926,7 +924,7 @@ export class BookComponent implements OnInit { Now, you can open your browser to see the changes: -![Save button to the modal](images/bookstore-new-book-form-v2.png) +![Save button to the modal](images/bookstore-new-book-form-v3.png) ## 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: -![Action buttons](images/bookstore-actions-buttons.png) +![Action buttons](images/bookstore-actions-buttons-2.png) 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: -![bookstore-final-actions-dropdown](images/bookstore-final-actions-dropdown.png) +![bookstore-final-actions-dropdown](images/bookstore-final-actions-dropdown-2.png) Clicking the "Delete" action calls the `delete` method which then shows a confirmation popup as shown below: -![bookstore-confirmation-popup](images/bookstore-confirmation-popup.png) +![bookstore-confirmation-popup](images/bookstore-confirmation-popup-2.png) {{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: -![blazor-add-book-button](images/blazor-add-book-button.png) +![blazor-add-book-button](images/blazor-add-book-button-2.png) 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: -![blazor-new-book-modal](images/blazor-new-book-modal.png) +![blazor-new-book-modal](images/blazor-new-book-modal-2.png) ## 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: -![blazor-edit-book-action](images/blazor-edit-book-action-2.png) +![blazor-edit-book-action](images/blazor-edit-book-action-3.png) ### 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. -![blazor-edit-book-modal](images/blazor-edit-book-modal.png) +![blazor-edit-book-modal](images/blazor-edit-book-modal-2.png) > 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: -![blazor-edit-book-action](images/blazor-delete-book-action.png) +![blazor-delete-book-action](images/blazor-delete-book-action-2.png) 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**: -![bookstore-author-permissions](images/bookstore-author-permissions-2.png) +![bookstore-author-permissions](images/bookstore-author-permissions-3.png) 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**: -![bookstore-authors-page](images/bookstore-authors-page-2.png) +![bookstore-authors-page](images/bookstore-authors-page-3.png) 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**: -![bookstore-author-permissions](images/bookstore-author-permissions.png) +![bookstore-author-permissions](images/bookstore-author-permissions-2.png) 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**: -![bookstore-authors-page](images/bookstore-angular-authors-page.png) +![bookstore-authors-page](images/bookstore-angular-authors-page-2.png) 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) {