如何获得导致除了添加IEnumerable的差异?

时间:2011-10-26 11:20:38

标签: .net vb.net linq linq-to-objects

我正在使用Enumerable.Except检查内存中的DataTable是否与数据库中的表同步。

背景是:此DataTable和其他常用表存储在WebApplication的Cache中。但同时我确信这不是一个好方法,因为它是难以重现/调试的令人讨厌的错误的来源。

因此我创建了一个检查数据库和内存是否同步的函数,否则将创建错误日志。这非常有效。如果内存中有一行不在数据库中,则该行将显示在“数据库中的差异”下面,反之亦然。但是如果两个数据源中存在行(PK idRMA)并且某些值不同,则日志将包含两个版本的该行(在“数据库中的差异”和“数据库中的差异”下面) )。第一眼看到差异并不容易。

:是否可以仅选择导致Except认为第一个序列不在秒中的属性?

这是完整的功能(第一行是相关的):

Public Shared Sub CheckRmaMemoryInSyncWithDB()
    Dim inSyncText As String
    Dim color As Drawing.Color
    Dim isInSync As Boolean
    Dim daRma As New ERPModel.dsRMATableAdapters.RMATableAdapter
    Dim tblRma = daRma.GetData()
    Dim memory = (From rma In dsRMA.RMA
                  Where Not rma.IsfiChargeNull
                  Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
    Dim dataBase = (From rma In tblRma
                    Where Not rma.IsfiChargeNull
                    Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
    Dim notInDatabase = memory.Except(dataBase).ToList
    Dim notInMemory= dataBase.Except(memory).ToList

    If notInMemory.Any OrElse notInDatabase.Any Then
        isInSync = False
        inSyncText = "Database and Memory are NOT in Sync! Event-Log created."
        Dim ex As New Exception("Database and Memory are NOT in Sync!")
        Dim errInfo = String.Empty
        If notInMemory.Any Then
            errInfo &= "Difference in memory:" & Environment.NewLine
            Dim memoryInfo = String.Join(Environment.NewLine, notInMemory)
            errInfo &= memoryInfo & Environment.NewLine
        End If
        If notInDatabase.Any Then
            errInfo &= "Difference in database:" & Environment.NewLine
            Dim databaseInfo = String.Join(Environment.NewLine, notInDatabase)
            errInfo &= databaseInfo & Environment.NewLine
        End If
        ErrorLog.WriteError(ex, errInfo)
    Else
        isInSync = True
        inSyncText = "Database and Memory are in Sync, all RMA's are identical in both."
    End If

    Dim master = DirectCast(DirectCast(HttpContext.Current.CurrentHandler, Page).Master, ERPMaster)

    color = If(isInSync, Drawing.Color.Green, Drawing.Color.Red)
    master.showStatusMessage(inSyncText, True, color)
End Sub

注意:我可以相互比较所有属性,但我想知道是否有更简单的(LINQ)方式。基本上我正在寻找一种LINQ方式来获得两个匿名类型序列的差异(因为我只选择DataRow的相关列进行此比较)。

编辑:我认为我必须同时加入主键(idRMA),但我如何选择差异?如果连接未返回结果,则ID不存在于第二个序列中,并且可以记录所有属性。但如果在第二个序列中有适当的记录,我如何选择不同的属性?

Dim diff = From rmaMem In notInMemory
           Join rmaDB In notInDatabase
           On rmaMem.IdRMA Equals rmaDB.IdRMA
           Select ...... ' i only want to select the properties in `rmaMem` that are different in `rmaDB` '

3 个答案:

答案 0 :(得分:2)

我认为您需要按照here所述创建自定义相等比较器。最终,您必须以某种方式比较属性值,因为您使用Except(),您可以保持一致并创建自定义比较器。

除此之外,您可以编写一个单独的函数来为您完成工作。这就是你喜欢的重要事项。

编辑:

这是非常粗略的代码,但希望可以帮助你:

class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Person();

            p1.Name = "A";
            p1.Age = 23;

            var p2 = new Person();

            p2.Name = "Ralph";
            p2.Age = 56;

             Diff(p1, p2);
        }

        private static void Diff(Person p1, Person p2)
        {
            var prop = p1.GetType().GetProperties();

            var notMatchingProperties = new List<string>();

            foreach(var p in prop)
            {
                var propName = p.Name;

                var propValue1 = p1.GetType().GetProperty(propName).GetValue(p1, null);
                var propValue2 = p2.GetType().GetProperty(propName).GetValue(p2, null);

                if (!propValue1.Equals(propValue2))
                {
            notMatchingProperties.Add(propName);        
                }
            }

            foreach (var notMatchingProperty in notMatchingProperties)
            {
            Console.WriteLine(notMatchingProperty);    
            }

        }
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

    }

