Blazor中CSVHelper泛型组件:如何传入Type Map
问题场景
基于.NET 7开发服务器端Blazor业务线(LOB)应用,每个列表页都设有CSV下载按钮。为避免重复代码,封装了泛型组件,通过类型参数<T>传入待导出的POCO/EF实体类,目前下载功能正常。但多数实体类有对应的Map类,不清楚如何在Blazor组件中处理多泛型类型(如<T>和<U>),无法同时传入实体类与对应的Map类。试过传入Map类名称字符串,但不知道怎么转成实际类型调用RegisterClassMap;也考虑过定义基类,但泛型定义仍不清晰。
解决方案
方案1:使用多泛型参数的Blazor组件
Blazor组件支持多个泛型参数,直接声明<T, U>,并约束U继承自ClassMap<T>,就能直接调用泛型版本的RegisterClassMap<U>()。
修改后的组件代码:
@using BlazorDownloadFile @using CsvHelper.Configuration @typeparam T @typeparam U where U : ClassMap<T> <MudButton Variant="@(_processing ? Variant.Filled : Variant.Outlined )" DisableElevation="true" Size="Size.Small" Color="Color.Primary" StartIcon="@(_processing ? "" : Icons.Filled.BrowserUpdated)" OnClick="@ExportToCSV" Disabled="@_processing" Class="ma-2 mt-8"> @if (_processing) { <MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" /> <span> Preparing...</span> } else { <span>Export CSV</span> } </MudButton> @code { private bool _processing = false; [Inject] public IBlazorDownloadFileService? BlazorDownloadFileService { get; set; } [Parameter] public string FileNameBase { get; set; } = ""; [Parameter] public Func<Task<List<T>>>? OnListRequest { get; set; } public List<T> ListItems { get; set; } = new(); private string _csv = ""; private string MakeCsvString(List<T> items) { using (var writer = new StringWriter()) using (var csv1 = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv1.Context.RegisterClassMap<U>(); csv1.WriteRecords(items); return writer.ToString(); } } private async Task ExportToCSV() { _processing = true; StateHasChanged(); ListItems = await OnListRequest(); _csv = MakeCsvString(ListItems); string filename = $"{FileNameBase.IfNullOrWhiteSpace("Download")}-{DateTime.Now:yyyyMMdd-HHmm}.csv"; await BlazorDownloadFileService.DownloadFileFromText(filename, _csv, System.Text.Encoding.UTF8, "text/csv"); await Task.Yield(); _processing = false; StateHasChanged(); } }
使用组件时直接指定两个泛型参数:
<ExportCsvButton T="YourEntity" U="YourEntityMap" FileNameBase="YourData" OnListRequest="@GetYourDataList" />
方案2:通过参数传入Map类型(反射方式)
如果不想用多泛型参数,可以传入Type类型的参数,通过反射调用非泛型的RegisterClassMap方法:
修改组件参数和MakeCsvString方法:
@using BlazorDownloadFile @typeparam T <!-- 按钮部分代码不变 --> @code { // 保留原有参数,新增Map类型参数 [Inject] public IBlazorDownloadFileService? BlazorDownloadFileService { get; set; } [Parameter] public string FileNameBase { get; set; } = ""; [Parameter] public Type? CsvMapType { get; set; } [Parameter] public Func<Task<List<T>>>? OnListRequest { get; set; } public List<T> ListItems { get; set; } = new(); private string _csv = ""; private string MakeCsvString(List<T> items) { using (var writer = new StringWriter()) using (var csv1 = new CsvWriter(writer, CultureInfo.InvariantCulture)) { if (CsvMapType != null) { csv1.Context.RegisterClassMap(CsvMapType); } csv1.WriteRecords(items); return writer.ToString(); } } // ExportToCSV方法代码不变 }
使用组件时传入Map类型:
<ExportCsvButton T="YourEntity" CsvMapType="@typeof(YourEntityMap)" FileNameBase="YourData" OnListRequest="@GetYourDataList" />
方案3:自动关联实体与Map类(约定优于配置)
如果实体和Map类遵循命名约定(比如YourEntity对应YourEntityMap),可以在组件中通过反射自动查找对应的Map类,无需手动传入:
修改MakeCsvString方法:
private string MakeCsvString(List<T> items) { using (var writer = new StringWriter()) using (var csv1 = new CsvWriter(writer, CultureInfo.InvariantCulture)) { // 按命名约定查找Map类 var entityType = typeof(T); var mapType = entityType.Assembly.GetType($"{entityType.Namespace}.{entityType.Name}Map"); if (mapType != null) { csv1.Context.RegisterClassMap(mapType); } csv1.WriteRecords(items); return writer.ToString(); } }
这种方式无需额外传入参数,组件会自动匹配对应的Map类,适合遵循统一命名规范的场景。
内容的提问来源于stack exchange,提问作者bph
相关产品推荐
相关产品推荐

