Browse Source

Revised book store

pull/329/head
Halil ibrahim Kalkan 8 years ago
parent
commit
7c37348c08
  1. 98
      docs/Tutorials/AspNetCore-Mvc/Part-I.md
  2. 71
      docs/Tutorials/AspNetCore-Mvc/Part-II.md
  3. BIN
      docs/images/bookstore-book-list.png
  4. BIN
      docs/images/bookstore-books-table.png
  5. BIN
      docs/images/bookstore-new-book-button.png
  6. 8
      samples/BookStore/src/Acme.BookStore.Application/BookAppService.cs
  7. 7
      samples/BookStore/src/Acme.BookStore.Application/BookDto.cs
  8. 6
      samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs
  9. 12
      samples/BookStore/src/Acme.BookStore.Application/BookStoreWebAutoMapperProfile.cs
  10. 24
      samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs
  11. 9
      samples/BookStore/src/Acme.BookStore.Application/IBookAppService.cs
  12. 1
      samples/BookStore/src/Acme.BookStore.Domain/Acme.BookStore.Domain.csproj
  13. 4
      samples/BookStore/src/Acme.BookStore.Domain/Book.cs
  14. 1
      samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json
  15. 1
      samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json
  16. 4
      samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Acme.BookStore.EntityFrameworkCore.csproj
  17. 9
      samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195241_Initial.Designer.cs
  18. 1
      samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195241_Initial.cs
  19. 10
      samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195424_Created_Book_Entity.Designer.cs
  20. 4
      samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195424_Created_Book_Entity.cs
  21. 8
      samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/BookStoreDbContextModelSnapshot.cs
  22. 7
      samples/BookStore/src/Acme.BookStore.Web/BookStoreWebAutoMapperProfile.cs
  23. 27
      samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs
  24. 1
      samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml
  25. 8
      samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/books/index.js

98
docs/Tutorials/AspNetCore-Mvc/Part-I.md