我不是反思专家,所以如果其他人可以评论我的代码,那就太棒了。

最终,我不确定缓存数据是否是最好的方法。您可能需要考虑将信息存储在数据库中。缓存的问题是,在任何时候,网站都可能被回收(或重新启动),您可能会丢失内存中的数据。

在数据库中拥有数据的另一个好处是,您可以直接在数据库上运行查询,以便了解哪些属性已更改。在你的鞋子里,这就是我要做的 - 在数据库中创建一个表来存储你在Cache中的信息。

答案 1 :(得分:2)

这不是在回答你的问题,所以如果没有帮助的话就会失败,但你可能想要更多地考虑一下你的设计。

  

此DataTable和其他常用表存储在WebApplication的Cache中。但同时我确信这不是一个好方法,因为它是难以重现/调试的令人讨厌的错误的来源。

这是一面红旗。 DataTable不是线程安全的,因此您可能永远不应修改Cache中的一个。{1}}。没有同步这样做肯定会给你带来难以调试的令人讨厌的错误。即使同步正确实施,我也会认为这样的设计很脆弱,因为它可能很容易被一个技能较低的维护程序员分解。

另一方面,如果Cache中的DataTable是只读的,我建议不要将它与从数据库加载的数据进行比较,而只需用数据库中的数据替换它 - 即定期刷新缓存。并且您的应用程序设计应该期望它有时可以在Cache中找到不是“新鲜”的数据。

答案 2 :(得分:0)

只要没有人知道更好的方法,我就会遵循笨拙的方法。只需再次比较它们,只显示不同的值,否则为“相等”。

