How to use BufferCompare.Compare?

,

I stumbled across the Epicor.Data.BufferCompare class in the Epicor.ServiceModel.dll. We have had user that have asked for an email of the differences when an update happens and I planned on creating my own compare but then came across this class.

I typically try to use anything that Epicor has already created but I haven’t been able to get it to work. I’m testing with the ABCCode BO. I’ve tried in a Pre-Processing Update Method and a Standard Directive just to see if I can get it to work. In both cases, it doesn’t return anything when using passing the “changedColumns”, which from the signature is supposed to be the columns that have are different or changed.

I’m also all ears if there is something different that can accomplish the same thing. Any help would be appreciated!

Here is my code:

    var sb = new StringBuilder();

    foreach (var row in ttABCCode.Where(w => w.Updated()))
    {
        var originalRow = ttABCCode.FirstOrDefault(w => w.Unchanged() && w.SysRowID == row.SysRowID);
        var usingList = IceColumnCollection.GetColumns(row.GetType()).Select(s => s.ColumnName).ToList();
        var excludingList = new List<string>() { "SysRowID", "SysRevID", "RowMod", "RowVersionAsUInt64" };
        var changedColumns = new List<string>();
        var changes = Epicor.Data.BufferCompare.Compare(originalRow, row, usingList, excludingList, changedColumns);

        if (changes)
        {
            foreach (var changedColumn in changedColumns)
            {
                sb.AppendLine(string.Format("{0} was: {1} now: {2}", changedColumn, originalRow[changedColumn], row[changedColumn]));
            }

        }
    }

Does this help?

/// <typeparam name="TTypeFrom">The type of the source object</typeparam>
/// <typeparam name="TTypeTo">The type of the target object</typeparam>
/// <param name="fromItem">The source object</param>
/// <param name="toItem">The target object</param>
/// <param name="usingList">A list of properties to compare</param>
/// <param name="exceptForList">A list of properties to exclude from compare</param>
/// <returns><c>true</c> if the properties of the two object have the same values.</returns>

public static bool Compare<TTypeFrom, TTypeTo>(TTypeFrom fromItem, TTypeTo toItem, List<string> usingList, List<string> exceptForList)
1 Like

Doesn’t ChangeLog do this for you already?

Key thing to note here is that you get true if NO changes

They also have an overloaded method that takes another List supposedly to return the columns that changed. This is the method I can’t get to work. I just want fields that changed and differences.

/// <param name="resultsList">A list of properties which were different during compare</param> 
/// <returns><c>true</c> if the properties of the two object have the same values.</returns>

public static bool Compare<TTypeFrom, TTypeTo>(TTypeFrom fromItem, TTypeTo toItem, List<string> usingList, List<string> exceptForList, List<string> resultsList)

the only issue is, without ref or out keyword, that var will never change.

if (cachedDelegate == null)
	{
		resultsList = new List<string>();
		return false;
	}
``

What about going row by row?
private static bool SimpleArrayCompare(ExtensionRow sourceRow, ExtensionRow targetRow, List<string> results, StringComparer stringComparer)

I believe it can. We don’t have need to document it in the DB and I was hoping not to add an extra layer of querying at the time of change. We would have to setup using in-transaction BPM, not a Standard Directive, which I try to steer clear of when possible. I think not all tables are supported. So I was going to make general routine I could use across tables.

I believe method can still add values to the list without the ref . At least in my experience but I could be wrong!

I can definitely go row by row and get what I need. It just spawned my curiosity when I came across it and curious if anybody else had thoughts about it or had used it before. I’m also curious to know how Epicor uses it or if they even use it all.

That article is from Jon Skeet - the legend. He mentions in there if you pass by value, the caller cannot see the change, that is the issue here.

I proved myself wrong. It seems since the list is stored on the heap, it doesnt matter.

   static void Main(string[] args)
        {
            var list = new List<string>() ;
            DoList(list);
            foreach(var s in list)
            {
                Console.WriteLine( s);
            }
            Console.ReadKey();
        }

        static void DoList(List<string> l)
        {
            l.Add("Hi");
        }

Looks like Epicor uses that in Triggers(CustCnt, Customer, EmpBasic, EmpExpense, to name a few) also it looks like a few BOs use it (APInvoice, AutoBankRec, CustShip, and more)

Example

 List<string> exceptList = new List<string>(new string[] { "RowMod", "SysRowID","SysRevID" });
 List<string> changedList = new List<string>();

BufferCompare.Compare(ttAPInvHed1, ttAPInvHed2, null, exceptList, changedList);
  foreach (string fld in changedList)
                {
                  
                }

So I was able to get it to work but not in a way that is useful.

It looks like the IDynamicColumnValues interface that an IceRow is inherited from is not populated and the BufferCompare uses this interface to do the comparison. So when you set field values like this, it works but passing the rows that are in the TableSet does not. I tried a BufferCopy.Copy to new objects but it still didn’t work.

ABCCodeRow row1 = new ABCCodeRow();
ABCCodeRow row2 = new ABCCodeRow();
foreach (var item in IceColumnCollection.GetColumns(ttABCCodeRow.GetType()))
{
	row1[item.ColumnName] = ttABCCodeRow[item.ColumnName];
	row2[item.ColumnName] = ottABCCodeRow[item.ColumnName];
}

var excludingList = new List<string>() { "SysRowID", "SysRevID", "RowMod" };
var usingList = new List<string>() { "CountFreq" };
var results = new List<string>();
var changes = Epicor.Data.BufferCompare.Compare<ABCCodeRow, ABCCodeRow>(row1, row2, usingList, excludingList, results);

I’m also curious, how did you figure out what Triggers/Services were using it?

1 Like

By text searching the decompiled DLLs