Рефакторинг парсинга

1. Добавлен шаблон для сообщений
2. Поправлен naming у сервисов парсинга траекторий
3. Удалена регистрация зависимостей парсеров траекторий
4. Внутри фабрики добавлено создание отдельного scope. Фикс нейминга констант
This commit is contained in:
Степанов Дмитрий 2024-02-08 12:50:14 +03:00
parent c33b7d086a
commit 1b3c06c927
11 changed files with 82 additions and 28 deletions

View File

@ -1,6 +1,4 @@
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
@ -35,4 +33,5 @@ public interface IParserService<TDto, in TOptions> : IParserService
/// </summary> /// </summary>
public interface IParserService public interface IParserService
{ {
const string MessageTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}";
} }

View File

@ -45,7 +45,6 @@ using AsbCloudDb.Model.WellSections;
using AsbCloudInfrastructure.Services.ProcessMaps; using AsbCloudInfrastructure.Services.ProcessMaps;
using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudInfrastructure.Services.Trajectory.Parser;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -205,8 +204,6 @@ namespace AsbCloudInfrastructure
services.AddTransient<TrajectoryPlanExportService>(); services.AddTransient<TrajectoryPlanExportService>();
services.AddTransient<TrajectoryFactManualExportService>(); services.AddTransient<TrajectoryFactManualExportService>();
services.AddTransient<TrajectoryFactNnbExportService>(); services.AddTransient<TrajectoryFactNnbExportService>();
services.AddTransient<TrajectoryPlanParserService>();
services.AddTransient<TrajectoryFactManualParserService>();
services.AddTransient<IWellOperationRepository, WellOperationRepository>(); services.AddTransient<IWellOperationRepository, WellOperationRepository>();
services.AddTransient<IDailyReportService, DailyReportService>(); services.AddTransient<IDailyReportService, DailyReportService>();
services.AddTransient<IDetectedOperationService, DetectedOperationService>(); services.AddTransient<IDetectedOperationService, DetectedOperationService>();

View File

@ -21,21 +21,68 @@ public abstract class ParserServiceBase<TDto, TOptions> : IParserService<TDto, T
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
protected virtual ICollection<ValidationResult> ValidationResults { get; } = new List<ValidationResult>();
protected abstract string SheetName { get; }
public abstract ParserResultDto<TDto> Parse(Stream file, TOptions options); public abstract ParserResultDto<TDto> Parse(Stream file, TOptions options);
public abstract Stream GetTemplateFile(); public abstract Stream GetTemplateFile();
protected virtual ValidationResultDto<TDto> ValidateRow(int rowNumber,
IDictionary<string, int> columnNumbers,
TDto dto)
{
var validationContext = new ValidationContext(dto, serviceProvider: null, items: null);
if (Validator.TryValidateObject(dto, validationContext, ValidationResults, true))
{
var validRow = new ValidationResultDto<TDto>
{
Item = dto
};
return validRow;
}
var invalidRow = new ValidationResultDto<TDto>
{
Item = dto,
};
var warnings = new List<ValidationResult>();
foreach (var validationResult in from v in ValidationResults
let memberNames = v.MemberNames.Where(columnNumbers.ContainsKey)
select memberNames.Select(x =>
{
var columnNumber = columnNumbers[x];
var errorMessage = v.ErrorMessage;
var warningMessage = string.Format(IParserService.MessageTemplate, SheetName, rowNumber, columnNumber, errorMessage);
var warning = new ValidationResult(warningMessage, new[] { x });
return warning;
}))
{
warnings.AddRange(validationResult);
}
invalidRow.Warnings = warnings;
return invalidRow;
}
protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet, protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet,
Func<IXLRow, ValidationResultDto<TDto>> parseRow, Func<IXLRow, ValidationResultDto<TDto>> parseRow,
int columnCount, int columnCount,
int headerRowsCount = 0) int headerRowsCount = 0)
{ {
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount) if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов."); throw new FileFormatException($"Лист {SheetName} содержит меньшее количество столбцов.");
var count = sheet.RowsUsed().Count() - headerRowsCount; var count = sheet.RowsUsed().Count() - headerRowsCount;
if (count > 1024) if (count > 1024)
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк."); throw new FileFormatException($"Лист {SheetName} содержит слишком большое количество строк.");
if (count <= 0) if (count <= 0)
return new ParserResultDto<TDto>(); return new ParserResultDto<TDto>();

View File

@ -3,23 +3,30 @@ using System.Collections.Generic;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
using AsbCloudInfrastructure.Services.Trajectory.Parser; using AsbCloudInfrastructure.Services.Trajectory.Parser;
using Microsoft.Extensions.DependencyInjection;
namespace AsbCloudInfrastructure.Services; namespace AsbCloudInfrastructure.Services;
public class ParserServiceFactory public class ParserServiceFactory : IDisposable
{ {
public const int IdTrajectoryFactManualParserService = 1; public const int IdTrajectoryFactManualParser = 1;
public const int IdTrajectoryPlanParserService = 2; public const int IdTrajectoryPlanParser = 2;
public const int IdProcessMapPlanDrillingParser = 3;
private readonly IDictionary<int, Func<IParserService>> parsers; private readonly IDictionary<int, Func<IParserService>> parsers;
private readonly IServiceScope serviceScope;
public ParserServiceFactory(IServiceProvider serviceProvider) public ParserServiceFactory(IServiceProvider serviceProvider)
{ {
serviceScope = serviceProvider.CreateScope();
parsers = new Dictionary<int, Func<IParserService>> parsers = new Dictionary<int, Func<IParserService>>
{ {
{ IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService(serviceProvider) }, { IdTrajectoryPlanParser, () => new TrajectoryPlanParser(serviceScope.ServiceProvider) },
{ IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService(serviceProvider) } { IdTrajectoryFactManualParser, () => new TrajectoryFactManualParser(serviceScope.ServiceProvider) },
{ IdProcessMapPlanDrillingParser, () => new ProcessMapPlanDrillingParser(serviceScope.ServiceProvider) }
}; };
} }
@ -33,4 +40,9 @@ public class ParserServiceFactory
return parserService.Invoke() as IParserService<TDto, TOptions> return parserService.Invoke() as IParserService<TDto, TOptions>
?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа"); ?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа");
} }
public void Dispose()
{
serviceScope.Dispose();
}
} }

