c#折叠并展平对象列表中嵌套付款的列表

时间:2018-04-24 16:43:49

标签: c# arrays json linq sorting

我有一个(简化的)付款数据库架构:

ID /姓名/付款/ ParentPaymentId

当付款失败并且我“重试”时,我会创建新的付款并将其父级设置为先前“失败”的付款。这意味着我可以看到每笔付款的历史记录和之前的尝试。如果后续付款失败,则会创建另一笔付款,并再次将ParentPaymentId设置为上次尝试付款(依此类推)

当试图显示付款清单(一些成功/一些需要n次重试)时,我想查看最“当前”的付款,子付款需要在一个次要的heirachy中。

e.g。 数据库

ID / Name / Payment / ParentPaymentId
1    Jan     100          NULL
2    Feb     100          NULL
3    Mar     100          NULL
4    Mar(r)  100          3
5    Mar(r)  100          4
6    Mar(r)  100          5
7    Mar(r)  100          6
8    Apr     100          NULL

从上面你可以看到,ID 7是最近尝试向March付款计费的最近尝试

我如何获得此列表<>数组(已经在EF c#中)就像这样的对象图。

[  
   {  
      "Id":"1",
      "Name":"Jan",
      "Payment":"100"
   },
   {  
      "Id":"2",
      "Name":"Feb",
      "Payment":"100"
   },
   {  
      "Id":"7",
      "Name":"Mar(r)",
      "Payment":"100",
      "Payments":[  
         {  
            "Id":"6",
            "Name":"Mar(r)",
            "Payment":"100"
         },
         {  
            "Id":"5",
            "Name":"Mar(r)",
            "Payment":"100"
         },
         {  
            "Id":"4",
            "Name":"Mar(r)",
            "Payment":"100"
         },
         {  
            "Id":"3",
            "Name":"Mar",
            "Payment":"100"
         }
      ]
   },
   {  
      "Id":"8",
      "Name":"Apr",
      "Payment":"100"
   }
]

3 个答案:

答案 0 :(得分:0)

使用您的示例,您有这些付款(最后一个数字是父付款。-1表示没有父母):

1,Jan,100,-1
2,Feb,100,-1
3,Mar(r),100,-1
4,Mar(r),100,3
5,Mar(r),100,4
6,Mar(r),100,5
7,Mar(r),100,6
8,Apr,100,-1

首先创建一个由付款ID键入的字典,其中包含父ID和最初设置为-1的子ID。你最终得到:

1, {-1, -1}
2, {-1, -1}
3, {-1, -1}
4, {3, -1}
5, {4, -1}
6, {5, -1}
7, {6, -1}
8, {-1, -1}

现在,迭代字典的键,并为每个具有父ID的条目,查找该父节点并将其子ID链接到当前条目。你最终得到:

1, {-1, -1}
2, {-1, -1}
3, {-1, 4}
4, {3, 5}
5, {4, 6}
6, {5, 7}
7, {6, -1}
8, {-1, -1}

最后,再次浏览一下这本词典。子项为-1的任何条目都是已完成的付款,您可以向后关注父链接以获取历史记录。

答案 1 :(得分:0)

当问题是"我如何进行一些奇怪的旋转来重建过去某个时候已知的简单关系?",答案是"修复设计所以这种关系以简单明了的方式表达出来。我认为这是一个X / Y问题。

付款表:

ID / Name / Payment / PaymentGroupID

PaymentGroup表:

ID / InitialPaymentID

付款失败时,请创建PaymentGroup记录。 ID是自动编号的。将新记录中的InitialPaymentID设置为失败的付款ID,并将该付款的PaymentGroupID设置为新的PaymentGroup.ID。

这是一个替代解决方案,在一个表中:

ID / Name / Payment / InitialPaymentID / ParentPaymentID

ParentPaymentID就是以前的样子。 InitialPaymentID是失败的付款的ID。这给你一些明智的选择。我选择在项目3上将InitialPaymentID设置为3,即失败的那个,因为乍一看我觉得这很有用。但我可能会在开发过程中改变这个决定。

ID / Name / Payment InitialPaymentID / ParentPaymentId
1    Jan    100     NULL               NULL
2    Feb    100     NULL               NULL
3    Mar    100     3                  NULL
4    Mar(r) 100     3                  3
5    Mar(r) 100     3                  4
6    Mar(r) 100     3                  5
7    Mar(r) 100     3                  6
8    Apr    100     NULL               NULL

我正在使用您的"简化架构"方法:当然,在真实数据库中,这些表将包含许多其他列。但这是它的本质。

答案 2 :(得分:0)

我定义了一些类来表示数据,因为你没有:

public class PaymentClass {
    public int ID;
    public string Name;
    public double Payment;
    public int? ParentPaymentId;
}

public class PaymentWithChildren {
    public int ID;
    public string Name;
    public double Payment;
    public int? ParentPaymentId;
    public PaymentClass[] Payments;
}

如果您设置映射以按ID检索付款,则可以创建帮助程序以返回父项:

public static class PaymentHelpers {
    public static Dictionary<int, PaymentClass> PaymentMap;
    public static void SetupPaymentMap(PaymentClass[] db) {
        PaymentMap = db.ToDictionary(p => p.ID);
    }
    public static PaymentClass PaymentForID(int anID) => PaymentMap.TryGetValue(anID, out var p) ? p : null;

    public static IEnumerable<PaymentClass> Parents(PaymentClass[] db, int id) {
        var p = PaymentForID(id);
        while (p.ParentPaymentId.HasValue) {
            p = PaymentForID(p.ParentPaymentId.Value);
            yield return p;
        }
    }
}

现在您可以使用一些LINQ调用setup / helper方法:

PaymentHelpers.SetupPaymentMap(db);
var parents = db.Where(p => !db.Any(p2 => p2.ParentPaymentId == p.ID));
var parentsWithChildren = parents.Select(p => new PaymentWithChildren {
    ID = p.ID,
    Name = p.Name,
    Payment = p.Payment,
    Payments = PaymentHelpers.Parents(db, p.ID).ToArray()
}).ToList();