如何向实现ITypeConverter的CustomDecimalConverter类注入服务类?
解决思路与实现步骤
1. 给转换器添加构造函数注入
修改CustomDecimalConverter,通过构造函数接收所需服务,避免硬编码实例化:
public class CustomDecimalConverter : ITypeConverter { private readonly IDecimalRangeService _rangeService; // 构造函数注入服务 public CustomDecimalConverter(IDecimalRangeService rangeService) { _rangeService = rangeService; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var inputStr = value?.ToString().Trim(); if (string.IsNullOrEmpty(inputStr)) { // 按需处理空值,返回null或默认decimal值 return default(decimal); } // 处理OVR+/-标记 if (inputStr.Equals("OVR+", StringComparison.OrdinalIgnoreCase)) { // 通过parameter传递目标列的decimal类型定义,如"decimal(5,1)" return _rangeService.GetMaxValue(parameter.ToString()); } else if (inputStr.Equals("OVR-", StringComparison.OrdinalIgnoreCase)) { return _rangeService.GetMinValue(parameter.ToString()); } // 正常转换小数 if (decimal.TryParse(inputStr, NumberStyles.Any, culture, out var result)) { return result; } throw new InvalidOperationException($"无法转换值 {inputStr} 为decimal类型"); } }
2. 实现范围计算服务
创建服务类处理decimal(x,y)格式的最大/最小值计算:
public interface IDecimalRangeService { decimal GetMaxValue(string decimalTypeDefinition); decimal GetMinValue(string decimalTypeDefinition); } public class DecimalRangeService : IDecimalRangeService { public decimal GetMaxValue(string decimalTypeDefinition) { // 解析decimal(x,y)格式字符串 var regexMatch = Regex.Match(decimalTypeDefinition, @"decimal\((\d+),(\d+)\)", RegexOptions.IgnoreCase); if (!regexMatch.Success) { throw new ArgumentException($"无效的decimal类型定义: {decimalTypeDefinition}"); } int totalDigits = int.Parse(regexMatch.Groups[1].Value); int decimalPlaces = int.Parse(regexMatch.Groups[2].Value); int integerDigits = totalDigits - decimalPlaces; // 拼接最大数值字符串 string integerPart = new string('9', integerDigits); string decimalPart = new string('9', decimalPlaces); return decimal.Parse($"{integerPart}.{decimalPart}", CultureInfo.InvariantCulture); } public decimal GetMinValue(string decimalTypeDefinition) { // 最小值为最大值的负数 return -GetMaxValue(decimalTypeDefinition); } }
3. 配置框架使用依赖注入创建转换器
不同框架的配置方式略有差异,以下是常见场景的示例:
场景一:使用CsvHelper处理CSV
自定义TypeConverterFactory从DI容器获取转换器实例:
public class DiTypeConverterFactory : ITypeConverterFactory { private readonly IServiceProvider _serviceProvider; public DiTypeConverterFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public ITypeConverter GetConverter(Type type) { // 针对decimal类型使用自定义转换器 if (type == typeof(decimal)) { return _serviceProvider.GetRequiredService<CustomDecimalConverter>(); } // 其他类型使用默认转换器 return TypeConverterFactory.Default.GetConverter(type); } }
注册服务并使用:
// 在Program.cs/Startup.cs中注册服务 services.AddScoped<IDecimalRangeService, DecimalRangeService>(); services.AddScoped<CustomDecimalConverter>(); // 初始化CsvReader时配置自定义工厂 var config = new CsvConfiguration(CultureInfo.InvariantCulture) { TypeConverterFactory = new DiTypeConverterFactory(serviceProvider) }; using var reader = new StreamReader("your-data.csv"); using var csv = new CsvReader(reader, config); // 映射时传递decimal类型参数 csv.Context.RegisterClassMap<YourCsvMap>(); public class YourCsvMap : ClassMap<YourCsvModel> { public YourCsvMap() { Map(m => m.TargetDecimalColumn) .ConvertUsing<CustomDecimalConverter>("decimal(5,1)"); } }
场景二:使用AutoMapper映射到数据库实体
配置AutoMapper从DI容器构造转换器:
// 注册服务 services.AddScoped<IDecimalRangeService, DecimalRangeService>(); services.AddScoped<CustomDecimalConverter>(); services.AddAutoMapper(cfg => { // 让AutoMapper通过DI创建转换器实例 cfg.ConstructServicesUsing(type => serviceProvider.GetService(type)); cfg.CreateMap<CsvRowModel, DbEntity>() .ForMember(dest => dest.TargetDecimalColumn, opt => opt.ConvertUsing<CustomDecimalConverter, string>("decimal(5,1)")); });
4. 补充说明
- 若需自动获取实体列的decimal类型定义,可在服务中添加反射逻辑,读取EF Core的
ColumnAttribute的TypeName属性,无需手动传递参数。 - 根据业务需求调整异常处理逻辑,比如对无效输入返回默认值而非抛出异常。
内容的提问来源于stack exchange,提问作者kidbabic
相关产品推荐
相关产品推荐

