Partager via


Suivi des modifications d’objets et accès concurrentiel optimiste

Dernière modification : jeudi 4 mars 2010

S’applique à : SharePoint Foundation 2010

Dans cet article
Identité de l’entité
Accès concurrentiel optimiste et différences
Propriétés, méthodes et événements critiques

Vous pouvez ajouter, supprimer et modifier des éléments de liste et des champs spécifiques dans ces derniers à l’aide du fournisseur LINQ to SharePoint. Ce fournisseur assure le suivi des modifications apportées par votre code aux objets représentant des entités dans la base de données de contenu. De plus, avant d’écrire vos modifications, il détermine si les entités ont été modifiées par d’autres utilisateurs après avoir été récupérées par votre code. Cette rubrique décrit le système de suivi des modifications d’objets. Pour plus d’informations sur la modification des données Microsoft SharePoint Foundation, voir How to: Write to the Content Databases Using LINQ to SharePoint.

Identité de l’entité

Le fournisseur LINQ to SharePoint assure le suivi de toutes les entités renvoyées par les requêtes et de toutes les modifications apportées à celles-ci. Lorsqu’une entité spécifiée est renvoyée plusieurs fois, le fournisseur renvoie toujours la même instance de l’entité qui a été renvoyée la première fois. Ce comportement permet de s’assurer qu’une application utilise toujours la même instance d’une entité spécifiée et qu’elle n’utilise jamais une entité qui a été modifiée par une autre application.

Accès concurrentiel optimiste et différences

Lorsque DataContext.SubmitChanges() s’exécute, il compare l’état actuel de l’entité dans la base de donnée à celui de l’entité renvoyée la première fois par le fournisseur LINQ to SharePoint (après le dernier appel de la DataContext.SubmitChanges() méthode). Si ces états diffèrent, cela signifie que l’entité a été modifiée par une autre application après sa première récupération. Les développeurs d’applications peuvent configurer le comportement de la propriété SubmitChanges() de sorte qu’elle arrête d’écrire d’autres modifications dès détection de la première différence ou cette propriété peut être définie pour continuer à écrire les modifications dans la base de données et enregistrer chaque conflit détecté. Les modifications relatives à un champ impliqué dans un conflit ne sont pas validées dans la base de données. Le code appelant doit résoudre les différences avant que la méthode DataContext.SubmitChanges() puisse être de nouveau appelée. Dans un scénario d’accès concurrentiel optimiste, la résolution consiste en règle générale à demander à l’utilisateur des informations sur les différences et à lui permettre de décider si les modifications doivent être annulées ou non et à conserver ou remplacer les modifications apportées par l’autre utilisateur.

Propriétés, méthodes et événements critiques

Les sections ci-après donnent une vue d’ensemble des principales classes et des principaux membres utilisés par votre code pour exploiter le système de suivi des modifications d’objets et implémenter l’écriture dans la base de données de contenu en cas d’accès concurrentiel optimiste.

Suivi de l’état de l’entité et des valeurs d’origine

L’état d’une entité (indiquant si elle a été modifiée ou non et le type de modification) est stocké dans la propriété EntityState de la classe qui représente l’entité. Cette propriété est déclarée dans l’interface ITrackEntityState que vous devez implémenter pour toute classe représentant les entités à inclure dans le système de suivi des modifications d’objets. En règle générale, vous souhaitez que les classes représentant des types de contenu implémentent l’interfaceITrackEntityState, car chaque objet d’une telle classe représente un élément de liste spécifique, et la classe contient les propriétés représentant les champs de l’élément de liste.

Il est possible que votre code doive annuler les modifications en cas de détection de conflit d’accès concurrentiel. Dans ce cas, ces mêmes classes de type de contenu doivent également stockées les valeurs d’origine de leurs champs. Pour ce faire, ces classes doivent implémenter l’interface ITrackOriginalValues . Cette interface définit une propriété OriginalValues qui est un dictionnaire des noms et valeurs des propriétés. Lorsqu’une propriété d’un objet qui instancie la classe est modifiée, une entrée est ajoutée au dictionnaire qui enregistre la valeur d’origine de la propriété.

Toutes les classes de type de contenu héritent du type de contenu Élément de base de SharePoint Foundation. En règle générale, seule la classe représentant le type de contenu Élément implémente donc ITrackEntityState et ITrackOriginalValues.

Il est recommandé d’utiliser l’outil SPMetal pour générer les déclarations de classe d’entité. Par défaut, cet outil génère une déclaration d’une classe Item pour représenter le type de contenu Élément et la configure pour implémenter ITrackEntityState et ITrackOriginalValues. Voici un exemple simplifié à des fins de lisibilité :

[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.
}

Enfin, ces classes de type de contenu (ou la classe Item dont elles héritent) doivent implémenter INotifyPropertyChanged et INotifyPropertyChanging. Le code ci-après montre comment SPMetal implémente ces interfaces dans la classe 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.
}

