Définir une stratégie de verrouillage pour créer des segments en lecture seule

L’API Immuabilité du Kit de développement logiciel (SDK) Visualisation et modélisation Visual Studio permet à un programme de verrouiller une partie ou l’ensemble d’un modèle de langage spécifique au domaine (DSL) afin qu’il puisse être lu, mais pas modifié. Cette option en lecture seule peut être utilisée, par exemple, afin qu’un utilisateur puisse demander à ses collègues d’annoter et de passer en revue un modèle DSL, mais peut les interdire de modifier l’original.

En outre, en tant qu’auteur d’une DSL, vous pouvez définir une stratégie de verrouillage. Une stratégie de verrouillage définit les verrous autorisés, non autorisés ou obligatoires. Par exemple, lorsque vous publiez un DSL, vous pouvez encourager les développeurs tiers à l’étendre avec de nouvelles commandes. Toutefois, vous pouvez également utiliser une stratégie de verrouillage pour les empêcher de modifier l’état en lecture seule des parties spécifiées du modèle.

Notes

Une stratégie de verrouillage peut être contournée à l’aide de la réflexion. Elle fournit une limite claire pour les développeurs tiers, mais ne fournit pas de sécurité forte.

Pour plus d’informations et d’exemples, consultez le Kit de développement logiciel (SDK) de visualisation et modélisation Visual Studio.

Notes

Le composant Transformation de modèle de texte est automatiquement installé dans le cadre de la charge de travail Développement d’extensions Visual Studio. Vous pouvez aussi l’installer à partir de l’onglet Composants individuels de Visual Studio Installer, sous la catégorie SDK, bibliothèques et frameworks. Installez le composant Modélisation de SDK à partir de l’onglet Composants individuels.

Définition et obtention de verrous

Vous pouvez définir des verrous sur le magasin, sur une partition ou sur un élément individuel. Par exemple, cette instruction empêche la suppression d’un élément de modèle et empêche également la modification de ses propriétés :

using Microsoft.VisualStudio.Modeling.Immutability; ...
element.SetLocks(Locks.Delete | Locks.Property);

D’autres valeurs de verrou peuvent être utilisées pour empêcher les modifications apportées aux relations, la création d’éléments, le déplacement entre les partitions et la réorganisation des liens dans un rôle.

Les verrous s’appliquent à la fois aux actions utilisateur et au code de programme. Si le code du programme tente d’apporter une modification, un InvalidOperationException est levé. Les verrous sont ignorés dans une opération Annuler ou Rétablir.

Vous pouvez découvrir si un élément a un verrou dans un jeu donné à l’aide de IsLocked(Locks) et vous pouvez obtenir l’ensemble actuel de verrous sur un élément à l’aide de GetLocks().

Vous pouvez définir un verrou sans utiliser de transaction. La base de données de verrouillage ne fait pas partie du magasin. Si vous définissez un verrou en réponse à une modification d’une valeur dans le magasin, par exemple dans OnValueChanged, vous devez autoriser les modifications qui font partie d’une opération d’annulation.

Ces méthodes sont des méthodes d’extension définies dans l’espace de noms Microsoft.VisualStudio.Modeling.Immutability.

Verrous sur les partitions et les magasins

Les verrous peuvent également être appliqués aux partitions et au magasin. Un verrou défini sur une partition s’applique à tous les éléments de la partition. Par exemple, l’instruction suivante empêche la suppression de tous les éléments d’une partition, quels que soient les états de leurs propres verrous. Néanmoins, d’autres verrous tels que Locks.Property peuvent toujours être définis sur des éléments individuels :

partition.SetLocks(Locks.Delete);

Un verrou défini sur le magasin s’applique à tous ses éléments, quels que soient les paramètres de ce verrou sur les partitions et les éléments.

Utilisation de verrous

Vous pouvez utiliser des verrous pour implémenter des schémas tels que les exemples suivants :

  • Interdire les modifications apportées à tous les éléments et relations, sauf celles qui représentent des commentaires. Cette approche permet aux utilisateurs d’annoter un modèle sans le modifier.

  • Interdire les modifications dans la partition par défaut, mais autoriser les modifications dans la partition de diagramme. L’utilisateur peut réorganiser le diagramme, mais ne peut pas modifier le modèle sous-jacent.

  • Interdire les modifications apportées au magasin, à l’exception d’un groupe d’utilisateurs inscrits dans une base de données distincte. Pour les autres utilisateurs, le diagramme et le modèle sont en lecture seule.

  • Interdire les modifications apportées au modèle si une propriété booléenne du diagramme a la valeur true. Fournissez une commande de menu pour modifier cette propriété. Cette approche permet de s’assurer que les utilisateurs ne modifient pas accidentellement les modifications.

  • Interdire l’ajout et la suppression d’éléments et de relations de classes particulières, mais autoriser les modifications de propriété. Cette approche fournit aux utilisateurs un formulaire fixe dans lequel ils peuvent remplir les propriétés.

Valeurs de verrou

Les verrous peuvent être définis sur un magasin, une partition ou un ModelElement individuel. Les verrous sont une énumération Flags : vous pouvez combiner ses valeurs à l’aide de « | ».

  • Les verrous d’un ModelElement incluent toujours les verrous de sa partition.

  • Les verrous d’une partition incluent toujours les verrous du magasin.

    Vous ne pouvez pas définir de verrou sur une partition ou un magasin et désactiver le verrou sur un élément individuel.

