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

如何不新增布尔类型自定义特性,基于条件访问类属性生成Excel报表表头

方案1:独立报表字段配置(最推荐,完全解耦实体定义与报表规则)

不需要在特性或实体类中添加任何和报表归属相关的标识,单独为每类报表维护属性路径配置,完全不会污染实体层代码:

// 报表配置类,和实体层完全分离
public static class ReportConfigs
{
    // 报表1要展示的嵌套属性路径
    public static readonly List<string> Report1IncludedProperties = new()
    {
        "SpaceIdentity.Name",
        "SpaceIdentity.RoomNumber",
        "SpaceIdentity.RoomName",
        "SpaceGeometry.FloorArea"
    };

    // 报表2要展示的嵌套属性路径
    public static readonly List<string> Report2IncludedProperties = new()
    {
        "SpaceIdentity.Number",
        "SpaceIdentity.Name"
    };
}

改造原有递归方法,拼接属性路径和配置匹配即可:

public static void GetReportHeaderColumnName(Type type, OrderedDictionary headerNameByUnit, List<string> includedProperties, string parentPath = "")
{
    var properties = type.GetProperties();
    foreach (var propertyInfo in properties)
    {
        string currentPath = string.IsNullOrEmpty(parentPath) ? propertyInfo.Name : $"{parentPath}.{propertyInfo.Name}";
        // 匹配到配置直接添加列
        if (includedProperties.Contains(currentPath))
        {
            AddReportHeaderName(headerNameByUnit, propertyInfo);
        }
        // 嵌套类递归处理
        if (propertyInfo.PropertyType.IsClass && !propertyInfo.PropertyType.FullName.StartsWith("System."))
        {
            if (propertyInfo.PropertyType == typeof(Overridable<double>))
            {
                continue;
            }
            GetReportHeaderColumnName(propertyInfo.PropertyType, headerNameByUnit, includedProperties, currentPath);
        }
    }
}

// 调用时传入对应报表的配置即可
var columnResult = new OrderedDictionary();
GetReportHeaderColumnName(typeof(Space), columnResult, ReportConfigs.Report1IncludedProperties);

新增报表时只需新增对应的配置列表,不需要修改任何实体类、特性的代码。


方案2:特性标签匹配模式(适合希望把规则存在特性里的场景)

不用在特性中定义大量零散的IsIncludedInReportX布尔字段,改造特性支持多标签匹配:

// 改造后的自定义特性
public class DisplayNameWithUnitsAttribute : Attribute
{
    public string DisplayName { get; set; }
    public string Units { get; set; }
    // 用标签数组替代布尔字段,支持匹配任意多报表
    public string[] IncludeInReports { get; set; }
}

属性标注示例:

[DisplayNameWithUnits(DisplayName = "Space Name", IncludeInReports = new[]{"Report1","Report2"})]
public string Name { get; set; }

[DisplayNameWithUnits(DisplayName = "Room Number", IncludeInReports = new[]{"Report1"})]
public string RoomNumber { get; set; }

筛选时只需判断当前报表名是否在特性的标签数组中即可,新增报表不需要修改特性的定义,只需给对应属性加标签即可。


方案3:Fluent强类型配置(无硬编码,编译时校验)

如果不想用硬编码的属性字符串,可以用表达式树实现强类型配置,修改属性名时编译器会自动校验错误:

public class ReportConfig<T>
{
    public List<Expression<Func<T, object>>> IncludedProperties { get; } = new();

    public ReportConfig<T> AddProperty<TProp>(Expression<Func<T, TProp>> propertyExpr)
    {
        IncludedProperties.Add(propertyExpr);
        return this;
    }
}

// 初始化报表1的强类型配置
public static readonly ReportConfig<Space> Report1Config = new ReportConfig<Space>()
    .AddProperty(s => s.SpaceIdentity.Name)
    .AddProperty(s => s.SpaceIdentity.RoomNumber)
    .AddProperty(s => s.SpaceGeometry.FloorArea);

后续可以通过辅助方法从表达式树解析出对应的PropertyInfo信息,避免硬编码字符串的拼写错误。


内容的提问来源于stack exchange,提问作者Glory Raj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 03:27:01