Domanda

I have two DataTables, e.g. OriginalEntity and Entity. Interface application modifies Entity dataTable's rows. While saving I want to check DataRows that is modified or different from OrigianlEntity. But, also I need to exclude few fields while comparing e.g. modified date and other audit fields. Currently I am looping through each rows of datatable, like this:

List<string> auditFields = new List<string>(){"createdon","modifiedon"};
string IdentityKeyName = "id";
object ent,orgEnt; 
foreach(string columnName in columnList) // ColumnList is List of columns available in datatable
{
    foreach(DataRow dr in Entity.Rows)
    {
        ent = dr[columnName];
        orgEnt = OriginalEntity.Select(IdentityKeyName + " = " + dr[IdentityKeyName].ToString())[0][columnName];
        if(!ent.Equals(orgEnt) && !auditFields.Contains(columnName))
        {
            isModified = true;
            break;
        }
    }
}

I just want an efficient way to achieve above. Please suggest.

Thanks everyone for your suggestion, and this is my (as I don't have primary key defined)

Solution:

public bool isModified(DataTable dt1, DataTable dt2, string IdentityKeyName)
{
    bool isModified = false;
    List<string> auditFields = new List<string>() { "createdon", "modifiedon" };
    isModified = isModified || (dt1.Rows.Count != dt2.Rows.Count);
    if(!isModified)
    {
        //Approach takes 150 ms to compare two datatable of 10000 rows and 24 columns each
        DataTable copyOriginalEntity = dt1.Copy();
        DataTable copyEntity = dt2.Copy();

        //Exclude field you don't want in your comparison -- It was my main task
        foreach(string column in auditFields)
        {
            copyOriginalEntity.Columns.Remove(column);
            copyEntity.Columns.Remove(column);
        }
        for(int i=0;i<copyOriginalEntity.Rows.Count;i++)
        {
            var origItems = copyOriginalEntity.Rows[i].ItemArray;
            var entityItem = copyEntity.Select(IdentityKeyName + " = " + copyOriginalEntity.Rows[i][dentityKeyName].ToString())[0].ItemArray;
            if(string.Concat(origItems) != string.Concat(entityItem)){ isModified = true; break; }
        }
    }
    return isModified;
}
È stato utile?

Soluzione

You are going to have to loop though the columns to compare. This compare ent.Equals(orgEnt) in your code is comparing if the object references are the same. This doesn't seem like what you want and you want to compare values.

public bool IsChanged(DataTable original, DataTable source, string idKeyName, params string[] ignoreColumns)
{
    // make sure "key" column exist in both
    if (!original.Columns.Contains(idKeyName) || !source.Columns.Contains(idKeyName))
    {
        throw new MissingPrimaryKeyException("Primary key column not found.");
    }

    // if source rows are not the same as original then something was deleted or added
    if (source.Rows.Count != original.Rows.Count)
    {
        return false;
    }

    // Get a list of columns ignoring passed in and key (key will have to be equal to find)
    var originalColumns =
        original.Columns.Cast<DataColumn>()
                .Select(c => c.ColumnName)
                .Where(n => !ignoreColumns.Contains(n) && n != idKeyName)
                .ToArray();

    // check to make sure same column count otherwise just fail no need to check
    var sourceColumnsCount =
        source.Columns.Cast<DataColumn>()
              .Select(c => c.ColumnName).Count(originalColumns.Contains);
    if (originalColumns.Length != sourceColumnsCount)
    {
        return false;
    }

    //Switch to linq
    var sourceRows = source.AsEnumerable();
    return sourceRows.All(sourceRow =>
        {
            // use select since not real key
            var originalCheck = original.Select(idKeyName + " = " + sourceRow[idKeyName]);
            if (originalCheck.Length != 1)
            {
                // Couldn't find key or multiple matches
                return false;
            }

            var originalRow = originalCheck.First();
            //Since using same array we can use linq's SequenceEqual to compare for us
            return
                originalColumns.Select(oc => sourceRow[oc])
                               .SequenceEqual(originalColumns.Select(oc => originalRow[oc]));
        });
}

There might be some micro optimizations but I think no matter what you will have to check each column.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top