繁体   English   中英

如果项目已存在,则 C# 更新多元素列表项目,否则将其添加为新项目

[英]C# update multiple element list item if item already exists otherwise add it as new item

使用 C# 我试图创建一个包含多个元素的列表,如果主键不在列表中,则添加新项目,否则更新项目。 我已经找到了很多关于每个部分的内容,但是我正在努力将我迄今为止发现的内容组合成一个可行的解决方案。

下面是编译的注释代码。

问题:解决方案是将所有项目添加为新项目,即使密钥 (ReceiptID) 已存在于列表中,因此我的检查方式存在问题。

也许问题:(编辑:不是问题,因为它按预期工作)因为我无法测试更新是否存在部分,我不知道我是否有这个权利。

任何指导表示赞赏。

编辑:(注意:根据 WhoIsRich 评论,这可以使用字典而不是列表来完成。这可能是一个更好、更有效的解决方案。谢谢 WhoIsRich)。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            IList<Receipts> receiptList = new List<Receipts>()
            // The real app reads through a temporary table in the database
            // to pick up the line items of a sale. The objective is to combine those line
            // items into a summary list called receipts based on the receipt number. For this
            // runnable example, I add some data to the receipt list here.
            {
                new Receipts() { ReceiptID = 1, TotalPrice = 10, TotalCost = 5, Profit = 5, EmployeeID = 1 },
                new Receipts() { ReceiptID = 2, TotalPrice = 15, TotalCost = 6, Profit = 9, EmployeeID = 1 },
                new Receipts() { ReceiptID = 3, TotalPrice = 20, TotalCost = 7, Profit = 13, EmployeeID = 1 },
                new Receipts() { ReceiptID = 4, TotalPrice = 25, TotalCost = 10.50M, Profit = 14.50M, EmployeeID = 1 },
            };
            // some dummy data to update list item with.  Note: if receiptID is the same as one already in the list, employeeID
            // will also be the same. (It's always the same employee that completes a whole transaction).
            int[] receiptID = { 3, 4, 5, 5, 6, 7, 7, 8 };
            decimal[] totalPrice = { 5, 6, 7, 8, 9, 10, 11, 12 };
            decimal[] totolCost = { 2, 2.5M, 3, 3.5M, 4, 4.5M, 5, 5.5M };
            decimal[] profit = { 3, 3.5M, 4, 4.5M, 5, 5.5M, 6, 6.5M };
            int[] employeeID = { 1, 1, 1, 1, 2, 1, 1, 2 };

            // This for loop represents the while loop reading the database table
            for (int i = 0; i < 8; i++)
            {
                // first, check to see if the receiptID is already in the list. This is the 
                // part I am having trouble with.
                Receipts r = new Receipts();
//EDIT:
            //if (receiptList.Contains(new Receipts { ReceiptID = receiptID[i] }))  <== original in question
            //if (receiptList.Any(rc => rc.ReceiptID == receiptID[i])) <== Keyur PATEL's comment to question - Works
            //if (receiptList.Any(o => o.ReceiptID == receiptID[i])) <== kurakura88 answer - Works
            if (receiptList.Any(receipt => receipt.ReceiptID == receiptID[i])) // <== Eric Wu - Works

// END EDIT
                {
                    // The code never enters here! <<===  This is what I need help with fixing
                    var tu = receiptList.Single(x => x.ReceiptID == receiptID[i]);
                    tu.TotalPrice += totalPrice[i];
                    tu.TotalCost += totolCost[i];
                    tu.Profit += profit[i];
                    // receiptID and employeeID are not updated as they don't change in this if loop.
                }
                else
                {
                    // This should happen if the receiptID is not in the list, but it's happening
                    // every time.
                    r.ReceiptID = receiptID[i];
                    r.EmployeeID = employeeID[i];
                    r.TotalPrice = totalPrice[i];
                    r.TotalCost = totolCost[i];
                    r.Profit = profit[i];
                    receiptList.Add(r);
                }
            }
            // Below here just displays the results in a sorted maner which is working ok.
            var orderByValue = from s in receiptList
                               orderby s.ReceiptID
                               ascending
                               select s;

            foreach (var item in orderByValue)
            {
                Console.WriteLine("Receipt: {0} Employee: {1} TotalPrice: {2} TotalCost: {3} Profit: {4}", item.ReceiptID.ToString(), item.EmployeeID.ToString(), item.TotalPrice.ToString(), item.TotalCost.ToString(), item.Profit.ToString());
            }
            Console.ReadLine();
        }
    }

    public class Receipts
    {
        public int ReceiptID { get; set; }
        public int EmployeeID { get; set; }
        public decimal TotalPrice { get; set; }
        public decimal TotalCost { get; set; }
        public decimal Profit { get; set; }
    }

}

