You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 06:32:10