比较类的属性以识别哪些已更改

时间:2015-04-09 05:07:16

标签: c# asp.net comparison

在我的Web应用程序中,我希望在通过UI更改某些内容时通知用户。例如,我的Project类看起来像这样

public class Project
    {
        public string Name { get; set; }
        public TaskStatus Status { get; set; }
        public string Planner { get; set; }
        public DateTime ScheduleStart { get; set; }
        public DateTime ScheduleEnd { get; set; }
        public double EstimatedCost { get; set; }
        public double ActualCost { get; set; }
        public string AssignedTo { get; set; }
    }

现在我在UI上显示此信息,并且具有更改某些内容(例如状态,日程安排,费用等)的特定用户可以更改此信息。所以我想要的是,当用户更改某些内容时,应该发送电子邮件通知项目经理让我们说或任何感兴趣的人。

我已经编写了所有其他必需的代码来发送电子邮件和管理权限等。现在我想具体查看确切改变的内容例如如果只更改了Planner或状态已更改,那么电子邮件应包含新旧值,如TFS生成通知

P.S:上面的代码显示了我的Project类的一个非常简单的版本,实际的类有30多个属性。所以我在想,不应该对每个单独的属性进行比较,而应该有一种更简单,更通用的方式告诉我哪些属性已经改变,以便我可以根据它们进行通知。

4 个答案:

答案 0 :(得分:1)

基于反射的简单解决方案。请注意,它可以进行优化,并且它不支持(此时)比较内部集合/对象。比较对象必须是POD(普通旧数据)

public class Project
{
    public string Name { get; set; }
    public TaskStatus Status { get; set; }
    public string Planner { get; set; }
    public DateTime ScheduleStart { get; set; }
    public DateTime ScheduleEnd { get; set; }
    public double EstimatedCost { get; set; }
    public double ActualCost { get; set; }
    public string AssignedTo { get; set; }

    public Project Clone()
    {
        // If your object has inner collections, or
        // references to other objects, you'll have to deep
        // clone them ***manually***!!!
        return (Project)MemberwiseClone();
    }
}

public static class SimpleComparer
{
    // Item1: property name, Item2 current, Item3 original
    public static List<Tuple<string, object, object>> Differences<T>(T current, T original)
    {
        var diffs = new List<Tuple<string, object, object>>();

        MethodInfo areEqualMethod = typeof(SimpleComparer).GetMethod("AreEqual", BindingFlags.Static | BindingFlags.NonPublic);

        foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            object x = prop.GetValue(current);
            object y = prop.GetValue(original);
            bool areEqual = (bool)areEqualMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { x, y });

            if (!areEqual)
            {
                diffs.Add(Tuple.Create(prop.Name, x, y));
            }
        }

        return diffs;
    }

    private static bool AreEqual<T>(T x, T y)
    {
        return EqualityComparer<T>.Default.Equals(x, y);
    }
}

现在,您需要Clone()方法:

public class Project
{
    public string Name { get; set; }
    public TaskStatus Status { get; set; }
    public string Planner { get; set; }
    public DateTime ScheduleStart { get; set; }
    public DateTime ScheduleEnd { get; set; }
    public double EstimatedCost { get; set; }
    public double ActualCost { get; set; }
    public string AssignedTo { get; set; }

    public Project Clone()
    {
        // If your object has inner collections, you'll have to deep
        // clone them ***manually***!!!
        return (Project)MemberwiseClone();
    }
}

然后......

var current = new Project();
var original = current.Clone();
current.ActualCost = 10000;

var diffs = SimpleComparer.Differences(current, original);

foreach (var diff in diffs)
{
    Console.WriteLine("'{0}' changed from {1} to {2}", diff.Item1, diff.Item3, diff.Item2);
}

答案 1 :(得分:0)

我假设您指的是财产价值而不是实际的类别&#39;属性。为了能够比较哪些属性值已更改,必须有两个版本的对象,比如oldupdated。我建议实现IEquatable接口,如果你有复杂的对象,在你的情况下是一个嵌套的类TaskStatus,它也有你需要比较的属性。您还可以让TaskStatus或其他嵌套类实现IEquatable界面,这样您就不必担心比较它们的属性值,从而为您提供只需调用{{{ 1}}方法。您可以拥有在覆盖Project's Equals()方法中获取更改的逻辑。

如果您不想对每个属性进行硬编码以进行比较,那么可以进行一些反思。 :)

Equals()

答案 2 :(得分:0)

您可以使用如下类,即使在UI上更新属性后,每次更改属性时都会存储属性的旧值和新值,然后该对象可用于检索这两个值。您需要做的就是在每个属性的set方法下创建此类的实例。您还可以在创建对象之前检查值是否已更改。

public class PropertyChangingEventArgs : EventArgs
{
    public PropertyChangingEventArgs()
    {
    }

    public PropertyChangingEventArgs(string propName, object oldValue, object newValue)
    {
        PropertyName = propName;
        OldValue = oldValue;
        NewValue = newValue;
    }

    public string PropertyName { get; set; }

    public object OldValue { get; set; }

    public object NewValue { get; set; }
}

在财产方面,你可以这样做:

private string family;
    public string Family
    {
        get { return family; }
        set
        {
            if (family != value)
            {
                PropertyChangingEventArgs e = new PropertyChangingEventArgs("Family", family, value);
                OnPropertyChanging(e);
                family = value;
                OnPropertyChanged("Family");
            }
        }
    }

更新后,您可以检查所有属性是否已更改(或者您可以在每次更改属性时继续填充已更改属性的列表)并使用旧值和新值邮寄列表。

答案 3 :(得分:-1)