ASP.NET Core MVC实现数据计算并插入SQL数据库的方法及问题解决
ASP.NET Core MVC 燃油数据计算与入库的高效实现方案
现有代码的核心问题
- 累计值范围错误:当前
Sum()没有过滤车辆,会把全表所有车辆的数据加总,完全不符合单车辆累计的需求。 - 空值/除零异常:除法运算前未判断除数是否为0或null,比如
accLitres为0时,consumptionKm = accKilometeres / accLitres直接报错;fuelPrice为0或null时,costOfTheKm也会出问题。 - 重复查询数据库:多次调用
fuelDbContext.FuelData会触发多次SQL查询,效率极低。 - 未持久化数据:修改完
fuel对象后没调用SaveChangesAsync(),数据根本不会写入数据库。 - 方法不符合规范:实现了
IActionResult但无返回值,违反MVC控制器方法要求。
高效实现思路
- 限定查询范围:只查询当前车辆的燃油记录,避免全表扫描。
- 内存中批量计算:一次性加载当前车辆的所有数据到内存后计算,减少数据库交互次数。
- 添加异常防护:除法运算前先校验除数合法性,避免空值/除零错误。
- 分离计算逻辑:把计算逻辑抽离到独立服务类,让控制器更简洁,同时方便复用和测试。
- 全程异步操作:使用EF Core异步方法提升并发性能。
优化后的代码实现
1. 控制器方法
[HttpPost] public async Task<IActionResult> Calculate(FuelCalculationDataModel calcFuelDataRequest) { // 基础参数校验 if (string.IsNullOrEmpty(calcFuelDataRequest.vehicleregistrationNumber)) { return BadRequest("车辆注册号不能为空"); } // 一次性获取当前车辆的所有燃油记录 var currentVehicleRecords = await fuelDbContext.FuelData .Where(p => p.vehicleregistrationNumber == calcFuelDataRequest.vehicleregistrationNumber) .ToListAsync(); if (!currentVehicleRecords.Any()) { return NotFound("未找到该车辆的燃油记录"); } // 匹配要更新的目标记录(请根据业务调整匹配条件,比如ID) var targetFuelRecord = currentVehicleRecords.FirstOrDefault(p => /* 你的匹配逻辑,比如p.Id == calcFuelDataRequest.Id */); if (targetFuelRecord == null) { return NotFound("未找到要更新的燃油记录"); } // 计算单车辆累计值 var totalKilometers = currentVehicleRecords.Sum(p => p.odometerTotal ?? 0); var totalLitres = currentVehicleRecords.Sum(p => p.filledVolume ?? 0); // 计算单条记录的加油成本 targetFuelRecord.refillCost = (targetFuelRecord.fuelPrice ?? 0) * (targetFuelRecord.filledVolume ?? 0); // 计算油耗(处理除零情况) targetFuelRecord.consumptionKm = totalLitres != 0 ? totalKilometers / totalLitres : 0; targetFuelRecord.consumptionLitres = totalKilometers != 0 ? totalLitres / totalKilometers : 0; // 计算每公里成本(处理油价为0的情况) targetFuelRecord.costOfTheKm = (targetFuelRecord.fuelPrice ?? 0) != 0 ? (1 / targetFuelRecord.fuelPrice.Value) * targetFuelRecord.consumptionKm : 0; // 计算总累计值(根据业务需求调整) targetFuelRecord.accLitresTotal = currentVehicleRecords.Sum(p => p.accLitres ?? 0); targetFuelRecord.accKilometerTotal = currentVehicleRecords.Sum(p => p.accKilometeres ?? 0); targetFuelRecord.consumptionKmTotal = currentVehicleRecords.Sum(p => p.consumptionKm ?? 0); targetFuelRecord.consumptionlitresTotal = currentVehicleRecords.Sum(p => p.consumptionLitres ?? 0); // 保存更改到数据库 await fuelDbContext.SaveChangesAsync(); return Ok("计算与数据更新完成"); }
2. 抽离计算逻辑到服务类(可选但推荐)
public class FuelCalculationService { // 计算单车辆里程与油量累计值 public (decimal TotalKilometers, decimal TotalLitres) CalculateAccumulatedValues(List<FuelData> vehicleRecords) { var totalKilometers = vehicleRecords.Sum(p => p.odometerTotal ?? 0); var totalLitres = vehicleRecords.Sum(p => p.filledVolume ?? 0); return (totalKilometers, totalLitres); } // 计算单次加油成本 public decimal CalculateRefillCost(decimal? fuelPrice, decimal? filledVolume) { return (fuelPrice ?? 0) * (filledVolume ?? 0); } // 计算油耗 public (decimal ConsumptionKm, decimal ConsumptionLitres) CalculateFuelConsumption(decimal totalKilometers, decimal totalLitres) { var consumptionKm = totalLitres != 0 ? totalKilometers / totalLitres : 0; var consumptionLitres = totalKilometers != 0 ? totalLitres / totalKilometers : 0; return (consumptionKm, consumptionLitres); } // 计算每公里成本 public decimal CalculateCostPerKm(decimal? fuelPrice, decimal consumptionKm) { return (fuelPrice ?? 0) != 0 ? (1 / fuelPrice.Value) * consumptionKm : 0; } // 计算总累计值 public (decimal AccLitresTotal, decimal AccKilometerTotal, decimal ConsumptionKmTotal, decimal ConsumptionLitresTotal) CalculateTotalAccumulated(List<FuelData> vehicleRecords) { var accLitresTotal = vehicleRecords.Sum(p => p.accLitres ?? 0); var accKilometerTotal = vehicleRecords.Sum(p => p.accKilometeres ?? 0); var consumptionKmTotal = vehicleRecords.Sum(p => p.consumptionKm ?? 0); var consumptionLitresTotal = vehicleRecords.Sum(p => p.consumptionLitres ?? 0); return (accLitresTotal, accKilometerTotal, consumptionKmTotal, consumptionLitresTotal); } }
3. 控制器注入服务后的简化写法
private readonly FuelDbContext _fuelDbContext; private readonly FuelCalculationService _calculationService; // 构造函数注入 public YourController(FuelDbContext fuelDbContext, FuelCalculationService calculationService) { _fuelDbContext = fuelDbContext; _calculationService = calculationService; } [HttpPost] public async Task<IActionResult> Calculate(FuelCalculationDataModel calcFuelDataRequest) { if (string.IsNullOrEmpty(calcFuelDataRequest.vehicleregistrationNumber)) { return BadRequest("车辆注册号不能为空"); } var currentVehicleRecords = await _fuelDbContext.FuelData .Where(p => p.vehicleregistrationNumber == calcFuelDataRequest.vehicleregistrationNumber) .ToListAsync(); if (!currentVehicleRecords.Any()) { return NotFound("未找到该车辆的燃油记录"); } var targetFuelRecord = currentVehicleRecords.FirstOrDefault(p => /* 你的匹配逻辑 */); if (targetFuelRecord == null) { return NotFound("未找到要更新的燃油记录"); } // 调用服务完成计算 var (totalKilometers, totalLitres) = _calculationService.CalculateAccumulatedValues(currentVehicleRecords); targetFuelRecord.refillCost = _calculationService.CalculateRefillCost(targetFuelRecord.fuelPrice, targetFuelRecord.filledVolume); var (consumptionKm, consumptionLitres) = _calculationService.CalculateFuelConsumption(totalKilometers, totalLitres); targetFuelRecord.consumptionKm = consumptionKm; targetFuelRecord.consumptionLitres = consumptionLitres; targetFuelRecord.costOfTheKm = _calculationService.CalculateCostPerKm(targetFuelRecord.fuelPrice, consumptionKm); var (accLitresTotal, accKilometerTotal, consumptionKmTotal, consumptionLitresTotal) = _calculationService.CalculateTotalAccumulated(currentVehicleRecords); targetFuelRecord.accLitresTotal = accLitresTotal; targetFuelRecord.accKilometerTotal = accKilometerTotal; targetFuelRecord.consumptionKmTotal = consumptionKmTotal; targetFuelRecord.consumptionlitresTotal = consumptionLitresTotal; await _fuelDbContext.SaveChangesAsync(); return Ok("计算与数据更新完成"); }
额外注意事项
- 数据验证:给
FuelCalculationDataModel添加数据注解(如[Required]、[Range])或使用FluentValidation,提前拦截无效输入。 - 数据库优化:给
vehicleregistrationNumber字段添加索引,提升查询效率。 - 业务逻辑确认:如果
accKilometeres应该是车辆的里程差总和(如本次加油里程减上次加油里程),请调整计算逻辑,不要直接累加所有odometerTotal。 - 异常处理:添加try-catch块捕获数据库操作异常,返回更友好的错误提示。
内容的提问来源于stack exchange,提问作者Cory Blake
相关产品推荐
相关产品推荐

