Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Oct 4, 2023. It is now read-only.

Commit 63f2414

Browse files
author
kahmingt
committed
Enabled Products Sorting, Filtering, & Paging
1 parent ce25ed8 commit 63f2414

17 files changed

+954
-32
lines changed

‎WebApi/Area/Product/Controller/ProductController.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace WebApi.Area.Product.Controllers
1111
{
12-
//[Authorize]
12+
[Authorize]
1313
[ApiController]
1414
[Route("api/[controller]")]
1515
public class ProductController : ControllerBase
@@ -138,6 +138,7 @@ public async Task<ActionResult<Products>> GetProductListAsync([FromQuery] Produc
138138
}
139139
else
140140
{
141+
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(modelDB.GetPagingMetadata));
141142
var modelDTO = _mapper.Map<List<Products>, List<ProductListRetrieveModel>>(modelDB);
142143
return Ok(modelDTO);
143144
}

‎WebApi/Area/Product/Model/ProductDetailNewModel.cs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,24 @@ namespace WebApi.Area.Product.Model
66
public class ProductDetailCreateModel
77
{
88
[Required]
9-
[RegularExpression("^[0-9]*",ErrorMessage="Category Id must be numeric.")]
9+
[ValidateUniqueIdentifier]
1010
public int CategoryID { get; set; }
1111

12+
[Required]
1213
[ValidateProductName]
13-
[RegularExpression("^[A-Za-z]", ErrorMessage = "Product name can contain only alphabets (A-Za-z).")]
1414
public string ProductName { get; set; }
1515

1616
[Required]
17-
[RegularExpression("^[0-9]*",ErrorMessage="Category Id must be numeric.")]
17+
[ValidateUniqueIdentifier]
1818
public int SupplierID { get; set; }
1919

2020
[Required]
21-
[RegularExpression("^\\d{0,}?$",ErrorMessage="Unit in stock is invalid format.")]
21+
[ValidateUnitInStock]
2222
public short UnitsInStock { get; set; }
2323

2424
[Required]
2525
[DisplayFormat(DataFormatString = "{0:0.00}", ApplyFormatInEditMode = true)]
26-
[RegularExpression("^\\d{0,}(\\.\\d{0,2})?$",ErrorMessage="Unit price is invalid format.")]
26+
[ValidateUnitPrice]
2727
public decimal UnitPrice { get; set; }
2828

2929
}

‎WebApi/Area/Product/Model/ProductDetailUpdateModel.cs‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
using System.ComponentModel.DataAnnotations;
2+
using WebApi.Area.Product.Utility;
23

34
namespace WebApi.Area.Product.Model
45
{
56
public class ProductDetailUpdateModel
67
{
78
[Required]
8-
[RegularExpression("^[0-9]*",ErrorMessage="Category Id must be numeric.")]
9+
[ValidateUniqueIdentifier]
910
public int CategoryID { get; set; }
1011

1112
[Required]
12-
[RegularExpression("^[0-9]*",ErrorMessage="Product Id must be numeric.")]
13+
[ValidateUniqueIdentifier]
1314
public int ProductID { get; set; }
1415

1516
[Display(Name = "Product")]
16-
[RegularExpression("^[A-Za-z]",ErrorMessage="Product name can contain only alphabets (A-Za-z).")]
17+
[ValidateProductName]
1718
public string ProductName { get; set; }
1819

1920
[Required]
20-
[RegularExpression("^[0-9]*",ErrorMessage="Supplier Id must be numeric.")]
21+
[ValidateUniqueIdentifier]
2122
public int SupplierID { get; set; }
2223

2324
[Required]
24-
[RegularExpression("^\\d{0,}?$",ErrorMessage="Unit in stock format is invalid.")]
25+
[ValidateUnitInStock]
2526
public short UnitsInStock { get; set; }
2627

2728
[Required]
2829
[DisplayFormat(DataFormatString = "{0:0.00}", ApplyFormatInEditMode = true)]
29-
[RegularExpression("^\\d{0,}(\\.\\d{0,2})?$",ErrorMessage="Unit price is invalid format.")]
30+
[ValidateUnitPrice]
3031
public decimal UnitPrice { get; set; }
3132

3233
}

‎WebApi/Area/Product/Repository/IProductRepository.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public interface IProductRepository : IGenericRepository<Products>
2525
/// <summary>
2626
/// Get entire product list.
2727
/// </summary>
28-
Task<List<Products>> GetProductListAsync(ProductQueryableParameter parameter);
28+
Task<PagedList<Products>> GetProductListAsync(ProductQueryableParameter parameter);
2929

3030
/// <summary>
3131
/// Update product details by id.

‎WebApi/Area/Product/Repository/ProductRepository.cs‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
using Microsoft.AspNetCore.Identity;
2-
using Microsoft.CodeAnalysis;
1+
using Microsoft.CodeAnalysis;
32
using Microsoft.EntityFrameworkCore;
4-
using System.Reflection;
5-
using System.Text;
63
using WebApi.Area.Product.Utility;
74
using WebApi.Shared.Database;
85
using WebApi.Shared.Database.Entity;
@@ -45,17 +42,24 @@ public async Task<Products> GetProductDetailsByIdAsync(int id)
4542
return model!;
4643
}
4744

