【问题标题】:CsvHelper with non-default constructor具有非默认构造函数的 CsvHelper
【发布时间】:2021-06-26 14:13:11
【问题描述】:

我的问题如下。我想借助 WPF c# 下的 CsvHelper 将类属性写入 CSV 文件,然后再将它们读回。引用的类之一具有非默认的参数化构造函数。如何让 CsvHelper 正确调用其构造函数?

因此我使用以下地图文件。

public class TradeLogRecMap : ClassMap<TradeLogRec>
{
    public TradeLogRecMap()
    {
        AutoMap(CSVConfig);
    }
}

使用这些类和结构

public class Liquidity
{
    static readonly Dictionary<int, string> Values = new Dictionary<int, string> 
    { 
        {0, "None"}, 
        {1, "Added Liquidity"}, 
        {2, "Removed Liquidity"}, 
        {3, "Liquidity Routed Out" }
    };

    public Liquidity(int p)
    {
        Value = Values.ContainsKey(p) ? p : 0;
    }

    public int Value { get; set; }

    public override string ToString()
    {
        return Values[Value];
    }
}

public class Execution
{
    public Liquidity LastLiquidity { get; set; }

    public Execution()
    {
        LastLiquidity = new Liquidity(0);
    }

}

public struct TradeLogRec
{
    public Execution execution { set; get; }
}

当我编写 CSV 文件时,它看起来还不错。但是读回来它说它错过了标题“p”。向 CSV 文件添加“p”标头可以解决此问题,但这样做不是一种选择。还添加了一个默认构造函数

    public Liquidity()
    {
    }

解决问题。但这不是以太的选择。

为什么它需要标题“p”?

是否可以通过map文件或CvsHelper配置中的一些构造函数设置来解决?

或者它是一个错误?

【问题讨论】:

  • 你能以任何方式修改Liquidity吗?如果将 p 重命名为 Value,则 CsvHelper 将调用具有正确单元格值的构造函数,即 public Liquidity(int Value) =&gt; this.Value = Values.ContainsKey(Value) ? Value : 0;。请参阅:dotnetfiddle.net/eyeWsq
  • 感谢您的指出!另请参阅我在答案中的评论。

标签: c# csv constructor csvhelper


【解决方案1】:

从 CsvHelper 27.1.1 开始,当 CsvHelper 在读取过程中尝试构造类时,它将使用无参数构造函数(如果存在)。如果不存在,它将使用带有 most parameters [1] 的参数化构造函数。当调用参数化构造函数时,CSV 列将与构造函数参数匹配通过参数名称的精确匹配。在您的 Liquidity 类中,您要映射到“值”列的参数名为 p,因此无法进行匹配。

那么,你有什么选择?

首先,如果您可以修改 Liquidity 类,您可以将 p 构造函数参数重命名为 Value

public Liquidity(int Value)
{
    // Make the constructor parameter name match the property name exactly for CsvHelper
    this.Value = Values.ContainsKey(Value) ? Value : 0;
}

这样做之后,一切都会正常进行。演示小提琴 #1 here.

其次,您可以修改Liquidity,将[CsvHelper.Configuration.Attributes.Name("Value")]添加到p

public Liquidity([CsvHelper.Configuration.Attributes.Name("Value")] int p)
{
    Value = Values.ContainsKey(p) ? p : 0;
}

演示小提琴 #2 here.

第三,如果您无法以任何方式修改您的类,您可以覆盖AutoMap 生成的默认引用映射,并为Liquidity 提供您自己的映射。

创建以下ClassMap&lt;Liquidity&gt;

public class LiquidityMap : ClassMap<Liquidity>
{
    public LiquidityMap()
    {
        Map(m => m.Value);
        Parameter("p").Name(nameof(Liquidity.Value));
    }
}   

并修改TradeLogRecMap使用如下:

public class TradeLogRecMap : ClassMap<TradeLogRec>
{
    public TradeLogRecMap()
    {
        // Automap TradeLogRecMap
        AutoMap(CSVConfig);  // CSVConfig was not shown in your question, I used new CsvConfiguration(CultureInfo.InvariantCulture) { }
        // Get the reference map for Execution
        var executionRefMap = ReferenceMaps.Find<TradeLogRec>(m => m.execution);
        // Get its reference map for LastLiquidity
        var liquidityRefMap = executionRefMap.Data.Mapping.ReferenceMaps.Find<Execution>(m => m.LastLiquidity);
        // Remove the auto-generated reference map for LastLiquidity
        executionRefMap.Data.Mapping.ReferenceMaps.Remove(liquidityRefMap);
        // And add a reference map for LastLiquidity using LiquidityMap
        executionRefMap.Data.Mapping.ReferenceMaps.Add(new MemberReferenceMap(liquidityRefMap.Data.Member, new LiquidityMap()));
    }
}   

演示小提琴#3 here.

最后,您可以修改CsvConfiguration.PrepareHeaderForMatch 以自动将任何名为“p”的列重新映射为“Value”:

public static CsvConfiguration CSVConfig => 
    new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        PrepareHeaderForMatch = a => a.Header == "p" ? nameof(Liquidity.Value) : a.Header,
    };

然后在构造你的CsvReader时使用它:

public static List<TradeLogRec> DeserializeTradeLogRecMapFromCsv(TextReader reader)
{
    using (var csv = new CsvReader(reader, CSVConfig))
    {
        csv.Context.RegisterClassMap<TradeLogRecMap>();
        return csv.GetRecords<TradeLogRec>().ToList();
    }           
}

这似乎有点笨拙,因为它将名为“Value”的所有列映射到“p”,而不仅仅是Liquidity的列-但它确实有效。演示小提琴 #4 here.


[1] 此行为是通过CsvConfiguration.ShouldUseConstructorParameterCsvConfiguration.GetConstructor 配置的。

【讨论】:

  • 非常感谢您的快速帮助!它正在工作。我不想接触 Liquidity 和 Execution 类的原因是因为它们来自第三方库。即使忽略 Map(m => m.execution.LastLiquidity.Value).Ignore();没用。它总是缺少“p”。我很高兴你有这么好的解决方案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
  • 1970-01-01
  • 1970-01-01
  • 2018-10-28
  • 2017-09-27
  • 1970-01-01
  • 2015-01-28
相关资源
最近更新 更多