Проверь мой код. var hierarchicalCostItems = BuildHierarchy(costItems) ничего не возвращает using AutoMapper; using BuildingFlow.Data.Contract; using BuildingFlow.Data.Entities; using BuildingFlow.Services.Models.ViewModels; using BuildingFlow.Services.Models.ViewModels.ProjectStage; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using OfficeOpenXml;
namespace BuildingFlow.Web.Controllers;
public class CostItemsController : Controller { private readonly IUnitOfWork _unitOfWork; private readonly IMapper _mapper; private readonly IRepository<CostItem> _costItemRepository;
public CostItemsController(IUnitOfWork unitOfWork, IMapper mapper) { _unitOfWork = unitOfWork; _mapper = mapper; _costItemRepository = _unitOfWork.GetRepository<CostItem>(); }
// GET: CostItems public async Task<IActionResult> Index() { var costItems = await _costItemRepository.AsReadOnlyQueryable() .Where(ci => ci.ProjectId == 1) .ToListAsync();
var costItemViewModel = _mapper.Map<IEnumerable<CostItemViewModel>>(costItems);
return View(costItemViewModel);
}
//GET: CostItems/Upload [HttpGet] public IActionResult Upload() { return View(); }
[HttpPost] public async Task<IActionResult> Upload(IFormFile file) { if (file == null || file.Length == 0) { ModelState.AddModelError("", "Please upload a file."); return View(); }
var costItems = new List<CostItemAddViewModel>();
using (var stream = new MemoryStream())
{
await file.CopyToAsync(stream);
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var package = new ExcelPackage(stream))
{
var worksheet = package.Workbook.Worksheets.First();
var rowCount = worksheet.Dimension.Rows;
for (int row = 2; row <= rowCount; row++) // Начинаем со второй строки, чтобы пропустить заголовки
{
var costItem = new CostItemAddViewModel();
costItem.NumberItem =
string.IsNullOrWhiteSpace(worksheet.Cells[row, 2].Text) ? null : worksheet.Cells[row, 2].Text;
costItem.NodeCode = string.IsNullOrWhiteSpace(worksheet.Cells[row, 3].Text)
? null
: worksheet.Cells[row, 3].Text;
costItem.CostName = string.IsNullOrWhiteSpace(worksheet.Cells[row, 4].Text)
? null
: worksheet.Cells[row, 4].Text;
costItem.ISRComment =
string.IsNullOrWhiteSpace(worksheet.Cells[row, 5].Text) ? null : worksheet.Cells[row, 5].Text;
costItem.ContractorComment = string.IsNullOrWhiteSpace(worksheet.Cells[row, 6].Text)
? null
: worksheet.Cells[row, 6].Text;
costItem.UnitOfMeasure = string.IsNullOrWhiteSpace(worksheet.Cells[row, 7].Text)
? null
: worksheet.Cells[row, 7].Text;
costItem.ConsumptionCoefficient = ParseNullableDecimal(worksheet.Cells[row, 8].Text);
costItem.BOCount = ParseNullableInt(worksheet.Cells[row, 9].Text);
costItem.TotalCount = ParseNullableDecimal(worksheet.Cells[row, 10].Text);
costItem.MatEquipmPrice = ParseNullableDecimal(worksheet.Cells[row, 11].Text);
costItem.SmpPnpPrice = ParseNullableDecimal(worksheet.Cells[row, 12].Text);
costItem.TotalPrice = ParseNullableDecimal(worksheet.Cells[row, 13].Text);
//MatEquipmCost = ParseNullableDecimal(worksheet.Cells[row, 14].Text),
//SmpPnpCost = ParseNullableDecimal(worksheet.Cells[row, 15].Text),
//TotalCost = ParseNullableDecimal(worksheet.Cells[row, 16].Text),
costItem.ProjectId = 1;
// Определение типа записи
costItem.TypeCost = DetermineType(costItem.NodeCode, costItem.UnitOfMeasure);
if (costItem.TypeCost == "Материал")
{
costItem.MatEquipmCost = CalculateMatEquipmCost(costItem.TotalCount, costItem.MatEquipmPrice);
}
if (costItem.TypeCost == "Работа")
{
costItem.SmpPnpCost = CalculateSmpPnpCost(costItem.TotalCount, costItem.SmpPnpPrice);
}
costItems.Add(costItem);
}
// Построение иерархии
var hierarchicalCostItems = BuildHierarchy(costItems);
// Вычисление затрат
CalculateCosts(hierarchicalCostItems);
// Преобразование в плоский список
var flatCostItems = FlattenHierarchy(hierarchicalCostItems).ToList();
// Маппинг в сущности
var entities = _mapper.Map<List<CostItem>>(flatCostItems);
// Сохранение записей без ParentId
_costItemRepository.CreateRange(entities);
await _unitOfWork.SaveChangesAsync();
// Создание словаря NumberItem -> Id
var numberItemToId = await _costItemRepository.AsReadOnlyQueryable()
.Where(ci => ci.ProjectId == 1)
.ToListAsync();
var mapping = numberItemToId.ToDictionary(ci => ci.NumberItem, ci => ci.Id);
// Обновление ParentId
foreach (var entity in numberItemToId)
{
var parentNumberItem = GetParentNumberItem(entity.NumberItem);
if (!string.IsNullOrEmpty(parentNumberItem) && mapping.ContainsKey(parentNumberItem))
{
entity.ParentId = mapping[parentNumberItem];
}
}
await _unitOfWork.SaveChangesAsync();
}
}
return RedirectToAction("Index"); // Перенаправление на другую страницу после загрузки
}
private List<CostItemAddViewModel> BuildHierarchy(List<CostItemAddViewModel> items) { var lookup = items.ToLookup(i => GetParentNumberItem(i.NumberItem));
var rootItems = items.Where(i => string.IsNullOrEmpty(GetParentNumberItem(i.NumberItem))).ToList();
foreach (var item in rootItems)
{
BuildChildren(item, lookup);
}
return rootItems;
}
private void BuildChildren(CostItemAddViewModel parent, ILookup<string, CostItemAddViewModel> lookup) { var children = lookup[parent.NumberItem]; foreach (var child in children) { parent.Children = parent.Children ?? new List<CostItemAddViewModel>(); parent.Children.Add(child); BuildChildren(child, lookup); } }
private string GetParentNumberItem(string numberItem) { if (string.IsNullOrEmpty(numberItem)) return null;
var parts = numberItem.Split('.');
if (parts.Length <= 1)
return null;
return string.Join(".", parts.Take(parts.Length - 1));
}
private void CalculateCosts(List<CostItemAddViewModel> items) { foreach (var item in items) { if (item.Children != null && item.Children.Any()) { CalculateCosts(item.Children); item.MatEquipmCost = item.Children.Sum(c => c.MatEquipmCost ?? 0); item.SmpPnpCost = item.Children.Sum(c => c.SmpPnpCost ?? 0); item.TotalCost = item.Children.Sum(c => c.TotalCost ?? 0); } else { // Вычисление MatEquipmCost if (item.ConsumptionCoefficient.HasValue && item.TotalCount.HasValue && item.MatEquipmPrice.HasValue) { item.MatEquipmCost = item.ConsumptionCoefficient.Value * item.TotalCount.Value * item.MatEquipmPrice.Value; } else { item.MatEquipmCost = 0m; }
// Вычисление SmpPnpCost
if (item.TotalCount.HasValue)
{
item.SmpPnpCost = item.SmpPnpPrice * item.TotalCount.Value;
}
else
{
item.SmpPnpCost = 0m;
}
// Вычисление TotalCost
item.TotalCost = (item.MatEquipmCost ?? 0) + (item.SmpPnpCost ?? 0);
}
}
}
private List<CostItemAddViewModel> FlattenHierarchy(List<CostItemAddViewModel> items) { var flatList = new List<CostItemAddViewModel>();
foreach (var item in items)
{
flatList.Add(item);
if (item.Children != null && item.Children.Any())
{
flatList.AddRange(FlattenHierarchy(item.Children.ToList()));
}
}
return flatList;
}
public decimal CalculateSmpPnpCost(decimal? totalCount, decimal? smpPnpPrice) { if (!totalCount.HasValue || !smpPnpPrice.HasValue) { // Обработка отсутствующих значений, можно выбросить исключение или вернуть ноль return 0m; }
var smpPnp = totalCount.Value * smpPnpPrice.Value;
var roundedResult = Math.Round(smpPnp, 2, MidpointRounding.AwayFromZero);
return roundedResult;
}
public decimal CalculateMatEquipmCost(decimal? totalCount, decimal? matEquipmPrice) { // Проверяем наличие значений if (!totalCount.HasValue || !matEquipmPrice.HasValue) { // Обработка отсутствующих значений, можно выбросить исключение или вернуть ноль return 0m; }
var roundedTotalRound = Math.Round(totalCount.Value, 2, MidpointRounding.AwayFromZero);
var multipliedValue = roundedTotalRound * matEquipmPrice.Value;
var matEquipmCost = Math.Round(multipliedValue, 2, MidpointRounding.AwayFromZero);
return matEquipmCost;
}
private string DetermineType(string? nodeCode, string? unitOfMeasure) { if (string.IsNullOrWhiteSpace(nodeCode)&& !string.IsNullOrEmpty(unitOfMeasure)) { return "Материал"; }
return "Работа"; // По умолчанию считаем запись работой
}
private decimal? ParseNullableDecimal(string text) { return decimal.TryParse(text, out decimal result) ? result : (decimal?)null; }
private int? ParseNullableInt(string text) { return int.TryParse(text, out int result) ? result : (int?)null; } }
Ваш код выглядит довольно хорошо, но есть несколько моментов, которые стоит проверить, чтобы убедиться, что метод BuildHierarchy
работает корректно и возвращает ожидаемые результаты. Давайте рассмотрим несколько возможных причин, по которым hierarchicalCostItems
может не возвращать ожидаемые данные.
Убедитесь, что список costItems
, передаваемый в метод BuildHierarchy
, не пуст. Если он пуст, то и иерархия не будет построена. Вы можете добавить отладочный вывод перед вызовом BuildHierarchy
:
// Построение иерархии
if (costItems.Count == 0)
{
// Логирование или отладочный вывод
Console.WriteLine("costItems is empty.");
}
var hierarchicalCostItems = BuildHierarchy(costItems);
GetParentNumberItem
Убедитесь, что метод GetParentNumberItem
работает корректно и возвращает правильные значения. Если он возвращает неправильные значения, это может привести к тому, что иерархия не будет построена должным образом.
BuildChildren
Убедитесь, что метод BuildChildren
корректно добавляет дочерние элементы. Если lookup
не содержит дочерних элементов для родительского элемента, то иерархия не будет построена.
CostItemAddViewModel
Убедитесь, что у вас есть свойство Children
в классе CostItemAddViewModel
. Если его нет, то вы не сможете добавлять дочерние элементы. Например:
public class CostItemAddViewModel
{
public string NumberItem { get; set; }
public List<CostItemAddViewModel> Children { get; set; } // Убедитесь, что это свойство существует
// Другие свойства...
}
Добавьте логирование в ключевые места, чтобы отслеживать, что происходит в вашем коде. Это поможет вам понять, где именно происходит сбой.
Убедитесь, что вы проверяете на null
и обрабатываете возможные исключения, чтобы избежать неожиданных сбоев.
Вот пример, как можно добавить отладочные сообщения:
private List<CostItemAddViewModel> BuildHierarchy(List<CostItemAddViewModel> items)
{
var lookup = items.ToLookup(i => GetParentNumberItem(i.NumberItem));
var rootItems = items.Where(i => string.IsNullOrEmpty(GetParentNumberItem(i.NumberItem))).ToList();
Console.WriteLine($"Found {rootItems.Count} root items.");
foreach (var item in rootItems)
{
BuildChildren(item, lookup);
}
return rootItems;
}
Проверьте указанные выше моменты, и, скорее всего, вы сможете найти причину, по которой hierarchicalCostItems
не возвращает ожидаемые данные. Если после всех проверок проблема все еще остается, попробуйте предоставить больше информации о том, что именно не работает, и я постараюсь помочь вам более детально.