48-
public async Task<List<Products>> GetProductListAsync(ProductQueryableParameter parameter)
45+
public async Task<PagedList<Products>> GetProductListAsync(ProductQueryableParameter parameter)
4946
{
5047
var model = (IQueryable<Products>)GetAll(x => !x.IsDeleted).Include(x => x.Category);
51-
return model!;
48+
49+
// Sorting & Filtering
50+
model = _operationHelper.RunAll(model, parameter);
51+
52+
return PagedList<Products>.ToPagedList(
53+
model,
54+
parameter.PageNumber,
55+
parameter.PageSize);
5256
}
5357

5458
public async Task UpdateProductDetailsByIdAsync(Products products)
5559
{
5660
_db.Entry(products).Property(x => x.ProductId).IsModified = false;
57-
5861
Update(products);
5962
}
63+
6064
}
6165
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using System.Text.RegularExpressions;
3+
4+
namespace WebApi.Area.Product.Utility
5+
{
6+
/// <summary>
7+
/// [CustomModelValidation] Validate unique identifier.
8+
/// </summary>
9+
/// <remarks>
10+
/// Criteria:
11+
/// <br>1. It cannot be null or whitespace.</br>
12+
/// <br>2. It must be numeric (0-9).</br>
13+
/// </remarks>
14+
public class ValidateUniqueIdentifierAttribute : ValidationAttribute
15+
{
16+
// Keep the expression compiled to improve performance.
17+
private static readonly Regex ValidationRegex = new Regex(@"^[0-9]*", RegexOptions.Compiled);
18+
19+
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
20+
{
21+
if (string.IsNullOrWhiteSpace(value.ToString()))
22+
{
23+
return new ValidationResult(validationContext.MemberName + " is required.");
24+
}
25+
if (!ValidationRegex.IsMatch(value.ToString()))
26+
{
27+
return new ValidationResult(validationContext.MemberName + " must be numeric (0-9).");
28+
}
29+
return ValidationResult.Success;
30+
}
31+
}
32+
33+
/// <summary>
34+
/// [CustomModelValidation] Validate Product name.
35+
/// </summary>
36+
/// <remarks>
37+
/// Criteria:
38+
/// <br>1. It cannot be null or whitespace.</br>
39+
/// <br>2. It must begin with an alphabet (A-Za-z).</br>
40+
/// </remarks>
41+
public class ValidateProductNameAttribute : ValidationAttribute
42+
{
43+
// Keep the expression compiled to improve performance.
44+
private static readonly Regex ValidationRegex = new Regex(@"^[A-Za-z]*", RegexOptions.Compiled);
45+
46+
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
47+
{
48+
if (string.IsNullOrWhiteSpace(value.ToString()))
49+
{
50+
return new ValidationResult(validationContext.MemberName + " is required.");
51+
}
52+
if (!ValidationRegex.IsMatch(value.ToString()))
53+
{
54+
return new ValidationResult(validationContext.MemberName + " must begin wtih an alphabet (A-Za-z).");
55+
}
56+
return ValidationResult.Success;
57+
}
58+
}
59+
60+
/// <summary>
61+
/// [CustomModelValidation] Validate Product unit in stock.
62+
/// </summary>
63+
/// <remarks>
64+
/// Criteria:
65+
/// <br>1. It cannot be null or whitespace.</br>
66+
/// <br>1. If not null, it must be a be a round integer (ie. no decimal).</br>
67+
/// </remarks>
68+
public class ValidateUnitInStockAttribute : ValidationAttribute
69+
{
70+
// Keep the expression compiled to improve performance.
71+
private static readonly Regex ValidationRegex = new Regex(@"^[0-9]*$", RegexOptions.Compiled);
72+
73+
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
74+
{
75+
if (string.IsNullOrWhiteSpace(value.ToString()))
76+
{
77+
return new ValidationResult(validationContext.MemberName + " is required.");
78+
}
79+
if (!ValidationRegex.IsMatch(value.ToString()))
80+
{
81+
return new ValidationResult(validationContext.MemberName + " must be a round integer (ie. no decimal).");
82+
}
83+
return ValidationResult.Success;
84+
}
85+
}
86+
87+
/// <summary>
88+
/// [CustomModelValidation] Validate Product unit price.
89+
/// </summary>
90+
/// <remarks>
91+
/// Criteria:
92+
/// <br>1. It cannot be null or whitespace.</br>
93+
/// <br>2. It must be a valid currency denomination (0-9).</br>
94+
/// </remarks>
95+
public class ValidateUnitPriceAttribute : ValidationAttribute
96+
{
97+
// Keep the expression compiled to improve performance.
98+
private static readonly Regex ValidationRegex = new Regex(@"^[0-9]*[.]{0,1}[0-9]{0,2}$", RegexOptions.Compiled);
99+
100+
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
101+
{
102+
if (string.IsNullOrWhiteSpace(value.ToString()))
103+
{
104+
return new ValidationResult(validationContext.MemberName + " is required.");
105+
}
106+
if (!ValidationRegex.IsMatch(value.ToString()))
107+
{
108+
return new ValidationResult(validationContext.MemberName + " must be in a valid currency denomination.");
109+
}
110+
return ValidationResult.Success;
111+
}
112+
}
113+
114+
115+
/// <summary>
116+
/// [CustomModelValidation] Validate nullable Product unit in stock.
117+
/// </summary>
118+
/// <remarks>
119+
/// Criteria:
120+
/// <br>1. If not null, it must be a be a round integer (ie. no decimal).</br>
121+
/// </remarks>
122+
public class ValidateNullableUnitInStockAttribute : ValidationAttribute
123+
{
124+
// Keep the expression compiled to improve performance.
125+
private static readonly Regex ValidationRegex = new Regex(@"^[0-9]*$", RegexOptions.Compiled);
126+
127+
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
128+
{
129+
if (value != null)
130+
{
131+
if (!ValidationRegex.IsMatch(value.ToString()))
132+
{
133+
return new ValidationResult(validationContext.MemberName + " must be a positive whole number (ie. no decimal).");
134+
}
135+
}
136+
return ValidationResult.Success;
137+
}
138+
}
139+
140+
/// <summary>
141+
/// [CustomModelValidation] Validate nullable Product unit price.
142+
/// </summary>
143+
/// <remarks>
144+
/// Criteria:
145+
/// <br>1. If not null, it must be a valid currency denomination (0-9).</br>
146+
/// </remarks>
147+
public class ValidateNullableUnitPriceAttribute : ValidationAttribute
148+
{
149+
// Keep the expression compiled to improve performance.
150+
private static readonly Regex ValidationRegex = new Regex(@"^[0-9]*[.]{0,1}[0-9]{0,2}$", RegexOptions.Compiled);
151+
152+
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
153+
{
154+
if (value != null)
155+
{
156+
if (!ValidationRegex.IsMatch(value.ToString()))
157+
{
158+
return new ValidationResult(validationContext.MemberName + " must be in a valid positive currency denomination.");
159+
}
160+
}
161+
return ValidationResult.Success;
162+
}
163+
}
164+
165+
}

‎WebApi/Area/Product/Utility/ProductMappingProfile.cs‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using AutoMapper;
22
using WebApi.Shared.Database.Entity;
33
using WebApi.Area.Product.Model;
4+
using WebApi.Shared.Utility;
45

56
namespace WebApi.Area.Product.Utility;
67

@@ -12,9 +13,6 @@ public MappingProfile()
1213
CreateMap<Products, ProductListRetrieveModel>()
1314
.ForMember(dest => dest.CategoryName, opts => opts.MapFrom(src => src.Category.CategoryName));
1415

15-
//CreateMap<PagedList<Products>, PagedList<ProductListing>>()
16-
// .ConvertUsing<PagedListConverter<Products, ProductListing>>();
17-
1816
// Database.Products -> Model.Retrieve.ProductDetail
1917
CreateMap<Products, ProductDetailRetrieveModel>()
2018
.ForMember(dest => dest.CategoryName, opts => opts.MapFrom(src => src.Category.CategoryName))
@@ -25,7 +23,6 @@ public MappingProfile()
2523

2624
// Model.Create.Product.Detail -> Database.Products
2725
CreateMap<ProductDetailCreateModel, Products>();
28-
29-
3026
}
27+
3128
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /