Найти различия между двумя объектами одного типа - PullRequest
15 голосов
/ 28 апреля 2011

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

Прямо сейчас вот как выглядит мое действие сохранения:

[HttpPost]
public RedirectToRouteResult SaveSingleEdit(CompLang newcomplang)
{
    var oldCompLang = _db.CompLangs.First(x => x.Id == newcomplang.Id);

    _db.CompLangs.Attach(oldCompLang);
    newcomplang.LastUpdate = DateTime.Today;
    _db.CompLangs.ApplyCurrentValues(newcomplang);
    _db.SaveChanges();

    var comp = _db.CompLangs.First(x => x.Id == newcomplang.Id);

    return RedirectToAction("ViewSingleEdit", comp);
}

Я обнаружил, что могу использовать это для перебора моего свойства oldCompLang:

var oldpropertyInfos = oldCompLang.GetType().GetProperties();

Но это не очень помогает, так как показывает только свойства (Id, Name, Status ...), а не значения этих свойств (1, Hello, Ready ...).

Я мог бы просто пройти трудный путь:

if (oldCompLang.Status != newcomplang.Status)
{
    // Add to my activity log table something for this scenario
}

Но я действительно не хочу делать это для всех свойств объекта.

Я не уверен, каков лучший способ перебрать оба объекта, чтобы найти несоответствия (например, пользователь изменил имя или статус ...) и построить список из тех различий, которые я могу сохранить в другой таблице .

Ответы [ 5 ]

24 голосов
/ 28 апреля 2011

Это не так уж плохо, вы можете сравнить свойства "вручную", используя отражение, и написать методы расширения для повторного использования - вы можете взять это как отправную точку:

public static class MyExtensions
{
    public static IEnumerable<string> EnumeratePropertyDifferences<T>(this T obj1, T obj2)
    {
        PropertyInfo[] properties = typeof(T).GetProperties();
        List<string> changes = new List<string>();

        foreach (PropertyInfo pi in properties)
        {
            object value1 = typeof(T).GetProperty(pi.Name).GetValue(obj1, null);
            object value2 = typeof(T).GetProperty(pi.Name).GetValue(obj2, null);

            if (value1 != value2 && (value1 == null || !value1.Equals(value2)))
            {
                changes.Add(string.Format("Property {0} changed from {1} to {2}", pi.Name, value1, value2));
            }
        }
        return changes;
    }
}
9 голосов
/ 28 апреля 2011

Если вы используете EntityFramework, вы можете получить изменения непосредственно из ObjectContext

Получение записей об изменении состояния:

  var modifiedEntries= this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);

Получение сохраненных имен свойств, исходных и измененных значений:

  for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount - 1; i++)
            {
                var fieldName = stateChangeEntry.OriginalValues.GetName(i);

                if (fieldName != changedPropertyName)
                    continue;

                var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString();
                var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString();
            }

Это лучше, чем ответ @ BrokenGlass, потому что он углубится в граф объектов для любых измененных состояний и даст вам измененные свойства связанных коллекций.Это также лучше, потому что это отражает все, что ObjectContext будет в конечном итоге сохранять в базе данных.С принятым решением вы можете получить изменения свойств, которые фактически не будут сохраняться в ситуации, когда вы отключаетесь через контекст объекта.

С EF 4.0 вы также можете переопределить метод SaveChanges () и обернуть любой аудитили действие в той же транзакции, что и в случае возможного сохранения объекта, что означает, что контрольный журнал не будет существовать без изменения объектов, и наоборот.Это гарантирует точный журнал.Если вы не можете гарантировать, что аудит точен, то он почти бесполезен.

1 голос
/ 28 июля 2016

Расширение ответа jfar, чтобы дать некоторые разъяснения и изменения, которые мне пришлось внести, чтобы заставить его работать:

// Had to add this:
db.ChangeTracker.DetectChanges();
// Had to add using System.Data.Entity.Infrastructure; for this:
var modifiedEntries = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);

foreach (var stateChangeEntry in modifiedEntries)
{
    for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount; i++)
    {
        var fieldName = stateChangeEntry.OriginalValues.GetName(i);
        var changedPropertyName = stateChangeEntry.CurrentValues.GetName(i);

        if (fieldName != changedPropertyName)
            continue;

        var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString();
        var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString();
        if (originalValue != changedValue)
        {
            // do stuff
            var foo = originalValue;
            var bar = changedValue;
        }

    }
}
0 голосов
/ 28 апреля 2011

Реализация IEquatable<T> на вашей сущности.Другим способом является реализация пользовательского IComparer<T>, где вы можете, например, выставить событие, которое будет запущено, если свойство отличается.

0 голосов
/ 28 апреля 2011

используйте этот пользовательский код для сравнения 2 объектов.Если они терпят неудачу, зарегистрируйте это.Проектная точка зрения Создайте это в классе Utility.

используйте его как object1.IsEqualTo(object2)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...