Как получить различия, которые вызвали кроме добавления IEnumerable? - PullRequest
1 голос
/ 26 октября 2011

Я использую Enumerable.Except , чтобы проверить, синхронизируется ли DataTable в памяти с таблицей в базе данных.

Фон: эта DataTable и другие часто используемые таблицы хранятся в Cache веб-приложения. Но между тем я убежден, что это не очень хороший подход, потому что это источник неприятных ошибок, которые трудно воспроизвести / отладить.

Поэтому я создал функцию, которая проверяет, синхронизируются ли база данных и память, иначе будет создан журнал ошибок. Это работает отлично. Если в памяти есть строка, которой нет в базе данных, эта строка будет показана ниже «Разница в базе данных» , то же самое применимо в обратном порядке. Но если строки существуют в обоих источниках данных (PK idRMA) и некоторые значения отличаются, журнал будет содержать эту строку в двух версиях (ниже «Разница в базе данных» и «Разница в базе данных» ). Нелегко увидеть различия на первый взгляд.

Q : можно ли выбрать только те свойства, которые заставили 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), но как я могу выбрать различия? Если объединение не возвращает результат, идентификатор не существует во второй последовательности, и можно регистрировать все свойства. Но если во второй последовательности есть соответствующая запись, как я могу выбрать свойства, которые отличаются?

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 ]

2 голосов
/ 26 октября 2011

Это не ответ на ваш вопрос, так что не принимайте во внимание, если это бесполезно, но вы можете подумать немного больше о вашем дизайне.

этот DataTable и другие часто используемые таблицы хранятся в кэше веб-приложения. Но тем временем я убежден, что это не очень хороший подход, потому что это источник неприятных ошибок, которые трудно воспроизвести / отладить.

Это красный флаг. DataTable не является потокобезопасным, поэтому вам, вероятно, никогда не следует изменять тот, который находится в Cache. Без синхронизации вы получите неприятные ошибки, которые сложно отладить. И даже если синхронизация реализована правильно, я бы посчитал такую ​​конструкцию хрупкой, поскольку ее может легко сломать менее опытный программист по техническому обслуживанию.

С другой стороны, если DataTable в Cache предназначен только для чтения, я бы предложил вместо сравнения его с данными, загруженными из базы данных, просто заменить его данными из базы данных - т.е. обновить кеш периодически. И ваш дизайн приложения должен ожидать, что он может иногда находить данные в Cache, которые не являются «свежими».

2 голосов
/ 26 октября 2011

Я думаю, вам нужно будет создать собственный компаратор равенства, как описано здесь . В конечном итоге вам придется как-то сравнивать значения свойств, и, поскольку вы используете Except(), вы можете сохранить согласованность и создать собственный компаратор.

Кроме того, вы могли бы написать отдельную функцию, чтобы сделать работу за вас. Важно то, что вы предпочитаете.

EDIT:

Это очень грубый код, но, надеюсь, может помочь вам:

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.

0 голосов
/ 26 октября 2011

Я придерживаюсь неуклюжего подхода, пока никто не знает лучшего способа. Просто сравните их снова и покажите только те значения, которые отличаются, иначе «равны».

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) параллельно производственному серверу и удалите / создайте несколько тестовых записей или измените некоторые значения здесь и там.

...