Seguimiento de cambios en objetos y concurrencia optimista

Última modificación: jueves, 04 de marzo de 2010

Hace referencia a: SharePoint Foundation 2010

En este artículo
Identidad de entidades
Simultaneidad optimista y discrepancias
Eventos, métodos y propiedades críticos

Puede agregar, eliminar y editar elementos de lista y campos específicos de los elementos de lista usando el proveedor LINQ to SharePoint. El proveedor realiza un seguimiento de los cambios que realiza el código a los objetos que representan entidades en la base de datos de contenido. Además, antes de escribir los cambios, determina si otros usuarios cambiaron las entidades después de que el código las recuperó. En este tema se describe el sistema de seguimiento de cambios en el objeto. Para obtener más información sobre cómo realizar cambios a datos de Microsoft SharePoint Foundation, vea How to: Write to the Content Databases Using LINQ to SharePoint.

Identidad de entidades

El proveedor LINQ to SharePoint realiza un seguimiento de todas las entidades que devuelven las consultas y de todos los cambios en dichas entidades. Cuando una entidad específica se devuelve más de una vez, el proveedor siempre devuelve la misma instancia de la entidad que devolvió la primera vez. Este comportamiento garantiza que una aplicación siempre funcione con la misma instancia de una entidad específica y que nunca funcione con una entidad modificada por otra aplicación.

Simultaneidad optimista y discrepancias

Cuando se ejecuta DataContext.SubmitChanges(), compara el estado actual de la entidad de la base de datos con el estado de la entidad cuando el proveedor LINQ to SharePoint la devolvió por primera vez (después de la última llamada del método DataContext.SubmitChanges() method). Si hay una discrepancia, significa que otra aplicación modificó la entidad después de la primera recuperación. Los desarrolladores de aplicaciones pueden configurar el comportamiento de SubmitChanges() para que no escriba más cambios cuando encuentra la primera discrepancia, o bien para que continúe escribiendo cambios a la base de datos y registrando cada conflicto que encuentre. Los cambios relacionados con un campo implicado en un conflicto no se confirman en la base de datos. El código de llamada debe resolver las discrepancias antes de que se pueda volver a llamar al método DataContext.SubmitChanges(). En un escenario de simultaneidad optimista, la resolución normalmente consiste en avisar al usuario sobre la discrepancia y permitirle decidir si desea cancelar los cambios y dejar los cambios de los demás usuarios, o bien sobrescribir los cambios de los demás usuarios.

Eventos, métodos y propiedades críticos

En las secciones siguientes se proporciona información general sobre las principales clases y miembros que usa el código para aprovechar el sistema de seguimiento de cambios en los objetos y para implementar la escritura a la base de datos de contenido con simultaneidad optimista.

Seguimiento de estado de la entidad y valores originales

El estado de una entidad con respecto a si ha cambiado, y en ese caso cómo lo ha hecho, se almacena en la propiedad EntityState de la clase que representa la entidad. Esta propiedad se declara en la interfaz de ITrackEntityState, que se debe implementar en cualquier clase que represente entidades que se desean incluir en el sistema de seguimiento de cambios en los objetos. En general, es conveniente que las clases representan tipos de contenido implementen la interfaz ITrackEntityState, ya que cada objeto de dicha clase representa un elemento de lista específico y hay propiedades de la clase que representan los campos del elemento de lista.

Es posible que el código deba cancelar cambios si se detecta un conflicto de simultaneidad, por lo que estas mismas clases de tipo de contenido también necesitan almacenar los valores originales de los campos. Para garantizar esto, estas clases deben implementar la interfaz de ITrackOriginalValues. Esta interfaz define una propiedad OriginalValues, que es un diccionario de nombres y valores de propiedades. Cuando se modifica una propiedad de un objeto que crea una instancia de la clase, se agrega una entrada en el diccionario que registra el valor original de la propiedad.

