后台商品列表功能开发全攻略
实现后台商品列表功能的关键步骤
数据模型设计
创建商品实体类(Product.cs),包含基础字段如Id、Name、Price、Stock等。使用数据注解进行验证:
public class Product
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Range(0.01, double.MaxValue)]
public decimal Price { get; set; }
public int Stock { get; set; }
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
数据库上下文配置
在ApplicationDbContext中配置DbSet和可能的种子数据:
public class ApplicationDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasData(
new Product { Id = 1, Name = "示例商品", Price = 99.99m, Stock = 100 }
);
}
}
控制器实现
创建ProductsController并实现CRUD操作。列表功能需包含分页和搜索:
public class ProductsController : Controller
{
private readonly ApplicationDbContext _context;
public ProductsController(ApplicationDbContext context)
{
_context = context;
}
public async Task<IActionResult> Index(int page = 1, string searchString = "")
{
var query = _context.Products.AsQueryable();
if (!string.IsNullOrEmpty(searchString))
{
query = query.Where(p => p.Name.Contains(searchString));
}
var pageSize = 10;
var totalItems = await query.CountAsync();
var products = await query.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
var model = new ProductListViewModel
{
Products = products,
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = pageSize,
TotalItems = totalItems
},
SearchString = searchString
};
return View(model);
}
}
视图模型设计
创建专用的视图模型类处理分页和搜索参数:
public class ProductListViewModel
{
public IEnumerable<Product> Products { get; set; }
public PagingInfo PagingInfo { get; set; }
public string SearchString { get; set; }
}
public class PagingInfo
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages => (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);
}
Razor视图实现
在Views/Products/Index.cshtml中实现交互界面:
@model ProductListViewModel
<form asp-action="Index" method="get">
<div class="input-group mb-3">
<input type="text" class="form-control" name="searchString" value="@Model.SearchString"/>
<button type="submit" class="btn btn-primary">搜索</button>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>价格</th>
<th>库存</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Products)
{
<tr>
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Price.ToString("C")</td>
<td>@item.Stock</td>
</tr>
}
</tbody>
</table>
<div class="pagination">
@for (int i = 1; i <= Model.PagingInfo.TotalPages; i++)
{
<a href="@Url.Action("Index", new { page = i, searchString = Model.SearchString })"
class="@(i == Model.PagingInfo.CurrentPage ? "active" : "")">
@i
</a>
}
</div>
性能优化技巧
使用异步编程模式提高吞吐量,添加适当的索引提升查询效率:
// 在DbContext的OnModelCreating中添加
modelBuilder.Entity<Product>()
.HasIndex(p => p.Name);
// 使用AsNoTracking()减少内存占用
var products = await query.AsNoTracking().ToListAsync();
安全性考虑
添加[Authorize]特性限制访问权限,防止CSRF攻击:
[Authorize(Roles = "Admin")]
public class ProductsController : Controller
{
// 控制器代码
}
// 在视图中添加防伪令牌
<form asp-action="Create" method="post">
@Html.AntiForgeryToken()
<!-- 表单字段 -->
</form>
前端增强
引入jQuery DataTables实现客户端分页和排序,或使用AJAX加载数据:
$(document).ready(function() {
$('#productsTable').DataTable({
"processing": true,
"serverSide": true,
"ajax": {
"url": "/Products/GetProducts",
"type": "POST"
}
});
});
单元测试策略
编写控制器和服务的单元测试,确保核心逻辑正确:
[Fact]
public async Task Index_ReturnsViewResult_WithProductList()
{
// 准备
var mockContext = new Mock<ApplicationDbContext>();
mockContext.Setup(c => c.Products).Returns(MockDbSet(new List<Product>{
new Product { Id = 1, Name = "Test" }
}));
// 执行
var controller = new ProductsController(mockContext.Object);
var result = await controller.Index();
// 断言
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<ProductListViewModel>(viewResult.Model);
Assert.Single(model.Products);
}
BbS.okacop060.info/PoSt/1120_603485.HtM
BbS.okacop061.info/PoSt/1120_182119.HtM
BbS.okacop062.info/PoSt/1120_245981.HtM
BbS.okacop063.info/PoSt/1120_886097.HtM
BbS.okacop065.info/PoSt/1120_462709.HtM
BbS.okacop066.info/PoSt/1120_128011.HtM
BbS.okacop067.info/PoSt/1120_982004.HtM
BbS.okacop068.info/PoSt/1120_690279.HtM
BbS.okacop069.info/PoSt/1120_298969.HtM
BbS.okacop070.info/PoSt/1120_896678.HtM
BbS.okacop060.info/PoSt/1120_233726.HtM
BbS.okacop061.info/PoSt/1120_545855.HtM
BbS.okacop062.info/PoSt/1120_853919.HtM
BbS.okacop063.info/PoSt/1120_563955.HtM
BbS.okacop065.info/PoSt/1120_523815.HtM
BbS.okacop066.info/PoSt/1120_984390.HtM
BbS.okacop067.info/PoSt/1120_046860.HtM
BbS.okacop068.info/PoSt/1120_806353.HtM
BbS.okacop069.info/PoSt/1120_005359.HtM
BbS.okacop070.info/PoSt/1120_221332.HtM
BbS.okacop060.info/PoSt/1120_457526.HtM
BbS.okacop061.info/PoSt/1120_368218.HtM
BbS.okacop062.info/PoSt/1120_760803.HtM
BbS.okacop063.info/PoSt/1120_735362.HtM
BbS.okacop065.info/PoSt/1120_764778.HtM
BbS.okacop066.info/PoSt/1120_002233.HtM
BbS.okacop067.info/PoSt/1120_526591.HtM
BbS.okacop068.info/PoSt/1120_150214.HtM
BbS.okacop069.info/PoSt/1120_665620.HtM
BbS.okacop070.info/PoSt/1120_940601.HtM
BbS.okacop060.info/PoSt/1120_132551.HtM
BbS.okacop061.info/PoSt/1120_249846.HtM
BbS.okacop062.info/PoSt/1120_558915.HtM
BbS.okacop063.info/PoSt/1120_518891.HtM
BbS.okacop065.info/PoSt/1120_100585.HtM
BbS.okacop066.info/PoSt/1120_518524.HtM
BbS.okacop067.info/PoSt/1120_266546.HtM
BbS.okacop068.info/PoSt/1120_243416.HtM
BbS.okacop069.info/PoSt/1120_671909.HtM
BbS.okacop070.info/PoSt/1120_202702.HtM
BbS.okacop060.info/PoSt/1120_447569.HtM
BbS.okacop061.info/PoSt/1120_435173.HtM
BbS.okacop062.info/PoSt/1120_608323.HtM
BbS.okacop063.info/PoSt/1120_367624.HtM
BbS.okacop065.info/PoSt/1120_976891.HtM
BbS.okacop066.info/PoSt/1120_567753.HtM
BbS.okacop067.info/PoSt/1120_427687.HtM
BbS.okacop068.info/PoSt/1120_925356.HtM
BbS.okacop069.info/PoSt/1120_939701.HtM
BbS.okacop070.info/PoSt/1120_279304.HtM
BbS.okacop060.info/PoSt/1120_874102.HtM
BbS.okacop061.info/PoSt/1120_780475.HtM
BbS.okacop062.info/PoSt/1120_823183.HtM
BbS.okacop063.info/PoSt/1120_623005.HtM
BbS.okacop065.info/PoSt/1120_905104.HtM
BbS.okacop066.info/PoSt/1120_164292.HtM
BbS.okacop067.info/PoSt/1120_204317.HtM
BbS.okacop068.info/PoSt/1120_089871.HtM
BbS.okacop069.info/PoSt/1120_925104.HtM
BbS.okacop070.info/PoSt/1120_139720.HtM
BbS.okacop060.info/PoSt/1120_010802.HtM
BbS.okacop061.info/PoSt/1120_987809.HtM
BbS.okacop062.info/PoSt/1120_948334.HtM
BbS.okacop063.info/PoSt/1120_609916.HtM
BbS.okacop065.info/PoSt/1120_313409.HtM
BbS.okacop066.info/PoSt/1120_735429.HtM
BbS.okacop067.info/PoSt/1120_211166.HtM
BbS.okacop068.info/PoSt/1120_001976.HtM
BbS.okacop069.info/PoSt/1120_481574.HtM
BbS.okacop070.info/PoSt/1120_606347.HtM
BbS.okacop071.info/PoSt/1120_297532.HtM
BbS.okacop072.info/PoSt/1120_755147.HtM
BbS.okacop073.info/PoSt/1120_101834.HtM
BbS.okacop074.info/PoSt/1120_670322.HtM
BbS.okacop075.info/PoSt/1120_599764.HtM
BbS.okacop076.info/PoSt/1120_503734.HtM
BbS.okacop077.info/PoSt/1120_494874.HtM
BbS.okacop078.info/PoSt/1120_518653.HtM
BbS.okacop079.info/PoSt/1120_560001.HtM
BbS.okacop080.info/PoSt/1120_903501.HtM
查看12道真题和解析