@ -30,12 +30,12 @@ Define [entities](../../Entities.md) in the **domain layer** (`Acme.BookStore.Do
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
[Table("Books")]
public class Book : AggregateRoot<Guid>
public class Book : AuditedAggregateRoot<Guid>
{
[Required]
[StringLength(128)]
@ -50,8 +50,10 @@ namespace Acme.BookStore
}
````
* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Roots** are one of the concepts of the **Domain Driven Design (DDD)**. See [entity document](../../Entities.md) for details and best practices.
* Used **data annotation attributes** in this code. You could use EF Core's [fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling) instead.
* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for details and best practices.
* `Book` entity inherits `AuditedAggregateRoot` which adds some auditing properties (`CreationTime`, `CreatorId`, `LastModificationTime`... etc.) on top of the `AggregateRoot` class.
* `Guid` is the **primary key type** of the `Book` entity.
* Used **data annotation attributes** in this code for EF Core mappings. You could use EF Core's [fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling) instead.
#### BookType Enum
@ -117,15 +119,14 @@ Create a DTO class named `BookDto` into the `Acme.BookStore.Application` project
````C#
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
public class BookDto : EntityDto<Guid>
[AutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
[Required]
[StringLength(128)]
public string Name { get; set; }
public BookType Type { get; set; }
@ -138,6 +139,45 @@ namespace Acme.BookStore
````
* **DTO** classes are used to transfer data between the presentation layer and the application layer. See the [Data Transfer Objects document](../../Data-Transfer-Objects.md) for details.
* `BookDto` is used to transfer a book data to the presentation layer to show a book information on the UI.
* `BookDto` is derived from the `AuditedEntityDto<Guid>` which has audit properties just like the `Book` defined above.
* `[AutoMapFrom(typeof(Book))]` is used to create AutoMapper mapping from the `Book` class to the `BookDto` class. Thus, you can automatically convert `Book` objects to `BookDto` objects (instead of manually copy all properties).
#### CreateUpdateDto
Create a DTO class named `CreateUpdateDto` into the `Acme.BookStore.Application` project:
````c#
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
[AutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Type")]
public BookType Type { get; set; } = BookType.Undefined;
[Display(Name = "PublishDate")]
public DateTime PublishDate { get; set; }
[Display(Name = "Price")]
public float Price { get; set; }
}
}
````
* This DTO class is used to get book information from the user interface.
* Each property has a `[Display]` property which sets the label on the form for the related input. It's also integrated to the localization system.
* It defines data annotation attributes (like `[Required]`) to define validations for the properties.
* Each property has a `[Display]` property which sets the label on UI forms for the related inputs. It's also integrated to the localization system. The same DTO will be used as View Model. That's why it defines that attribute. You may find incorrect to use DTOs as View Models. You could use a separated view model class, but we thought it's practical and makes the sample project less complex.
#### IBookAppService
@ -145,20 +185,28 @@ First, define an interface named `IBookAppService` for the book application serv
````C#
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService : IAsyncCrudAppService<BookDto, Guid>
public interface IBookAppService :
IAsyncCrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
CreateUpdateBookDto, //Used to create a new book
CreateUpdateBookDto> //Used to update a book
{
}
}
````
* Defining interfaces for application services is <u>not required</u> by the framework. However, it's suggested as a good practice.
* `IAsyncCrudAppService` defines common CRUD methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead you could inherit from the empty `IApplicationService` interface and define your own methods.
* There are some variations of the `IAsyncCrudAppService`. In this sample, the first generic parameter, `BookDto`, is the DTO used for service methods and the second parameter, `Guid`, is the type of the primary key of the entity.
* There are some variations of the `IAsyncCrudAppService` where you can use a single DTO or separated DTOs for each method.
#### BookAppService
@ -166,12 +214,16 @@ Implement the `IBookAppService` as named `BookAppService`:
````C#
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookAppService : AsyncCrudAppService<Book, BookDto, Guid>, IBookAppService
public class BookAppService :
AsyncCrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
@ -182,9 +234,9 @@ namespace Acme.BookStore
}
````
* `BookAppService` is derived from `AsyncCrudAppService<Book, BookDto, Guid>` which implements all CRUD methods defined above.
* `BookAppService` injects `IRepository<Book, Guid>` which is the default repository created for the `Book` entity. See the [repository document](../../Repositories.md).
* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and vice verse. Startup template uses the [AutoMapper](http://automapper.org/) library as the mapping provider. Since you haven't defined any mapping configuration, AutoMapper's [inline mapping](http://automapper.readthedocs.io/en/latest/Inline-Mapping.html) feature is used to automatically configure the mapping. This works fine if both classes have identical properties, but may cause to problems if they not. See the [AutoMapper integration document](../../AutoMapper-Integration.md) for details.
* `BookAppService` is derived from `AsyncCrudAppService<...>` which implements all CRUD methods defined above.
* `BookAppService` injects `IRepository<Book, Guid>` which is the default repository created for the `Book` entity. ABP automatically creates repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md).
* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. Startup template uses the [AutoMapper](http://automapper.org/) library as the mapping provider. You defined mappings using the `AutoMapFrom` and `AutoMapTo` attributes above. See the [AutoMapper integration document](../../AutoMapper-Integration.md) for details.
### Auto API Controllers
@ -229,14 +281,7 @@ You can see the **book list** returned from the server.
Let's **create a new book** using the `create` function:
````js
acme.bookStore.book.create({
name: 'Foundation',
type: 7,
publishDate: '1951-05-24',
price: 21.5
}).done(function (result) {
console.log('successfully created the book with id: ' + result.id);
});
acme.bookStore.book.create({ name: 'Foundation', type: 7, publishDate: '1951-05-24', price: 21.5 }).done(function (result) { console.log('successfully created the book with id: ' + result.id); });
````
You should see a message in the console something like that:
@ -336,6 +381,7 @@ Change the `Pages/Books/Index.cshtml` as following:
<th>@L["Type"]</th>
<th>@L["PublishDate"]</th>
<th>@L["Price"]</th>
<th>@L["CreationTime"]</th>
</tr>
</thead>
</abp-table>
@ -370,11 +416,15 @@ $(function() {
},
{
targets: 2,
data: "price"
data: "publishDate"
},
{
targets: 3,
data: "publishDate"
data: "price"
},
{
targets: 4,
data: "creationTime"
}
]
});

71
docs/Tutorials/AspNetCore-Mvc/Part-II.md

@ -29,8 +29,6 @@ Create a new razor page, named `CreateModal.cshtml` under the `Pages/Books` fold
Open the `CreateModal.cshtml.cs` file (`CreateModalModel` class) and replace with the following code:
````C#
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
@ -40,7 +38,7 @@ namespace Acme.BookStore.Pages.Books
public class CreateModalModel : AbpPageModel
{
[BindProperty]
public CreateBookViewModel Book { get; set; }
public CreateUpdateBookDto Book { get; set; }
private readonly IBookAppService _bookAppService;
@ -51,75 +49,15 @@ namespace Acme.BookStore.Pages.Books
public async Task<IActionResult> OnPostAsync()
{
ValidateModel();
var bookDto = ObjectMapper.Map<CreateBookViewModel, BookDto>(Book);
await _bookAppService.CreateAsync(bookDto);
await _bookAppService.CreateAsync(Book);
return NoContent();
}
public class CreateBookViewModel
{
[Required]
[StringLength(128)]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Type")]
public BookType Type { get; set; } = BookType.Undefined;
[Display(Name = "PublishDate")]
public DateTime PublishDate { get; set; }
[Display(Name = "Price")]
public float Price { get; set; }
}
}
}
````
* `CreateBookViewModel` is a nested class that will be used to create and post the form.
* Each property has a `[Display]` property which sets the label on the form for the related input. It's also integrated to the localization system.
* Each property has data annotations for validation which is used for validation in the client side and the server side and automatically localized.
* `[BindProperty]` attribute on the `Book` property binds post request data to this property.
##### AutoMapper Configuration
`OnPostAsync` method maps `CreateBookViewModel` object to `BookDto` object (which is accepted by the `BookAppService.CreateAsync` method).
Open the `BookStoreWebAutoMapperProfile` class and add the mapping:
````C#
using Acme.BookStore.Pages.Books;
using AutoMapper;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
public class BookStoreWebAutoMapperProfile : Profile
{
public BookStoreWebAutoMapperProfile()
{
CreateMap<CreateModalModel.CreateBookViewModel, BookDto>()
.Ignore(x => x.Id);
}
}
}
````
Thus, AutoMapper will create the mapping configuration between classes and will ignore the `Id` property of the `BookDto` class (to satisfy the configuration validation - see below).
##### AutoMapper Configuration Validation
AutoMapper has a [configuration validation feature](http://automapper.readthedocs.io/en/latest/Configuration-validation.html) which is not enabled for the startup template by default. If you want to perform validation, go to the `BookStoreWebModule` class, find the `ConfigureAutoMapper` method and change the `AddProfile` line as shown below:
````c#
options.AddProfile<BookStoreWebAutoMapperProfile>(validate: true);
````
It's up to you to use validation or not. It can prevent mapping mistakes, but comes with a cost of configuration. See [AutoMapper's documentation](http://automapper.readthedocs.io/en/latest/Configuration-validation.html) to fully understand it.
* This class simply injects the `IBookAppService` in its constructor and calls the `CreateAsync` method in the `OnPostAsync` handler.
##### CreateModal.cshtml
@ -139,8 +77,7 @@ Open the `CreateModal.cshtml` file and paste the code below:
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)">
</abp-modal-footer>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
````

BIN
docs/images/bookstore-book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/images/bookstore-books-table.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/images/bookstore-new-book-button.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 34 KiB

8
samples/BookStore/src/Acme.BookStore.Application/BookAppService.cs

@ -1,12 +1,16 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookAppService : AsyncCrudAppService<Book, BookDto, Guid>,IBookAppService
public class BookAppService :
AsyncCrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{

7
samples/BookStore/src/Acme.BookStore.Application/BookDto.cs

@ -1,13 +1,12 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
public class BookDto : EntityDto<Guid>
[AutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
[Required]
[StringLength(128)]
public string Name { get; set; }
public BookType Type { get; set; }

6
samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs

@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Acme.BookStore.Permissions;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.AutoMapper;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
@ -18,6 +19,11 @@ namespace Acme.BookStore
options.DefinitionProviders.Add<BookStorePermissionDefinitionProvider>();
});
services.Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<BookStoreApplicationAutoMapperProfile>();
});
services.AddAssemblyOf<BookStoreApplicationModule>();
}
}

12
samples/BookStore/src/Acme.BookStore.Application/BookStoreWebAutoMapperProfile.cs

@ -0,0 +1,12 @@
using AutoMapper;
namespace Acme.BookStore
{
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
//add your custom AutoMapper configuration
}
}
}

24
samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs

@ -0,0 +1,24 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
[AutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Type")]
public BookType Type { get; set; } = BookType.Undefined;
[Display(Name = "PublishDate")]
public DateTime PublishDate { get; set; }
[Display(Name = "Price")]
public float Price { get; set; }
}
}

9
samples/BookStore/src/Acme.BookStore.Application/IBookAppService.cs

@ -1,9 +1,16 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService : IAsyncCrudAppService<BookDto, Guid>
public interface IBookAppService :
IAsyncCrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
CreateUpdateBookDto, //Used to create a new book
CreateUpdateBookDto> //Used to update a book
{
}

1
samples/BookStore/src/Acme.BookStore.Domain/Acme.BookStore.Domain.csproj

@ -7,6 +7,7 @@
<ItemGroup>
<PackageReference Include="Volo.Abp.Identity.Domain" Version="0.3.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.1.1" />
</ItemGroup>
<ItemGroup>

4
samples/BookStore/src/Acme.BookStore.Domain/Book.cs

@ -1,12 +1,12 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
[Table("Books")]
public class Book : AggregateRoot<Guid>
public class Book : AuditedAggregateRoot<Guid>
{
[Required]
[StringLength(128)]

1
samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json

@ -10,6 +10,7 @@
"Type": "Type",
"PublishDate": "Publish Date",
"Price": "Price",
"CreationTime": "Creation Time",
"NewBook": "New book",
"Books": "Books"
}

1
samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json

@ -10,6 +10,7 @@
"Type": "Tür",
"PublishDate": "Yayınlanma Tarihi",
"Price": "Fiyat",
"CreationTime": "Oluşturulma zamanı",
"NewBook": "Yeni kitap",
"Books": "Kitaplar"
}

4
samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Acme.BookStore.EntityFrameworkCore.csproj

@ -5,10 +5,6 @@
<RootNamespace>Acme.BookStore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Migrations\20180628193042_Created_Book_Entity.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />

9
samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180528221424_Initial.Designer.cs → samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195241_Initial.Designer.cs

@ -1,25 +1,24 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Acme.BookStore.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Acme.BookStore.EntityFrameworkCore;
namespace Acme.BookStore.Migrations
{
[DbContext(typeof(BookStoreDbContext))]
[Migration("20180528221424_Initial")]
[Migration("20180701195241_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.0-preview2-30571")
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b =>

1
samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180528221424_Initial.cs → samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195241_Initial.cs

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Acme.BookStore.Migrations

10
samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180628193337_Created_Book_Entity.Designer.cs → samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195424_Created_Book_Entity.Designer.cs

@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Acme.BookStore.Migrations
{
[DbContext(typeof(BookStoreDbContext))]
[Migration("20180628193337_Created_Book_Entity")]
[Migration("20180701195424_Created_Book_Entity")]
partial class Created_Book_Entity
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -26,6 +26,14 @@ namespace Acme.BookStore.Migrations
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("CreationTime");
b.Property<Guid?>("CreatorId");
b.Property<DateTime?>("LastModificationTime");
b.Property<Guid?>("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128);

4
samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180628193337_Created_Book_Entity.cs → samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/20180701195424_Created_Book_Entity.cs

@ -12,6 +12,10 @@ namespace Acme.BookStore.Migrations
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
CreatorId = table.Column<Guid>(nullable: true),
LastModificationTime = table.Column<DateTime>(nullable: true),
LastModifierId = table.Column<Guid>(nullable: true),
Name = table.Column<string>(maxLength: 128, nullable: false),
Type = table.Column<byte>(nullable: false),
PublishDate = table.Column<DateTime>(nullable: false),

8
samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Migrations/BookStoreDbContextModelSnapshot.cs

@ -24,6 +24,14 @@ namespace Acme.BookStore.Migrations
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("CreationTime");
b.Property<Guid?>("CreatorId");
b.Property<DateTime?>("LastModificationTime");
b.Property<Guid?>("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128);

7
samples/BookStore/src/Acme.BookStore.Web/BookStoreWebAutoMapperProfile.cs

@ -1,6 +1,4 @@
using Acme.BookStore.Pages.Books;
using AutoMapper;
using Volo.Abp.AutoMapper;
using AutoMapper;
namespace Acme.BookStore
{
@ -8,8 +6,7 @@ namespace Acme.BookStore
{
public BookStoreWebAutoMapperProfile()
{
CreateMap<CreateModalModel.CreateBookViewModel, BookDto>()
.Ignore(x => x.Id);
//add your custom AutoMapper configuration
}
}
}

27
samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs

@ -1,5 +1,3 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
@ -9,7 +7,7 @@ namespace Acme.BookStore.Pages.Books
public class CreateModalModel : AbpPageModel
{
[BindProperty]
public CreateBookViewModel Book { get; set; }
public CreateUpdateBookDto Book { get; set; }
private readonly IBookAppService _bookAppService;
@ -20,29 +18,8 @@ namespace Acme.BookStore.Pages.Books
public async Task<IActionResult> OnPostAsync()
{
ValidateModel();
var bookDto = ObjectMapper.Map<CreateBookViewModel, BookDto>(Book);
await _bookAppService.CreateAsync(bookDto);
await _bookAppService.CreateAsync(Book);
return NoContent();
}
public class CreateBookViewModel
{
[Required]
[StringLength(128)]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Type")]
public BookType Type { get; set; } = BookType.Undefined;
[Display(Name = "PublishDate")]
public DateTime PublishDate { get; set; }
[Display(Name = "Price")]
public float Price { get; set; }
}
}
}

1
samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml

@ -28,6 +28,7 @@
<th>@L["Type"]</th>
<th>@L["PublishDate"]</th>
<th>@L["Price"]</th>
<th>@L["CreationTime"]</th>
</tr>
</thead>
</abp-table>

8
samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/books/index.js

@ -12,11 +12,15 @@
},
{
targets: 2,
data: "price"
data: "publishDate"
},
{
targets: 3,
data: "publishDate"
data: "price"
},
{
targets: 4,
data: "creationTime"
}
]
});

Loading…
Cancel
Save