Todas las clases de tipo de contenido heredan del tipo de contenido Item básico de SharePoint Foundation. Por esa razón, normalmente, solo la clase que representa el tipo de contenido Item implementa ITrackEntityState y ITrackOriginalValues.

Recomendamos usar la herramienta SPMetal para generar las declaraciones de clases de entidad. Esta herramienta genera, de forma predeterminada, una declaración de una clase Item para representar el tipo de contenido Item y la configura para implementar ITrackEntityState y ITrackOriginalValues. En el ejemplo siguiente se han quitado los detalles irrelevantes para facilitar su legibilidad:

[Microsoft.SharePoint.Linq.ContentTypeAttribute(Name="Item", Id="0x01")]
public partial class Item : ITrackEntityState, ITrackOriginalValues, INotifyPropertyChanged, INotifyPropertyChanging
{
    private EntityState _entityState;
    
    private IDictionary<string, object> _originalValues;

    EntityState EntityState { 
        get {
            return this._entityState;
        }
        set {
            this._entityState = value;
        }
    }

    IDictionary<string, object> OriginalValues {
        get {
            return this._originalValues;
        }
        set {
            this._originalValues = value;
        }
    }
    
    public Item() {
        this._entityState = EntityState.Unchanged;
        this.OnCreated();
    }

    // Other member declarations suppressed for readability.
}

Por último, estas clases de tipo de contenido (o la clase Item de la que heredan) deben implementar INotifyPropertyChanged y INotifyPropertyChanging. El código siguiente muestra cómo SPMetal implementa estas interfaces en la clase Item.

[Microsoft.SharePoint.Linq.ContentTypeAttribute(Name="Item", Id="0x01")]
public partial class Item : ITrackEntityState, ITrackOriginalValues, INotifyPropertyChanged, INotifyPropertyChanging
{
    // Material omitted.

    public event PropertyChangedEventHandler PropertyChanged;
    
    public event PropertyChangingEventHandler PropertyChanging;
    
