Foros del Web » Programación para mayores de 30 ;) » .NET »

WPF - Aportación ROLLBACK en Modelos

Estas en el tema de WPF - Aportación ROLLBACK en Modelos en el foro de .NET en Foros del Web. Hola foreros !!! Para los que trabajan con WPF aplicando el patrón MVVM, les dejo una clase que implementé para poder hacer Rollback en el ...
  #1 (permalink)  
Antiguo 19/02/2015, 06:57
 
Fecha de Ingreso: junio-2003
Ubicación: Asturias
Mensajes: 2.429
Antigüedad: 20 años, 10 meses
Puntos: 7
WPF - Aportación ROLLBACK en Modelos

Hola foreros !!!

Para los que trabajan con WPF aplicando el patrón MVVM, les dejo una clase que implementé para poder hacer Rollback en el Modelo.

Muchas veces tenemos el modelo bindeado a los controles, y cuando cambiamos las propiedades en el control, automáticamente se actualizan en el objeto.

Para facilitar la labor de restaurar el objeto a su valor inicial, he creado esta pequeña clase, la cual espero os sea útil. Por supuesto.. estoy abierto a cambios, y a opiniones. :)

PropertyChangedNotifier: Clase que ofrece métodos para poder usar el NotifyPropertyChange en las propiedades de forma cómoda.
Código:
    /// <summary>
    /// Clase que aporta métodos para notificación de cambio de las propiedades.
    /// </summary>
    public class PropertyChangedNotifier : INotifyPropertyChanged
    {

        #region FIELDS

        /// <summary>
        /// Indica si ha habido cambios en el modelo.
        /// </summary>
        private bool _isChanged = false;
        #endregion


        public static string GetPropertyName<TProperty>(Expression<Func<TProperty>> property)
        {
            return ((MemberExpression)property.Body).Member.Name;
        }

        protected void RaisePropertyChanged([CallerMemberName] string caller = "")
        {
            if (PropertyChanged != null)
            {

                //Notificamos la propiedad cambiada.
                PropertyChanged(this, new PropertyChangedEventArgs(caller));

                //Indicamos que el modelo ha tenido cambios.
                _isChanged = true;
            }
        }

        protected void RaisePropertyChanged<TProperty>(Expression<Func<TProperty>> property)
        {
            RaisePropertyChanged(((MemberExpression)property.Body).Member.Name);
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

ModelBase: Clase base desde la que deben heredar los modelos.
Código:
    /// <summary>
    /// Clase base para modelos, que ofrece funcionalidad de actualización de propiedades, y posibilidad de Rollback.
    /// </summary>
    public class ModelBase : PropertyChangedNotifier
    {

        #region FIELDS

        private object _rollBack;
        #endregion

        #region TRACKING


        /// <summary>
        /// Inicia el seguimiento del modelo.
        /// </summary>
        public void Tracking()
        {

            //Clono el objeto.
            this._rollBack = Activator.CreateInstance(this.GetType());

            this.CloneProperties(this, _rollBack);
        }


        /// <summary>
        /// Restaura el modelo a su posición inicial, dicha posición inicial se habrá establecido al llamar por primera vez al Tracking.
        /// </summary>
        public void Rollback()
        {

            this.CloneProperties(_rollBack, this);
        }


        /// <summary>
        /// Copia las propiedades del origen al destino.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        private void CloneProperties(object source, object target)
        {

            source.GetType().GetProperties().Where(obj => obj.Name != "Item").ToList().ForEach(objProperty =>
            {

                this.CloneProperties(source, target, objProperty);
            });
        }


        /// <summary>
        /// Copia la propiedad del elemento source en el objeto target.
        /// </summary>
        /// <param name="source">Objeto al que se le desean copiar las propiedades</param>
        /// <param name="target">Objeto </param>
        /// <param name="targetProperty"></param>
        private void CloneProperties(object source, object target, PropertyInfo targetProperty, string data = null)
        {

            //Recuperamos el valor de la propiedad del origen.
            object sourcePropertyValue = source.GetType().GetProperty(targetProperty.Name).GetValue(source);


            if (sourcePropertyValue != null)
            {

                //Comprobamos si la propiedad es primitiva.
                if (sourcePropertyValue.GetType().IsPrimitive ||
                    sourcePropertyValue.GetType().IsArray ||
                    sourcePropertyValue.GetType().IsEnum ||
                    sourcePropertyValue.GetType().Equals(typeof(string)) ||
                    sourcePropertyValue.GetType().Equals(typeof(Guid)) ||
                    sourcePropertyValue.GetType().Equals(typeof(DateTime)) ||
                    sourcePropertyValue.GetType().Equals(typeof(byte[])))
                {

                    dynamic elementRightType = Convert.ChangeType(sourcePropertyValue, sourcePropertyValue.GetType());

                    //Asignamos el valor al objeto al que deseamos copiar las propiedades (Si no es de solo escritura)
                    if (targetProperty.CanWrite) targetProperty.SetValue(target, elementRightType);
                }
                else
                {

                    object newSourceProperty = this.GenerateObject(source, sourcePropertyValue);

                    if (targetProperty.CanWrite) targetProperty.SetValue(target, newSourceProperty);
                }
            }
        }


        /// <summary>
        /// Genera un duplicado de la propiedad enviada al método, del objeto enviado.
        /// </summary>
        /// <param name="source">Objeto origen del que se desea copiar la propiedad</param>
        /// <param name="sourceObjectProperty">Objeto que representa la propiedad del source, y del cual se realizará un clon.</param>
        /// <returns></returns>
        private object GenerateObject(object source, object sourceObjectProperty)
        {

            if (sourceObjectProperty.GetType().GetInterface("IEnumerable") != null)

                return this.GenerateObjectCollection(sourceObjectProperty);
            else
                return this.GenerateObjectElement(sourceObjectProperty);
        }


        /// <summary>
        /// Genera un duplicado del objeto pasado.
        /// </summary>
        /// <param name="originalProperty"></param>
        /// <returns></returns>
        private object GenerateObjectElement(object originalProperty)
        {

            //Recuperamos el tipo del que es la propiedad.
            var constructedPropertyType = originalProperty.GetType();

            //Creamos una nueva instancia de ese objeto.
            dynamic newObjectCreated = Activator.CreateInstance(constructedPropertyType);

            //Recorremos cada propiedad y se la asignamos.
            originalProperty.GetType().GetProperties().Where(obj => obj.Name != "Item").ToList().ForEach(newObjectProperty =>
            {

                this.CloneProperties(originalProperty, newObjectCreated, newObjectProperty);
            });

            return newObjectCreated;
        }


        /// <summary>
        /// Genera un duplicado de un ObservableCollection
        /// </summary>
        /// <param name="originalCollection"></param>
        /// <param name="nameProperty"></param>
        /// <returns></returns>
        private object GenerateObjectCollection(object originalCollection)
        {

            //Recuperamos el tipo del que es la collección.
            var constructedTypeList = originalCollection.GetType();

            //Recuperamos el tipo de elementos que espera recibir el método Add
            MethodInfo methodAdd = constructedTypeList.GetMethod("Add");
            Type typeElements = methodAdd.GetParameters()[0].ParameterType;

            //Creamos el nuevo ObservableCollection.
            System.Collections.IList newObservableCollection = (System.Collections.IList)Activator.CreateInstance(constructedTypeList);

            //Volcamos los elementos de la lista Original, al nuevo ObservableCollection.
            System.Collections.IEnumerable enumerableOriginalList = (System.Collections.IEnumerable)originalCollection;
            foreach (var element in enumerableOriginalList)
            {

                //Detectamos si el elemento es un elemento primario.
                if (element.GetType().IsPrimitive ||
                    element.GetType().IsArray ||
                    element.GetType().IsEnum ||
                    element.GetType().Equals(typeof(string)) ||
                    element.GetType().Equals(typeof(Guid)) ||
                    element.GetType().Equals(typeof(DateTime)) ||
                    element.GetType().Equals(typeof(byte[])))
                {

                    //Realizamos la conversión dinámica.
                    dynamic elementCorrectType = Convert.ChangeType(element, element.GetType());

                    //Agregamos el elemento.
                    newObservableCollection.Add(elementCorrectType);
                }
                else
                {

                    //Creamos una nueva instancia del elemento.
                    object newElement = Activator.CreateInstance(element.GetType());

                    //Copiamos las propiedes
                    this.CloneProperties(element, newElement);

                    //Realizamos la conversión dinámica.
                    dynamic convertedElement = Convert.ChangeType(newElement, element.GetType());

                    //Agregamos el elemento.
                    newObservableCollection.Add(convertedElement);
                }
            }

            //Devolvemos la nueva ObservableCollection.
            return newObservableCollection;
        }
        #endregion
    }

De tal forma, que cuando se quiera controlar el Tracking valdría con lo siguiente:
Código:
PersonModel Model = new PersonModel();

//Se realiza una copia, para luego poder restaurarla.
Model.Tracking();


//Después.. cuando se quiera cancelar los cambios, y restaurar el objeto
Model.Rollback();

Espero que os sirva. Un saludo.
__________________
Charlie.

Última edición por chcma; 23/02/2015 a las 02:57

Etiquetas: modelos, rollback, wpf
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 01:13.