如何不新增布尔类型自定义特性,基于条件访问类属性生成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
相关产品推荐
相关产品推荐