Public Shared Sub CheckRmaMemoryInSyncWithDB()
    Dim inSyncText As String
    Dim color As Drawing.Color
    Dim isInSync As Boolean
    Dim daRma As New ERPModel.dsRMATableAdapters.RMATableAdapter
    Dim tblRma = daRma.GetData()
    Dim memory = (From rma In dsRMA.RMA
                  Where Not rma.IsfiChargeNull
                  Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
    Dim dataBase = (From rma In tblRma
                    Where Not rma.IsfiChargeNull
                    Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
    Dim memoryDiff = memory.Except(dataBase).ToList
    Dim databaseDiff = dataBase.Except(memory).ToList

    If memoryDiff.Any OrElse databaseDiff.Any Then
        isInSync = False
        inSyncText = "Database and Memory are NOT in Sync! Error-Log created."
        Dim ex As New Exception("Database and Memory are NOT in Sync!")
        Dim errInfo As String = String.Empty

        'following joins both sequences to detect differences
        'note: completely missing RMA's will be detected later
        Dim diff = From rmaMem In memoryDiff
                 Join rmaDB In databaseDiff
                 On rmaMem.IdRMA Equals rmaDB.IdRMA
                 Select rmaDB.IdRMA _
                 , fiCharge = If(rmaMem.fiCharge = rmaDB.fiCharge, "equal", String.Format("{0}/{1}", rmaMem.fiCharge, rmaDB.fiCharge)) _
                 , IMEI = If(rmaMem.IMEI = rmaDB.IMEI, "equal", String.Format("{0}/{1}", rmaMem.IMEI, rmaDB.IMEI)) _
                 , RMA_Number = If(rmaMem.RMA_Number = rmaDB.RMA_Number, "equal", String.Format("{0}/{1}", rmaMem.RMA_Number, rmaDB.RMA_Number)) _
                 , ModelID = If(rmaMem.ModelID = rmaDB.ModelID, "equal", String.Format("{0}/{1}", rmaMem.ModelID, rmaDB.ModelID)) _
                 , fiCustomer = If(rmaMem.fiCustomer = rmaDB.fiCustomer, "equal", String.Format("{0}/{1}", rmaMem.fiCustomer, rmaDB.fiCustomer)) _
                 , SI_DPY = If(rmaMem.SI_DPY = rmaDB.SI_DPY, "equal", String.Format("{0}/{1}", rmaMem.SI_DPY, rmaDB.SI_DPY)) _
                 , fiStatus = If(rmaMem.fiStatus = rmaDB.fiStatus, "equal", String.Format("{0}/{1}", rmaMem.fiStatus, rmaDB.fiStatus)) _
                 , HasErrors = If(rmaMem.HasErrors = rmaDB.HasErrors, "equal", String.Format("{0}/{1}", rmaMem.HasErrors, rmaDB.HasErrors))
        If diff.Any Then
            errInfo &= "Differences(memory/database):" & Environment.NewLine
            errInfo &= String.Join(Environment.NewLine, diff) & Environment.NewLine
        End If

        Dim memoryIDs = (From rma In memory
                        Select rma.IdRMA).ToList
        Dim databaseIDs = (From rma In dataBase
                       Select rma.IdRMA).ToList
        Dim missingInMemory = databaseIDs.Except(memoryIDs)
        Dim missingInDB = memoryIDs.Except(databaseIDs)
        If missingInMemory.Any Then
            Dim rmaInfo = From rma In dataBase
                       Join idRMA In missingInMemory
                       On idRMA Equals rma.IdRMA
                       Select rma
            errInfo &= "Missing RMA's in memory:" & Environment.NewLine
            errInfo &= String.Join(Environment.NewLine, rmaInfo) & Environment.NewLine
        End If
        If missingInDB.Any Then
            Dim rmaInfo = From rma In memory
                        Join idRMA In missingInDB
                        On idRMA Equals rma.IdRMA
                        Select rma
            errInfo &= "Missing RMA's in database:" & Environment.NewLine
            errInfo &= String.Join(Environment.NewLine, rmaInfo)
        End If
        ErrorLog.WriteError(ex, errInfo)
    Else
        isInSync = True
        inSyncText = "Database and Memory are in Sync, all RMA's are identical in both."
    End If

    Dim master = DirectCast(DirectCast(HttpContext.Current.CurrentHandler, Page).Master, ERPMaster)

    color = If(isInSync, Drawing.Color.Green, Drawing.Color.Red)
    master.showStatusMessage(inSyncText, True, color)
End Sub

生成以下示例输出:

Differences(memory/database):
{ IdRMA = 25878, fiCharge = equal, IMEI = equal, RMA_Number = equal, ModelID = equal, fiCustomer = equal, SI_DPY = 1207-1104/1206-1105, fiStatus = equal, HasErrors = equal }
Missing RMA's in database:
{ IdRMA = 25882, fiCharge = 1416, IMEI = 004401076362330, RMA_Number = RMA0022725, ModelID = 449, fiCustomer = 49, SI_DPY = , fiStatus = 17, HasErrors = False }
{ IdRMA = 25881, fiCharge = 1416, IMEI = 359419030480338, RMA_Number = RMA0022724, ModelID = 758, fiCustomer = 49, SI_DPY = 1207-1124, fiStatus = 18, HasErrors = False }

顺便说一下,这种看似不一致很容易被逼迫。在VisualStudio(Cassini)中启动与生产服务器并行的应用程序并删除/创建一些测试记录或在此处更改一些值。