.NET Web API仓储模式下,如何通过分类名添加带关联实体的商品
问题描述
当前添加商品时需要传入CategoryId才能关联分类,希望改为仅传入CategoryName即可完成关联,无需提供CategoryId,请问如何修改Post方法实现该需求?
提供的代码实现
Product实体
public class Product : BaseEntity { public string ProductName { get; set; } public string Description { get; set; } public string Img1 { get; set; } public string Img2 { get; set; } public string Img3 { get; set; } public decimal Price { get; set; } public int Stock { get; set; } // 关联实体 public Category Category { get; set; } public int CategoryId { get; set; } }
Category实体
public class Category : BaseEntity { public string Name { get; set; } public string Img { get; set; } public string Description { get; set; } }
IProductRepository接口
public interface IProductRepository { Task<Product> GetProductByIdAsync(int id); Task<IReadOnlyList<Product>> GetProductsAsync(); Task<bool> SaveChangesAsync(); void Add(Product product); void Update(Product product); void Delete(Product product); }
Product仓储实现
namespace Infrastructure { public class ProductRepository : IProductRepository { private readonly StoreContext _context; public ProductRepository(StoreContext context) { _context = context; } public void Add(Product product) { _context.Add(product); } public void Delete(Product product) { _context.Remove(product); } public async Task<Product> GetProductByIdAsync(int id) { return await _context.Products.Include(c => c.Category) .FirstOrDefaultAsync(p => p.Id == id); } public async Task<IReadOnlyList<Product>> GetProductsAsync() { return await _context.Products.Include(p => p.Category). ToArrayAsync(); } public async Task<bool> SaveChangesAsync() { return await _context.SaveChangesAsync().ConfigureAwait(false) > 0; } public void Update(Product product) { _context.Attach(product); _context.Entry(product).State = EntityState.Modified; } } }
原ProductController的Post方法
[HttpPost] public async Task<ActionResult> AddProduct(Product product) { try { if (product.Id == 0) { var responseError = new ResponseError(StatusCodes.Status400BadRequest, "Fields cannot be empty."); var response = new Response(false, null, responseError); return BadRequest(response); } else { _repository.Add(product); await _repository.SaveChangesAsync(); var response = new Response(true, product, null); return Ok(response); } } catch (Exception e) { var responseError = new ResponseError(StatusCodes.Status500InternalServerError, e.Message); var response = new Response(false, null, responseError); return StatusCode(StatusCodes.Status500InternalServerError, response); } }
解决方案
步骤1:创建新增商品的DTO
不要直接用Product实体接收前端请求,创建专门的DTO来接收参数,包含业务字段和CategoryName:
public class AddProductDto { public string ProductName { get; set; } public string Description { get; set; } public string Img1 { get; set; } public string Img2 { get; set; } public string Img3 { get; set; } public decimal Price { get; set; } public int Stock { get; set; } public string CategoryName { get; set; } // 接收分类名称 }
步骤2:修改Post方法逻辑
替换方法参数为AddProductDto,根据分类名称查询对应分类,处理关联逻辑,同时修复原方法的错误判断:
[HttpPost] public async Task<ActionResult> AddProduct(AddProductDto productDto) { try { // 验证必填业务字段 if (string.IsNullOrWhiteSpace(productDto.ProductName) || productDto.Price <= 0) { var responseError = new ResponseError(StatusCodes.Status400BadRequest, "必填字段不能为空或格式错误."); var response = new Response(false, null, responseError); return BadRequest(response); } // 根据分类名称查询对应的Category(忽略大小写匹配) var category = await _context.Categories.FirstOrDefaultAsync(c => c.Name.Equals(productDto.CategoryName, StringComparison.OrdinalIgnoreCase)); if (category == null) { var responseError = new ResponseError(StatusCodes.Status404NotFound, $"不存在名称为{productDto.CategoryName}的分类."); var response = new Response(false, null, responseError); return NotFound(response); } // 构建Product实体并关联分类ID var product = new Product { ProductName = productDto.ProductName, Description = productDto.Description, Img1 = productDto.Img1, Img2 = productDto.Img2, Img3 = productDto.Img3, Price = productDto.Price, Stock = productDto.Stock, CategoryId = category.Id }; _repository.Add(product); await _repository.SaveChangesAsync(); var response = new Response(true, product, null); return Ok(response); } catch (Exception e) { var responseError = new ResponseError(StatusCodes.Status500InternalServerError, e.Message); var response = new Response(false, null, responseError); return StatusCode(StatusCodes.Status500InternalServerError, response); } }
步骤3:仓储扩展(可选)
如果希望将分类查询逻辑封装到仓储,给IProductRepository添加方法:
// IProductRepository接口新增 Task<Category> GetCategoryByNameAsync(string categoryName);
仓储实现中添加对应方法:
public async Task<Category> GetCategoryByNameAsync(string categoryName) { return await _context.Categories.FirstOrDefaultAsync(c => c.Name.Equals(categoryName, StringComparison.OrdinalIgnoreCase)); }
之后控制器可调用_repository.GetCategoryByNameAsync(productDto.CategoryName)查询分类,遵循仓储模式的职责分离。
关键说明
- 原方法中
product.Id ==0的判断逻辑错误,新增商品时Id通常为自增默认值0,应验证实际必填的业务字段。 - 使用
StringComparison.OrdinalIgnoreCase忽略大小写匹配分类名称,提升用户体验。 - 必须处理分类不存在的情况,返回404错误,避免因无效CategoryId导致数据库异常。
内容的提问来源于stack exchange,提问作者Chiwen
相关产品推荐
相关产品推荐

