Store changed properties of a class

1

I need to have a list with (name and value) of the changed properties stored in the class itself. But I'm not sure what form I'm using is feasible.

I have the Employee class below:

public class Funcionario
{
    public int IdFuncionario { get; set; }

    public string Nome { get; set; }

    public string Sobrenome { get; set; }

    public string Setor { get; set; }

    public DateTime DataEntrada { get; set; }
}

Create a base class to be able to identify the change and store it:

public abstract class BaseLista
{
    public readonly Dictionary<string, object> Dictionary = new Dictionary<string, object>();

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }
        storage = value;
        if (propertyName == null) return true;
        if (!Dictionary.ContainsKey(propertyName))
        {
            Dictionary.Add(propertyName, value);
        }
        else
        {
            Dictionary[propertyName] = value;
        }
        return true;
    }
}

And I changed the Employee class this way:

public class Funcionario : BaseLista
{
    private int _idFuncionario;
    private string _nome;
    private string _sobrenome;
    private string _setor;
    private DateTime _dataEntrada;

    public int IdFuncionario
    {
        get { return _idFuncionario; }
        set { SetProperty(ref _idFuncionario, value);}
    }

    public string Nome
    {
        get { return _nome; }
        set { SetProperty(ref _nome, value); }
    }

    public string Sobrenome
    {
        get { return _sobrenome; }
        set { SetProperty(ref _sobrenome, value); }
    }

    public string Setor
    {
        get { return _setor; }
        set { SetProperty(ref _setor, value); }
    }

    public DateTime DataEntrada
    {
        get { return _dataEntrada; }
        set { SetProperty(ref _dataEntrada, value); }
    }
}

Below the test:

[TestClass]publicclassTestes{[TestMethod]publicvoidTesteLista(){varfuncionario=newFuncionario{Nome="Paulo",
                                  Sobrenome = "Balbino",
                                  Setor = "Desenvolvimento"
                              };


        var listaPropriedadesAlteradas = funcionario.Dictionary;

    }
}

Is there any better way to do this? I need this list of properties changed to mount a generic update statement, I do not want to pass all fields of the entity, as I have cases that I will not have all.

    
asked by anonymous 05.02.2015 / 14:16

2 answers

1

Data structure

First I find it strange for a class to store its own changes. But only you can tell if this really is necessary. I think it hurts at least the single responsibility principle . I find it strange a class store things that are not their own. And this "history" is not part of an official. Funcionário is just an employee. Employee change history must be a class that stores this. It even gives compile the "history" in the class, although I also find a bad idea.

Another principle that is hurt there is the Liskov replacement . A Funcionário is not a BaseList . It would be better to make an interface, maybe using an extension method maybe?). But it would only make sense to use the interface if you still want to hurt the previous principle. I reaffirm that it is the right thing to do outside this class.

At most I would do a composition instead of inheritance . So I would have a field that would store the state but the save mechanism would be in another class (I do not know, ClassPropertiesChanges ) - that would be used in this field - identical to BaseLista

And there are other hurt principles that could be cited. But I also do not think all principles need to be followed forever. And fixing one may fix the others. I am quoting these two because I would probably follow them in this case. But I do not have all the information on your case. For me it's the main change you should make.

I still do not know if it should be something completely separate and no composition to use.

Let's get together

SetProperty() returns a boolean that is not used. I do not know if I need to return something or if I need to use it. It might be that you will use it later. Just do not forget to rate this.

Dictionary besides being a little meaningful name of what it represents in the code (variable names should not say what type they are but what they hold), it seems strange to me that it is public (could encapsulate in a property) and stranger still it is declared as readonly .

Note that you are writing to her object. Maybe readonly works differently than you expect. Only the variable is read-only, the object contained in it can be written, because any part of the application is already public. Variable is one thing, its value is another , at least in reference types

I have my doubts if passing storage as ref is a good idea. I will not test in several situations but I do not know if it would work if I used it in other ways in the future. In the past I would do something like this, today I take more care even if I eventually have to write more code.

I imagine that it was intended to allow the field to be changed without going through the property that encapsulates it and that this does not go to store in this "history". Not wanting to store in any situation can be useful - it can also be poorly used - but I do not know if it's a good idea to change the field directly. In simple things it may not cause a problem but if the property starts having other algorithms, ignoring it can be problematic.

You are saving only the last changed value. That's it? Maybe you're wanting to do something else with this. You may want to implement a undo feature ( undo ) or something to compare if there has been a change in each field. If this is it, I definitely would not do it this way. It would do a separate mechanism to control this. In the bottom it falls on what I said at the beginning.

Finally, do you know that you do not need to check if a key is contained in the dictionary to see if you should change or add it? When you try to assign a value to a key that does not exist the key is automatically created.

Conclusion

If I spend more time thinking I would find other things that can be improved, but again, it might not apply to your specific case. But I hope this helps.

    
05.02.2015 / 15:32
1

Following the advice mentioned in the bigown response, I used the INotifyPropertyChanged interface in the Employee class, through the PropertyChangedEventHandler event I get the changed properties and store it in another object that will compose the message to be sent to the WCF Service:

Class Officer:

public class Funcionario : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int _idFuncionario;
    private string _nome;
    private string _sobrenome;
    private string _setor;
    private DateTime _dataEntrada;

    public int IdFuncionario
    {
        get { return _idFuncionario; }
        set
        {
            _idFuncionario = value;
            OnPropertyChanged();
        }
    }

    public string Nome
    {
        get { return _nome; }
        set
        {
            _nome = value;
            OnPropertyChanged();
        }
    }

    public string Sobrenome
    {
        get { return _sobrenome; }
        set
        {
            _sobrenome = value;
            OnPropertyChanged();
        }
    }

    public string Setor
    {
        get { return _setor; }
        set
        {
            _setor = value;
            OnPropertyChanged();
        }
    }

    public DateTime DataEntrada
    {
        get { return _dataEntrada; }
        set
        {
            _dataEntrada = value;
            OnPropertyChanged();
        }
    }

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Test Method:

[TestClass]
public class Testes
{
    private Dictionary<string, object> _listaPropriedadesAlteradas = new Dictionary<string, object>();

    [TestMethod]
    public void Teste1()
    {
        var funcionario = new Funcionario();
        funcionario.PropertyChanged += FuncionarioOnPropertyChanged;
        funcionario.Nome = "Paulo Balbino";
        funcionario.Setor = "Desenvolvimento";

        _listaPropriedadesAlteradas.ToList()
                                   .ForEach(c => Debug.WriteLine("Propriedade: {0} - Valor: {1}", c.Key, c.Value));
    }

    private void FuncionarioOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
    {
        _listaPropriedadesAlteradas[propertyChangedEventArgs.PropertyName] =
            sender.GetType()
                  .GetProperty(propertyChangedEventArgs.PropertyName)
                  .GetValue(sender);
    }
}

Now, I use the variable _list_ProductsChanges to compose the message. Any other suggestions for improvement?

    
06.02.2015 / 14:05