通过列表循环 foreach,为不同的值执行代码

Looping with foreach through list, to execute code for distinct values

我正在尝试创建一个函数,为 List 中 属性 的每个 distinct 值创建一 (1) 个 PDF 发票正在循环(使用 PDFSharp)。

我获取 string 的值,并希望将其用作发票的抬头(这是开具发票的企业的名称)。

举例说明: 在下面table中,我想得到LicenseHolder1(一次)、LicenseHolder2(一次)和LicenseHolder3(一次)的值。

ID LicenseHolderID Value1 Value2 Value3
1 LicenseHolder1 66 44 UF-2107
2 LicenseHolder2 22 25 UF-2107
3 LicenseHolder2 62 24 UF-2107
4 LicenseHolder3 82 12 UF-2107
5 LicenseHolder3 6 77 UF-2107
6 LicenseHolder3 15 62 UF-2107

这是我正在使用的方法:

using (SqlConnection con = new(ConnectionString.connectionString))
using (SqlCommand cmd = new("spCreateInvoice", con) {CommandType = CommandType.StoredProcedure})
using (SqlDataAdapter da = new(cmd))
// this is the name of my SQL table
using (DataTable dt = new("tblTripsPerMonth")) 
using (DataSet ds = new()) {

    con.Open();
    da.Fill(dt);
    ds.Tables.Add(dt);

    /* adding the SQL table rows to my ObservableCollection (PaidTrips), 
    which is bound to properties in my holder class (PaidTrip): */

    foreach (DataRow dr in ds.Tables[0].Rows {

       PaidTrips.Add(new PaidTrip {

             LicenseHolderID = dr[0].ToString(),
             Value1 = (decimal)dr[1],
             Value2 = (decimal)dr[2],
             Value3 = dr[3].ToString(),
        });

        // Now I am instantiating a list, so that I can group
        List<PaidTrip> paidTrip = PaidTrips
            .GroupBy(p => new {p.LicenseHolderID})
            .Select(g => g.First())
            .ToList();
            
        // this is copied code from another question, and this is where I fall of
        foreach (PaidTrip PaidTrips in paidTrip) {

            foreach (var LicenseHolderID in PaidTrips.GetType().GetProperties()) {

                string n = LicenseHolderID.GetValue(PaidTrips).ToString();

如果我在此之后抓取 string n 并将其传递给一个 MessageBox.Show,它应该显示 LicenseHolder1。 但是,如果我尝试将它传递到 PDF 中,就像这样...... (接上一个代码块)

                System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
                PdfDocument pdf = new();
                PdfPage page = pdf.AddPage();
                XGraphics gfx = XGraphics.FromPdfPage(page);

                // drawing in the string value
                gfx.DrawString(n, new XFont("Arial", 40, XFontStyle.Bold), myColor, new XPoint(40, 250));

                // saving the PDF
                pdf.Save("C:\File\Path\TestPDF.pdf");
            }
        }
    }
}

...创建了 PDF,但我绘制的字符串未填充 LicenseHolder1,而是填充了其他列的(看似)随机值(如 66UF-210777)。怎么会?

(哦,我知道这段代码只创建一 (1) 个 PDF 文件,我需要几个(LicenseHolderID 列中每个不同的值一个 - 但那是改天再说)

我认为您需要按 LicenseHolderID 分组,然后对其余字段求和/求最大值。从那里,您可以摆脱循环 foreach (var LicenseHolderID in PaidTrips.GetType().GetProperties()),因为您将构建一个需要迭代的对象集合。

在您创建集合的位置,您可能希望在那里对数据进行分组,看起来您一次取一行:

var paidTrips = ds.Tables[0].Rows.Cast<DataRow>().GroupBy(t => t["LicenseHolderID"])
                    .Select(g => new PaidTrip  {
                        LicenseHolderID = g.Key,
                        Value1 = g.Sum(x => x.["Value1"] ),
                        Value2 = g.Sum(x => x.["Value2"] ),
                        Value3 = g.Max(x => x.["Value3"] ) 
                    })
                    .ToList();

这应该会为您提供上面的列表,按 LicenseHolderID 分组,例如,LicenseHolder2 变为:

LicenseHolder2 84 49 UF-2107

鉴于您按 LicenseHolderID 分组。然后您可以迭代循环,在这种情况下获得 3 个 PDF。

    foreach (PaidTrip pd in paidTrips ) {
    
        string n = pd.LicenseHolderID;
       //...pdf code...
       gfx.DrawString(n, new XFont("Arial", 40, XFontStyle.Bold),
                      myColor, new XPoint(40, 250));
      // saving the PDF
      pdf.Save("C:\File\Path\TestPDF.pdf");
                }

请注意,这是我手写的,所以它可能无法编译 - 但你应该明白了。

也许你可以这样做:

List<PaidTrip> paidTrip = PaidTrips
        .GroupBy(p => new {p.LicenseHolderID})
        .Select(g => g.First()).Distinct().ToList();

我不明白你试图在这些嵌套的 foreach 循环中完成什么

foreach (PaidTrip PaidTrips in paidTrip) {

    foreach (var LicenseHolderID in PaidTrips.GetType().GetProperties()) {
        //...
    }
}

paidTrip 应该是一个 IEnumerable<PaidTrip> 代表每个公司的第一个 PaidTrip,所以也许将变量重命名为 firstPaidTrips 将有助于澄清代码中的内容,那么你的外部 foreach 循环可能是

foreach (var paidTrip in firstPaidTrips)

那么就会更清楚那个foreach循环中的每个变量都是一个单一的paidTrip

要获得该付费旅行的 LicenseHolderID,您只需直接访问 属性。

foreach (var paidTrip in firstPaidTrips)
{
    var n = paidTrip.LicenseHolderID;
    //rest of the code is the same without that inner foreach loop.
}

那个内部 foreach 循环看起来像是您在尝试使用反射来获取未知类型对象的所有属性,但是从您的示例代码看来您知道对象类型是什么,并且您应该能够只需从对象 PaidTrip

中获取 属性

您的数据表中是否有如上所示的 id 列?您的零列不会在那里取一个 ID 吗?

foreach (DataRow dr in ds.Tables[0].Rows {

   PaidTrips.Add(new PaidTrip {

         LicenseHolderID = dr[0].ToString(),
         Value1 = (decimal)dr[1],
         Value2 = (decimal)dr[2],
         Value3 = dr[3].ToString(),
    });

如果您按 LicenseHolderID 分组,则选择分组结果的第一个元素。这是故意的吗?

List<PaidTrip> paidTrip = PaidTrips
        .GroupBy(p => new {p.LicenseHolderID})
        .Select(g => g.First())
        .ToList();

这将本质上 return 一个包含多个包含相同 LicenseHolderID 的付费旅行对象的新对象。

通常如果你想使用组的字符串名称,你可以引用键值。

paidTrip.Key.ToString();
    

可能更好用,

    var paidTrip = PaidTrips
        .GroupBy(p => new {p.LicenseHolderID})
        .ToList();

    foreach (var tripFound in paidTrip)
    {
        string tripId = tripFound.Key.ToString();
        //Generate your PDF File.
    }

这应该为每个组提供一个文件。如果我误会了你,我深表歉意。