Comme vous pouvez le constater, le rôle de la méthode OnPropertyChanging est d’enregistrer la valeur d’origine d’une propriété lorsque celle-ci change et de déclencher l’événement PropertyChanging(), tandis que celui de la méthode OnPropertyChanged est de déclencher l’événement PropertyChanged() après avoir défini l’état d’identité de l’objet d’élément de liste sur ToBeUpdated. Cette valeur signifie qu’au moins une des propriétés de l’objet a été modifiée. À l’exception d’un conflit d’accès concurrentiel, l’appel suivant de SubmitChanges() écrit cette modification dans le champ correspondant de la base de données de contenu. Le but est que ces deux méthodes soient appelées par l’accesseur set d’une propriété qui représente un champ dans l’élément de liste. Par exemple, le code ci-après montre comment SPMetal crée la propriété de la classe Item qui représente le champ Titre d’un élément de liste. Cette version du code généré a été simplifié à des fins de lisibilité.

[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");
         }
    }
}

Classe DataContext

La classe DataContext représente les listes et les éléments de liste d’un site Web SharePoint Foundation. Pour cette raison, elle peut être comparée à un sous-ensemble de la base de données de contenu. Elle comporte trois membres qui sont essentiels au système de suivi des modifications d’objets :

  • ObjectTrackingEnabled : la valeur true doit être attribuée au préalable à cette propriété pour que le code puisse apporter des modifications aux données SharePoint Foundation.

  • SubmitChanges() : cette méthode écrit toute les modifications apportées depuis son dernier appel dans la base de données de contenu, à condition qu’aucun confit d’accès concurrentiel ne soit détecté. Si un conflit est détecté, son comportement varie en fonction des paramètres qui lui ont été transmis. Il peut lui être demandé de déclencher une exception ChangeConflictException dès qu’un conflit est détecté et d’arrêter d’écrire les modifications. Il peut également lui être demandé de différer le déclenchement de l’exception et de continuer à écrire dans la base de données de contenu toute modification qui n’a aucune incidence sur un champ impliqué dans un conflit. Dans les deux cas, elle stocke un ou plusieurs objets qui représentent des conflits dans la propriété ChangeConflicts.

  • ChangeConflicts cette propriété contient les objets représentant les conflits d’accès concurrentiel détectés la dernière fois que la méthode SubmitChanges() a été appelée et qu’au moins un conflit a été détecté.

Classe EntityList<T>

La classe EntityList<TEntity> représente une liste sur le site Web. Elle contient plusieurs méthodes pour écrire dans la base de données de contenu. Les deux méthodes suivantes sont les plus importantes :

  • InsertOnSubmit(TEntity) : cette méthode marque un élément de liste spécifié comme devant être ajouté à la liste.

  • DeleteOnSubmit(TEntity) : cette méthode marque un élément de liste spécifié comme devant être supprimé de la liste.

Classe MemberChangeConflict

La classe MemberChangeConflict représente un conflit d’accès concurrentiel possible en ce qui concerne un champ spécifique d’un élément de liste en particulier. Ses membres les plus importants comptent les suivants :

  • OriginalValue : cette propriété représente la valeur du champ lors de sa dernière extraction de la base de données de contenu ou immédiatement après la dernière exécution de SubmitChanges().

  • DatabaseValue : cette propriété représente la valeur actuelle du champ dans la base de données de contenu. Si cette valeur est différente de OriginalValue, cela signifie qu’un processus d’un autre utilisateur a modifié le champ, et MemberChangeConflict représente un conflit d’accès concurrentiel actuel.

  • CurrentValue : cette propriété représente la valeur la plus récente du champ dans le processus de votre code. (Cette valeur est également appelée « valeur cliente » bien que client fait référence ici au serveur Web frontal.) Si votre code a modifié la valeur du champ, CurrentValue diffère de OriginalValue.

  • Resolve() : cette méthode résout un conflit en choisissant quelle valeur doit être appliquée au champ au prochain appel de SubmitChanges().

Les objets MemberChangeConflict sont stockés dans la propriété MemberConflicts d’un objet ObjectChangeConflict.

Important

Si un champ d’un élément de liste fait l’objet d’un conflit d’accès concurrentiel, c’est-à-dire si DatabaseValue diffère de OriginalValue, un objet MemberChangeConflict est instancié non seulement pour ce champ mais aussi pour tous les champs dans lesquels DatabaseValue diffère de CurrentValue. Cela se produit, car les modifications des champs d’un objet de liste doivent souvent être toutes apportées ou pas du tout apportées. Par exemple, s’il existe un conflit d’accès concurrentiel pour un champ de code postal et si la résolution de ce conflit consiste à conserver les modifications de l’autre utilisateur, les autres champs d’une adresse, tels que la ville, doivent également ne pas être modifiés. Pour cette raison, toutes les différences entre les valeurs cliente et de la base de données doivent être représentées en vue de leur résolution.

Classe ObjectChangeConflict

La classe ObjectChangeConflict représente un élément de liste pour lequel il existe un conflit d’accès concurrentiel pour au moins un de ses champs. Ses deux membres les plus importants sont les suivants :

Classe ChangeConflictCollection

Un objet ChangeConflictCollection est une collection de tous les objets ObjectChangeConflict générés par un appel à SubmitChanges() ayant détecté au mois un conflit concurrentiel. Son membre le plus important est la méthode ResolveAll() qui permet au code appelant de résoudre tous les conflits enfants avec un seul appel de méthode.

Les objets ChangeConflictCollection sont stockés dans la propriétés DataContext.ChangeConflicts.

Voir aussi

Autres ressources

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