Valeur Signification si IsLocked(Value) est true
None Aucune restriction.
Propriété Les propriétés de domaine des éléments ne peuvent pas être modifiées. Cette valeur ne s’applique pas aux propriétés générées par le rôle d’une classe de domaine dans une relation.
Ajouter Les nouveaux éléments et liens ne peuvent pas être créés dans une partition ou un magasin. Non applicable à ModelElement.
Déplacer L’élément ne peut pas être déplacé entre les partitions si element.IsLocked(Move) ou targetPartition.IsLocked(Move) a la valeur true.
Supprimer Un élément ne peut pas être supprimé si ce verrou est défini sur l’élément lui-même ou sur l’un des éléments auxquels la suppression se propage, comme les éléments incorporés et les formes. Vous pouvez utiliser element.CanDelete() pour découvrir si un élément peut être supprimé.
Réorganiser L’ordre des liens sur un joueur de rôle ne peut pas être modifié.
RolePlayer Impossible de modifier l’ensemble de liens sources à cet élément. Par exemple, les nouveaux éléments ne peuvent pas être incorporés sous cet élément. Cette valeur n’affecte pas les liens pour lesquels cet élément est la cible. Si cet élément est un lien, sa source et sa cible ne sont pas affectées.
Tous Au niveau du bit OU des autres valeurs.

Stratégies de verrouillage

En tant qu’auteur d’une DSL, vous pouvez définir une stratégie de verrouillage. Une stratégie de verrouillage modérera l’opération de SetLocks(), afin que vous puissiez empêcher que des verrous spécifiques ne soient définis ou que des verrous spécifiques doivent être définis. En règle générale, vous utilisez une stratégie de verrouillage pour empêcher les utilisateurs ou les développeurs de contrevenir accidentellement à l’utilisation prévue d’un DSL, de la même façon que vous pouvez déclarer une variable private.

Vous pouvez également utiliser une stratégie de verrouillage pour définir des verrous sur tous les éléments dépendant du type de l’élément. Ce comportement est dû au fait que SetLocks(Locks.None) est toujours appelé lorsqu’un élément est créé ou désérialisé à partir d’un fichier.

Toutefois, vous ne pouvez pas utiliser de stratégie pour varier les verrous sur un élément pendant sa durée de vie. Pour atteindre cet effet, vous devez utiliser des appels à SetLocks().

Pour définir une stratégie de verrouillage :

  • Créez une classe qui implémente ILockingPolicy.

  • Ajoutez cette classe aux services disponibles via docData de votre DSL.

Définir une stratégie de verrouillage

ILockingPolicy a la définition suivante :

public interface ILockingPolicy
{
  Locks RefineLocks(ModelElement element, Locks proposedLocks);
  Locks RefineLocks(Partition partition, Locks proposedLocks);
  Locks RefineLocks(Store store, Locks proposedLocks);
}

Ces méthodes sont appelées lorsqu’un appel est effectué SetLocks() sur un magasin, une partition ou un ModelElement. Dans chaque méthode, un ensemble de verrous proposé vous est fourni. Vous pouvez retourner l’ensemble proposé, ou vous pouvez ajouter et soustraire des verrous.

Par exemple :

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Immutability;
namespace Company.YourDsl.DslPackage // Change
{
  public class MyLockingPolicy : ILockingPolicy
  {
    /// <summary>
    /// Moderate SetLocks(this ModelElement target, Locks locks)
    /// </summary>
    /// <param name="element">target</param>
    /// <param name="proposedLocks">locks</param>
    /// <returns></returns>
    public Locks RefineLocks(ModelElement element, Locks proposedLocks)
    {
      // In my policy, users can never delete an element,
      // and other developers cannot easily change that:
      return proposedLocks | Locks.Delete);
    }
    public Locks RefineLocks(Store store, Locks proposedLocks)
    {
      // Only one user can change this model:
      return Environment.UserName == "aUser"
           ? proposedLocks : Locks.All;
    }

Vous assurer que les utilisateurs peuvent toujours supprimer des éléments, même si d’autres appels de code SetLocks(Lock.Delete):

return proposedLocks & (Locks.All ^ Locks.Delete);

Interdire la modification dans toutes les propriétés de chaque élément de MyClass :

return element is MyClass ? (proposedLocks | Locks.Property) : proposedLocks;

Rendre votre stratégie disponible en tant que service

Dans votre projet DslPackage, ajoutez un nouveau fichier qui contient du code semblable à l’exemple suivant :

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Immutability;
namespace Company.YourDsl.DslPackage // Change
{
  // Override the DocData GetService() for this DSL.
  internal partial class YourDslDocData // Change
  {
    /// <summary>
    /// Custom locking policy cache.
    /// </summary>
    private ILockingPolicy myLockingPolicy = null;

    /// <summary>
    /// Called when a service is requested.
    /// </summary>
    /// <param name="serviceType">Service requested</param>
    /// <returns>Service implementation</returns>
    public override object GetService(System.Type serviceType)
    {
      if (serviceType == typeof(SLockingPolicy)
       || serviceType == typeof(ILockingPolicy))
      {
        if (myLockingPolicy == null)
        {
          myLockingPolicy = new MyLockingPolicy();
        }
        return myLockingPolicy;
      }
      // Request is for some other service.
      return base.GetService(serviceType);
    }
}