I am working on a razorpages web program which will collect input data for a simulation, run the simulation, and display the results to the user. Several of the outputs of the simulation (e.g. cash flow) are returned as an IEnumerable representing a time series. The report will display these values in a table, as follows:
<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>
@for (int year = 0; year <= Model.InputData.SystemLifetime; year++)
{
<tr>
<td>
@year
</td>
<td>
@Html.DisplayFor(model => model.OutputData.Production[year])
</td>
<td>
@Html.DisplayFor(model => model.OutputData.CashFlow[year])
</td>
</tr>
}
</tbody>
</table>
The relevant parts of the OutputData class look like:
public class OutputData
{
public double[] Production { get; set; }
public double[] CashFlow { get; set; }
public OutputData (InputData inputData)
{
ISimulation mySim = SimulationBuilder.BuildSim (inputData) // set up the simulation using the inputs provided by the user.
Production = mySim.LifetimeProduction().ToArray();
CashFlow = mySim.LifetimeCashFlows().ToArray();
}
}
All of this works, but the data is unformatted. I would like to use data annotation to format the production and cashflow values.
I tried adding the following data annotations to the property definitions:
[DisplayName("Annual Production")]
[DisplayFormat(DataFormatString = "{0:F2}")]
public double[] Production { get; set; }
[DisplayName("Cash Flow")]
[DisplayFormat(DataFormatString = "{0:C2}")]
public double[] CashFlow { get; set; }
(call this Solution A)
This compiles but, as expected, doesn't actually format the data: the data annotations are being applied to the arrays OutputData.Production and Output.CashFlow rather than to their individual items.
I found the following alternative, involving creating custom structs to hold each 'Production' and 'Cash Flow' value, which works but is very ugly and feels overly complicated:
public class OutputData
{
[DisplayName("Annual Production")]
public ProductionValue[] Production { get; set; }
public struct ProductionValue
{
[DisplayFormat(DataFormatString = "{0:F2}")]
public double Value { get; init; }
public ProductionValue (double value) { Value = value; }
public ProductionValue() { }
}
[DisplayName("Cash Flow")]
public CashFlowValue[] CashFlow{ get; set; }
public struct CashFlowValue
{
[DisplayFormat(DataFormatString = "{0:C2}")]
public double Value { get; init; }
public CashFlowValue(double value) { Value = value; }
public CashFlowValue() { }
}
public OutputData (InputData inputData)
{
ISimulation mySim = SimulationBuilder.BuildSim (inputData) // set up the simulation using the inputs provided by the user.
Production = mySim.LifetimeProduction()
.Select(item => new ProductionValue(item))
.ToArray();
CashFlow = mySim.LifetimeCashFlows()
.Select(item => new CashFlowValue(item))
.ToArray();
}
}
(note that I also have to change Report.cshtml to bind to the correct property)
(call this Solution B)
Is there a way to do this that looks more like Solution A?