View File

@ -5,12 +5,12 @@ using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto> public class TrajectoryFactManualParser : TrajectoryParser<TrajectoryGeoFactDto>
{ {
protected override string SheetName => "Фактическая траектория"; protected override string SheetName => "Фактическая траектория";
protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx"; protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx";
public TrajectoryFactManualParserService(IServiceProvider serviceProvider) public TrajectoryFactManualParser(IServiceProvider serviceProvider)
: base(serviceProvider) : base(serviceProvider)
{ {
} }

View File

@ -2,26 +2,23 @@
using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Data.Trajectory;
using ClosedXML.Excel; using ClosedXML.Excel;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public abstract class TrajectoryParserService<T> : ParserServiceBase<T, IParserOptionsRequest> public abstract class TrajectoryParser<T> : ParserServiceBase<T, IParserOptionsRequest>
where T : TrajectoryGeoDto where T : TrajectoryGeoDto
{ {
private const int HeaderRowsCount = 2; private const int HeaderRowsCount = 2;
private const int ColumnCount = 6; private const int ColumnCount = 6;
protected TrajectoryParserService(IServiceProvider serviceProvider) protected TrajectoryParser(IServiceProvider serviceProvider)
: base(serviceProvider) : base(serviceProvider)
{ {
} }
protected abstract string SheetName { get; }
protected abstract string TemplateFileName { get; } protected abstract string TemplateFileName { get; }
protected abstract ValidationResultDto<T> ParseRow(IXLRow row); protected abstract ValidationResultDto<T> ParseRow(IXLRow row);

View File

@ -5,12 +5,12 @@ using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto> public class TrajectoryPlanParser : TrajectoryParser<TrajectoryGeoPlanDto>
{ {
protected override string SheetName => "Плановая траектория"; protected override string SheetName => "Плановая траектория";
protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx"; protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx";
public TrajectoryPlanParserService(IServiceProvider serviceProvider) public TrajectoryPlanParser(IServiceProvider serviceProvider)
: base(serviceProvider) : base(serviceProvider)
{ {
} }

View File

@ -2,6 +2,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using AsbCloudApp.Services;
namespace AsbCloudInfrastructure; namespace AsbCloudInfrastructure;
@ -39,8 +40,9 @@ public static class XLExtentions
} }
catch catch
{ {
throw new FileFormatException( var message = string.Format(IParserService.MessageTemplate, cell.Worksheet.Name, cell.Address.RowNumber,
$"Лист '{cell.Worksheet.Name}'. {cell.Address.RowNumber} строка содержит некорректное значение в {cell.Address.ColumnNumber} столбце"); cell.Address.ColumnNumber, "Содержит некорректное значение");
throw new FileFormatException(message);
} }
} }
} }

View File

@ -37,7 +37,7 @@ public class TrajectoryParserTest
Assert.Fail("Файла для импорта не существует"); Assert.Fail("Файла для импорта не существует");
var parserService = parserServiceFactory.Create<TrajectoryGeoPlanDto, IParserOptionsRequest>( var parserService = parserServiceFactory.Create<TrajectoryGeoPlanDto, IParserOptionsRequest>(
ParserServiceFactory.IdTrajectoryPlanParserService); ParserServiceFactory.IdTrajectoryPlanParser);
var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty());
@ -54,7 +54,7 @@ public class TrajectoryParserTest
Assert.Fail("Файла для импорта не существует"); Assert.Fail("Файла для импорта не существует");
var parserService = parserServiceFactory.Create<TrajectoryGeoFactDto, IParserOptionsRequest>( var parserService = parserServiceFactory.Create<TrajectoryGeoFactDto, IParserOptionsRequest>(
ParserServiceFactory.IdTrajectoryFactManualParserService); ParserServiceFactory.IdTrajectoryFactManualParser);
var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty());

View File

@ -24,7 +24,7 @@ public class TrajectoryFactManualController : TrajectoryEditableController<Traje
parserServiceFactory, parserServiceFactory,
trajectoryExportService, trajectoryExportService,
trajectoryRepository, trajectoryRepository,
ParserServiceFactory.IdTrajectoryFactManualParserService) ParserServiceFactory.IdTrajectoryFactManualParser)
{ {
} }
} }

View File

@ -31,7 +31,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
parserServiceFactory, parserServiceFactory,
trajectoryExportService, trajectoryExportService,
trajectoryRepository, trajectoryRepository,
ParserServiceFactory.IdTrajectoryPlanParserService) ParserServiceFactory.IdTrajectoryPlanParser)
{ {
this.trajectoryVisualizationService = trajectoryVisualizationService; this.trajectoryVisualizationService = trajectoryVisualizationService;
} }