/*
The output I am getting is (note each sale line is added to list):
Receipt: 1 Employee: 1 TotalPrice: 10 TotalCost: 5 Profit: 5
Receipt: 2 Employee: 1 TotalPrice: 15 TotalCost: 6 Profit: 9
Receipt: 3 Employee: 1 TotalPrice: 20 TotalCost: 7 Profit: 13
Receipt: 3 Employee: 1 TotalPrice: 5 TotalCost: 2 Profit: 3
Receipt: 4 Employee: 1 TotalPrice: 25 TotalCost: 10.50 Profit: 14.50
Receipt: 4 Employee: 1 TotalPrice: 6 TotalCost: 2.5 Profit: 3.5
Receipt: 5 Employee: 1 TotalPrice: 7 TotalCost: 3 Profit: 4
Receipt: 5 Employee: 1 TotalPrice: 8 TotalCost: 3.5 Profit: 4.5
Receipt: 6 Employee: 2 TotalPrice: 9 TotalCost: 4 Profit: 5
Receipt: 7 Employee: 1 TotalPrice: 10 TotalCost: 4.5 Profit: 5.5
Receipt: 7 Employee: 1 TotalPrice: 11 TotalCost: 5 Profit: 6
Receipt: 8 Employee: 2 TotalPrice: 12 TotalCost: 5.5 Profit: 6.5

What want to get is (same receiptID's should have values added together to one item in list):
Receipt: 1 Employee: 1 TotalPrice: 10 TotalCost: 5 Profit: 5
Receipt: 2 Employee: 1 TotalPrice: 15 TotalCost: 6 Profit: 9
Receipt: 3 Employee: 1 TotalPrice: 25 TotalCost: 9 Profit: 16
Receipt: 4 Employee: 1 TotalPrice: 31 TotalCost: 13 Profit: 18
Receipt: 5 Employee: 1 TotalPrice: 15 TotalCost: 6.5 Profit: 8.5
Receipt: 6 Employee: 2 TotalPrice: 9 TotalCost: 4 Profit: 5
Receipt: 7 Employee: 1 TotalPrice: 21 TotalCost: 9.5 Profit: 11.5
Receipt: 8 Employee: 2 TotalPrice: 12 TotalCost: 5.5 Profit: 6.5
*/

编辑:更正 if 语句后,现在得到以下正确结果:

Receipt: 1 Employee: 1 TotalPrice: 10 TotalCost: 5 Profit: 5
Receipt: 2 Employee: 1 TotalPrice: 15 TotalCost: 6 Profit: 9
Receipt: 3 Employee: 1 TotalPrice: 25 TotalCost: 9 Profit: 16
Receipt: 4 Employee: 1 TotalPrice: 31 TotalCost: 13.00 Profit: 18.00
Receipt: 5 Employee: 1 TotalPrice: 15 TotalCost: 6.5 Profit: 8.5
Receipt: 6 Employee: 2 TotalPrice: 9 TotalCost: 4 Profit: 5
Receipt: 7 Employee: 1 TotalPrice: 21 TotalCost: 9.5 Profit: 11.5
Receipt: 8 Employee: 2 TotalPrice: 12 TotalCost: 5.5 Profit: 6.5

答案是相同类型的两个对象可能并不总是相等,即使它们具有相同的属性。

因此,

receiptList.Contains(new Receipts { ReceiptID = receiptID[i] })

可能永远不会是真的。

如果您真的想在列表中检查它,并且ReceiptID是您要检查的ID ,那么请执行

receiptList.Any(receipt=>receipt.ReceiptID == receiptID[i])

Any()将检查列表中的元素,如果在提供的条件下找到任何元素,则返回true

更新

从 C# 8 开始引入了一种新的数据类型record

根据文档

记录与类的不同之处在于记录类型使用基于值的相等性。 如果记录类型定义相同,则记录类型的两个变量相等,并且如果对于每个字段,两个记录中的值都相等`。

因此,在您的情况下,如果Receipt类下的所有属性都是可相等的,则可以将其转换为record类型。 然后, .Contains将按您最初的预期工作,因为所有必要的Equals覆盖都是由编译器自动创建的

记录是一种引用类型,并遵循基于值的相等语义。 为了强制执行值语义,编译器为您的记录类型生成了几种方法:

  • Object.Equals(Object)的覆盖。
  • 一个虚拟Equals方法,其参数是记录类型。
  • Object.GetHashCode()的覆盖。
  • 运算符==和运算符!=
  • 记录类型实现System.IEquatable<T>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM