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

如何优雅地为Razor Pages数组类型模型属性应用数据注解?

优雅实现方案

方法3:复用方案A的注解,仅修改视图(最接近需求)

这种方式完全保留OutputData的数组结构和方案A的注解,只在视图中做少量调整,直接从注解中读取格式字符串来格式化元素:

  1. 保留方案A的OutputData注解:
[DisplayName("Annual Production")]
[DisplayFormat(DataFormatString = "{0:F2}")]
public double[] Production { get; set; }
[DisplayName("Cash Flow")]
[DisplayFormat(DataFormatString = "{0:C2}")]
public double[] CashFlow { get; set; }
  1. 修改视图代码,获取属性的格式字符串并应用到数组元素:
@using System.ComponentModel.DataAnnotations;
<table class="table">
    <thead>
        <tr>
            <th>Year</th>
            <th>@Html.DisplayNameFor(model => model.OutputData.Production)</th>
            <th>@Html.DisplayNameFor(model => model.OutputData.CashFlow)</th>
        </tr>
    </thead>
    <tbody>
        @{
            // 从属性的元数据中读取格式字符串
            var productionFormat = ModelMetadata.FromLambdaExpression(model => model.OutputData.Production, ViewData).DisplayFormatString;
            var cashFlowFormat = ModelMetadata.FromLambdaExpression(model => model.OutputData.CashFlow, ViewData).DisplayFormatString;
        }
        @for (int year = 0; year <= Model.InputData.SystemLifetime; year++)
        {
            <tr>
                <td>@year</td>
                <td>@string.Format(productionFormat, Model.OutputData.Production[year])</td>
                <td>@string.Format(cashFlowFormat, Model.OutputData.CashFlow[year])</td>
            </tr>
        }
    </tbody>
</table>

优点:不用修改业务逻辑层的代码,完全复用方案A的注解思路,仅视图层做适配,代码量少且符合原有设计。


方法1:通用数值包装类(减少重复代码)

如果不想在视图中写逻辑,可以用通用包装类替代方案B中重复的结构体,避免冗余代码:

  1. 定义通用包装类和元数据类:
// 通用数值包装基类
public class FormattedNumber<T> where T : struct, IFormattable
{
    public T Value { get; init; }

    public FormattedNumber(T value) => Value = value;
}

// 产量数值类型+元数据
[ModelMetadataType(typeof(ProductionMetadata))]
public class ProductionNumber : FormattedNumber<double>
{
    public ProductionNumber(double value) : base(value) { }
}

public class ProductionMetadata
{
    [DisplayFormat(DataFormatString = "{0:F2}")]
    public double Value { get; init; }
}

// 现金流数值类型+元数据
[ModelMetadataType(typeof(CashFlowMetadata))]
public class CashFlowNumber : FormattedNumber<double>
{
    public CashFlowNumber(double value) : base(value) { }
}

public class CashFlowMetadata
{
    [DisplayFormat(DataFormatString = "{0:C2}")]
    public double Value { get; init; }
}
  1. 修改OutputData类:
public class OutputData
{
    [DisplayName("Annual Production")]
    public ProductionNumber[] Production { get; set; }

    [DisplayName("Cash Flow")]
    public CashFlowNumber[] CashFlow { get; set; }

    public OutputData(InputData inputData)
    {
        ISimulation mySim = SimulationBuilder.BuildSim(inputData);

        Production = mySim.LifetimeProduction()
              .Select(item => new ProductionNumber(item))
              .ToArray();
        CashFlow = mySim.LifetimeCashFlows()
              .Select(item => new CashFlowNumber(item))
              .ToArray();
    }
}
  1. 修改视图绑定到Value属性:
<td>
    @Html.DisplayFor(model => model.OutputData.Production[year].Value)
</td>
<td>
    @Html.DisplayFor(model => model.OutputData.CashFlow[year].Value)
</td>

优点:统一管理格式化逻辑,避免方案B中重复定义结构体的繁琐,后续新增数值类型只需添加新的子类和元数据。


方法2:自定义显示模板(视图层解耦)

如果想把格式化逻辑放在视图层,且不修改业务代码,可以创建针对不同数值类型的显示模板:

  1. 在Views/Shared/DisplayTemplates目录下创建两个模板文件:
  • Production.cshtml:
@model double
@string.Format("{0:F2}", Model)
  • CashFlow.cshtml:
@model double
@string.Format("{0:C2}", Model)
  1. 保留方案A的OutputData注解:
[DisplayName("Annual Production")]
public double[] Production { get; set; }
[DisplayName("Cash Flow")]
public double[] CashFlow { get; set; }
  1. 修改视图指定模板名称:
<td>
    @Html.DisplayFor(model => model.OutputData.Production[year], "Production")
</td>
<td>
    @Html.DisplayFor(model => model.OutputData.CashFlow[year], "CashFlow")
</td>

优点:格式化逻辑完全放在视图层,业务层代码不受影响,模板可复用在其他视图。


内容的提问来源于stack exchange,提问作者Heather C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:51:38