    protected virtual void OnPropertyChanged(string propertyName) {
        if ((EntityState.Unchanged == this._entityState)) {
            this._entityState = EntityState.ToBeUpdated;
        }
        if ((this.PropertyChanged != null)) {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    protected virtual void OnPropertyChanging(string propertyName, object value) {
        if ((null == _originalValues)) {
            this._originalValues = new Dictionary<string, object>();
        }
        if ((false == _originalValues.ContainsKey(propertyName))) {
            _originalValues.Add(propertyName, value);
        }
        if ((this.PropertyChanging != null)) {
            this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
        }
    }

    // Other member declarations suppressed for readability.
}

Como se puede ver, el propósito del método OnPropertyChanging es registrar el valor original de una propiedad cuyo valor cambia y, después, generar el evento PropertyChanging() mientras que el propósito del método OnPropertyChanged es generar el evento PropertyChanged() después de que establece el estado de la entidad del objeto de elemento de lista en ToBeUpdated. Este valor significa que se ha modificado al menos una de las propiedades del objeto de forma que, salvo que haya un conflicto de simultaneidad, la siguiente llamada de SubmitChanges() escribirá dicho cambio al campo correspondiente de la base de datos de contenido. La intención es que el descriptor de acceso set de una propiedad que representa un campo del elemento de lista llame a estos dos métodos. Por ejemplo, el siguiente ejemplo demuestra cómo SPMetal crea la propiedad de la clase Item que representa el campo Title de un elemento de lista. Esta versión del código generado se ha simplificado para facilitar su legibilidad.

[Column(Name="Title", Storage="_title", Required=true, FieldType="Text")]
public string Title {
    get {
        return this._title;
    }
    set {
         if ((this._title != value)) {
            this.OnPropertyChanging("Title", this._title);
            this._title = value;
            this.OnPropertyChanged("Title");
         }
    }
}

Clase DataContext

La clase DataContext representa las listas y los elementos de lista de un sitio web de SharePoint Foundation; por esta razón, puede considerarse un subconjunto de la base de datos de contenido. Tiene tres miembros que son críticos para el sistema de seguimiento de cambios en los objetos:

  • ObjectTrackingEnabled – Esta propiedad debe establecerse en true antes de que el código pueda realizar cambios en los datos de SharePoint Foundation.

  • SubmitChanges() – Este método escribe todos los cambios realizados después de la última vez que se lo llamó a la base de datos de contenido, siempre que no se encuentren conflictos de simultaneidad. Si se encuentra un conflicto, su comportamiento varía según los parámetros que se le pasen. Puede indicársele que inicie un ChangeConflictException en cuanto se encuentre un conflicto y que deje de escribir cambios. O bien puede indicársele que posponga el inicio de la excepción y que continúe escribiendo los cambios que no afectan a un campo implicado en un conflicto a la base de datos de contenido. En cualquiera de los dos casos, almacenará uno o varios objetos que representan conflictos en la propiedad ChangeConflicts.

  • ChangeConflicts – Esta propiedad contiene objetos que representan conflictos de simultaneidad detectados la última vez que se llamó a SubmitChanges() y que se detectó al menos un conflicto de simultaneidad.

Clase EntityList<T>

La clase EntityList<TEntity> representa una lista del sitio web. Posee varios métodos para escribir a la base de datos de contenido. Los dos siguientes son los más críticos:

Clase MemberChangeConflict

La clase MemberChangeConflict representa un posible conflicto de simultaneidad con respecto a un campo particular en un elemento de lista específico. Sus miembros más importantes son:

  • OriginalValue – Esta propiedad representa el valor del campo cuando se recuperó la última vez de la base de datos de contenido o inmediatamente después de la última vez que SubmitChanges() se ejecutó correctamente.

  • DatabaseValue – Esta propiedad representa el valor actual del campo de la base de datos de contenido. Si este valor es diferente de OriginalValue, significa que un proceso de otro usuario cambió el campo y MemberChangeConflict representa un conflicto de simultaneidad real.

  • CurrentValue – Esta propiedad representa el valor más reciente del campo en el proceso del código. (También se conoce como "valor cliente", aunque aquí "cliente” se refiere al servidor front-end web). Si el código cambió el valor del campo, CurrentValue es diferente de OriginalValue.

  • Resolve() – Este método resuelve un conflicto eligiendo qué valor debería aplicarse al campo para la siguiente llamada de SubmitChanges().

Los objetos MemberChangeConflict se almacenan en la propiedad MemberConflicts de un objeto ObjectChangeConflict.

Nota importanteImportante

Si cualquier campo de un elemento de lista muestra un conflicto de simultaneidad, es decir si DatabaseValue es diferente de OriginalValue, se crea una instancia de MemberChangeConflict no solo para dicho campo sino también para cualquier campo en que DatabaseValue sea diferente de CurrentValue. Esto ocurre porque a menudo los cambios en los campos de un objeto de lista deben realizarse siguiendo el criterio "todos o ninguno". Por ejemplo, si hay un conflicto de simultaneidad en un campo de código postal y la resolución es dejar los cambios del otro usuario, los demás campos de una dirección, como ciudad, tampoco deberían cambiarse. Por esta razón, deben representarse todas las discrepancias entre los valores cliente y de base de datos para la resolución.

Clase ObjectChangeConflict

La clase ObjectChangeConflict representa un elemento de lista para el que existe un conflicto de simultaneidad para al menos uno de sus campos. Sus dos miembros más importantes son:

Clase ChangeConflictCollection

Un objeto ChangeConflictCollection es una colección de todos los objetos ObjectChangeConflict que genera una llamada a SubmitChanges() que ha detectado al menos un conflicto de simultaneidad. Su miembro más importante es el método ResolveAll(), que permite llamar código para resolver todos los conflictos secundarios con una sola llamada al método.

Los objetos ChangeConflictCollection se almacenan en la propiedad DataContext.ChangeConflicts.

Vea también

Otros recursos

How to: Write to the Content Databases Using LINQ to SharePoint