【问题标题】:Proper organization and transformation of objects对象的适当组织和转换
【发布时间】:2020-02-07 08:41:18
【问题描述】:

我有一个复杂的 SQL 查询,它返回如下表:

我用这个对象描述了它 - IEnumerable<Invoice> 其中:

public partial class Invoice
{
    public string DocumentNumber { get; set; }
    public DateTime? DocumentDate { get; set; }
    public string DocumentReference { get; set; }
    public string SerialNumber { get; set; }
    public string ProductCode { get; set; }
    public string Description { get; set; }
    public string Certificate { get; set; }
}

这些对象的集合的特点是它们按前三个字段分组(如果它们相同),其余字段不同。

我的任务是实现这些数据的分层表示,它应该看起来像这样:

我自己决定,我的最终视图对象应该具有以下结构 - Dictionary<InvoiceHeader, List<InvoiceHierarchi>> 其中:

public class InvoiceHeader
{
   public string DocumentNumber { get; set; }
   public DateTime? DocumentDate { get; set; }
   public string DocumentReference { get; set; }
}

public class InvoiceHierarchi
{
   public string SerialNumber { get; set; }
   public string ProductCode { get; set; }
   public string Description { get; set; }
   public string Certificate { get; set; }
}

问题#1:我能否编写将IEnumerable<Invoice> 转换为Dictionary<InvoiceHeader, List<InvoiceHierarchi>> 的LINQ 查询以及如何正确执行?

问题 #2:总的来说,我是否选择了正确的方法来解决我的任务?

【问题讨论】:

  • 我可能只有一个 List - 其中 Invoice 包含一个 List 或类似的东西。然后,您可以更轻松地在前端进行过滤/排序,而不是使用字典。试试谷歌订单/订单线。听起来很像一个类似的问题:)

标签: c# linq dictionary ienumerable


【解决方案1】:

我可以编写将IEnumerable<Invoice> 转换为的LINQ 查询吗? Dictionary<InvoiceHeader, List<InvoiceHierarchi>> 以及如何操作 对吧?

是的,你可以。尝试通过InvoiceHeader 分组发票,并使用ToDictionary 方法转换结果

var invoices = new List<Invoice>();
var result = invoices.GroupBy(i => new InvoiceHeader
{
    DocumentDate = i.DocumentDate,
    DocumentNumber = i.DocumentNumber,
    DocumentReference = i.DocumentReference
}).ToDictionary(g => g.Key, g => g.Select(i => new InvoiceHierarchi
{
    Certificate = i.Certificate,
    SerialNumber = i.SerialNumber,
    ProductCode = i.ProductCode,
    Description = i.Description
}).ToList());

另一种选择是使用ToLookup 方法

var anotherResult = invoices.ToLookup(i => new InvoiceHeader
    {
        DocumentDate = i.DocumentDate,
        DocumentNumber = i.DocumentNumber,
        DocumentReference = i.DocumentReference
    },
    i => new InvoiceHierarchi
    {
        Certificate = i.Certificate,
        SerialNumber = i.SerialNumber,
        ProductCode = i.ProductCode,
        Description = i.Description
    });

要正确使用InvoiceHeader 作为Dictionary 键,您应该覆盖该类的GetHashCode()Equals() 方法,或implelement IEquatable&lt;T&gt; 接口

public class InvoiceHeader : IEquatable<InvoiceHeader>
{
    public string DocumentNumber { get; set; }
    public DateTime? DocumentDate { get; set; }
    public string DocumentReference { get; set; }

    public override int GetHashCode()
    {
        return DocumentNumber.GetHashCode() ^ DocumentDate.GetHashCode() ^ DocumentReference.GetHashCode();
    }

    public bool Equals(InvoiceHeader other)
    {
        if (ReferenceEquals(null, other))
            return false;
        if (ReferenceEquals(this, other))
            return true;

        return DocumentNumber == other.DocumentNumber && 
               Nullable.Equals(DocumentDate, other.DocumentDate) &&
               DocumentReference == other.DocumentReference;
    }

    public override bool Equals(object? obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((InvoiceHeader)obj);
    }
}

您会收到警告,GetHashCode() 中使用了非只读属性,因为不建议将GetHashCode() 与可变对象一起使用,因为对象的状态可以更改,但哈希值保持不变

对于不可变引用类型,您可以覆盖 GetHashCode()。在 一般来说,对于可变引用类型,您应该覆盖 GetHashCode() 仅当:

您可以从不可变的字段中计算哈希码;

可以确保可变对象的哈希码 当对象包含在一个集合中时不会改变 依赖于它的哈希码。

否则,您可能会认为可变对象在哈希表中丢失了。

因此,最好将InvoiceHeader 的任何(或所有)属性设为只读,从构造函数初始化它们并用于获取哈希码

【讨论】:

  • 谢谢你,你的回答对我来说很有价值,但结果我得到了List&lt;Dictionary&lt;InvoiceHeader, List&lt;InvoiceHierarchi&gt;&gt;&gt;
  • 您的源数据IEnumerable&lt;Invoice&gt; 是如何声明的,您是否对其进行了分组? ToDictionary 方法返回Dictionary&lt;InvoiceHeader, List&lt;InvoiceHierarchi&gt;&gt;,它不能返回字典列表。 ToList() 调用用于将 Select 结果评估为 List&lt;InvoiceHierarchi&gt; 以获取字典值,而不是字典本身。也许你错过了一些大括号?
  • 是的,你没看错,我的数据源是IEnumerable&lt;Invoice&gt;。目的地应该是Dictionary&lt;InvoiceHeader, List&lt;InvoiceHierarchi&gt;&gt;。实际上,源中的每个组(部分数据包括字段:DocumentDate, DocumentNumber, DocumentReference)必须呈现一个key,而其余数据 - vale(是一个集合)。我只是想知道是否可以使用 LINQ 执行此操作,或者我是否需要编写处理列表。
  • @РоманТимохов 第一个代码示例返回预期结果,InvoiceHeader 是字典中的键,List&lt;InvoiceHierarchi&gt; 是值。 ToLookup() 返回相同的结构
  • 让我们试着看一下第一张图片中的例子。第1、2、3行的块应该表示为一个对象Dictionary&lt;InvoiceHeader, List&lt;InvoiceHierarchi&gt;&gt;(由于字段DocumentDate, DocumentNumber, DocumentReference相同,所以必须提交一个key),右边的块是value,形式为@ 987654363@。由于处理第 1、2、3 行(在第一张图片中),我得到一个对象 Dictionary&lt;InvoiceHeader, List&lt;InvoiceHierarchi&gt;&gt;,其中 value.Count 将 = 3。您的答案将返回三个对象 Dictionary&lt;InvoiceHeader, List&lt;InvoiceHierarchi&gt;&gt;
猜你喜欢
  • 1970-01-01
  • 2019-05-07
  • 1970-01-01
  • 2014-07-13
  • 2013-01-04
  • 2016-05-12
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
相关资源
最近更新 更多