Condividi tramite


Definire un criterio di blocco per creare segmenti di sola lettura

L'API Immutability di Visual Studio Visualization and Modeling SDK consente a un programma di bloccare parte o tutto un modello DSL (Domain-Specific Language) in modo che possa essere letto ma non modificato. Questa opzione di sola lettura può essere usata, ad esempio, in modo che un utente possa chiedere ai colleghi di annotare ed esaminare un modello DSL, ma può impedire la modifica dell'originale.

Inoltre, come autore di un linguaggio DSL, è possibile definire un criterio di blocco. Un criterio di blocco definisce quali blocchi sono consentiti, non consentiti o obbligatori. Ad esempio, quando si pubblica un linguaggio DSL, è possibile incoraggiare gli sviluppatori di terze parti a estenderlo con nuovi comandi. Ma è anche possibile usare un criterio di blocco per impedirne la modifica dello stato di sola lettura delle parti specificate del modello.

Nota

Un criterio di blocco può essere aggirato tramite reflection. Fornisce un limite chiaro per gli sviluppatori di terze parti, ma non offre una sicurezza avanzata.

Altre informazioni ed esempi sono disponibili in Visual Studio Visualization and Modeling SDK.More information and samples are available at Visual Studio Visualization and Modeling SDK.

Nota

Il componente Trasformazione modello di testo viene installato automaticamente come parte del carico di lavoro sviluppo di estensioni di Visual Studio. È anche possibile installarlo dalla scheda Singoli componenti di Programma di installazione di Visual Studio, nella categoria SDK, librerie e framework. Installare il componente Modeling SDK dalla scheda Singoli componenti .

Impostazione e recupero di blocchi

È possibile impostare blocchi nell'archivio, in una partizione o in un singolo elemento. Ad esempio, questa istruzione impedisce l'eliminazione di un elemento del modello e impedisce anche la modifica delle relative proprietà:

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

È possibile usare altri valori di blocco per impedire modifiche apportate a relazioni, creazione di elementi, spostamento tra partizioni e riordinamento dei collegamenti in un ruolo.

I blocchi si applicano sia alle azioni dell'utente che al codice del programma. Se il codice del programma tenta di apportare una modifica, viene generata un'eccezione InvalidOperationException . I blocchi vengono ignorati in un'operazione Annulla o Ripeti.

È possibile determinare se un elemento dispone di un blocco in un determinato set usando IsLocked(Locks) ed è possibile ottenere il set corrente di blocchi su un elemento utilizzando GetLocks().

È possibile impostare un blocco senza usare una transazione. Il database di blocco non fa parte dell'archivio. Se si imposta un blocco in risposta a una modifica di un valore nell'archivio, ad esempio in OnValueChanged, è necessario consentire le modifiche che fanno parte di un'operazione Annulla.

Questi metodi sono metodi di estensione definiti nello spazio dei Microsoft.VisualStudio.Modeling.Immutability nomi .

Blocchi su partizioni e archivi

I blocchi possono essere applicati anche alle partizioni e all'archivio. Un blocco impostato in una partizione si applica a tutti gli elementi della partizione. Di conseguenza, l'istruzione seguente impedisce l'eliminazione di tutti gli elementi di una partizione, indipendentemente dagli stati dei propri blocchi. Tuttavia, altri blocchi, Locks.Property ad esempio, possono ancora essere impostati su singoli elementi:

partition.SetLocks(Locks.Delete);

Un blocco impostato nell'archivio si applica a tutti i relativi elementi, indipendentemente dalle impostazioni di tale blocco sulle partizioni e sugli elementi.

Uso dei blocchi

È possibile usare blocchi per implementare schemi come gli esempi seguenti:

  • Non consentire modifiche a tutti gli elementi e le relazioni, ad eccezione di quelli che rappresentano i commenti. Questo approccio consente agli utenti di annotare un modello senza modificarlo.

  • Non consentire le modifiche nella partizione predefinita, ma consentire le modifiche nella partizione del diagramma. L'utente può riorganizzare il diagramma, ma non può modificare il modello sottostante.

  • Non consentire modifiche all'archivio, ad eccezione di un gruppo di utenti registrati in un database separato. Per altri utenti, il diagramma e il modello sono di sola lettura.

  • Non consentire modifiche al modello se una proprietà booleana del diagramma è impostata su true. Specificare un comando di menu per modificare tale proprietà. Questo approccio consente agli utenti di non apportare modifiche accidentalmente.

  • Non consentire l'aggiunta e l'eliminazione di elementi e relazioni di classi specifiche, ma consentono modifiche alle proprietà. Questo approccio fornisce agli utenti un modulo fisso in cui possono riempire le proprietà.

Valori di blocco

I blocchi possono essere impostati in un archivio, una partizione o un singolo ModelElement. Locks è un'enumerazione Flags : è possibile combinarne i valori usando '|'.

  • I blocchi di un oggetto ModelElement includono sempre i blocchi della relativa partizione.

  • I blocchi di una partizione includono sempre i blocchi dell'archivio.

    Non è possibile impostare un blocco su una partizione o un archivio e allo stesso tempo disabilitare il blocco su un singolo elemento.

Valore Significato se IsLocked(Value) è true
None Nessuna restrizione.
Proprietà Non è possibile modificare le proprietà di dominio degli elementi. Questo valore non si applica alle proprietà generate dal ruolo di una classe di dominio in una relazione.
Aggiungi Non è possibile creare nuovi elementi e collegamenti in una partizione o in un archivio. Non applicabile a ModelElement.
Sposta L'elemento non può essere spostato tra partizioni se element.IsLocked(Move) è true o se targetPartition.IsLocked(Move) è true.
Elimina Un elemento non può essere eliminato se questo blocco viene impostato sull'elemento stesso o su uno degli elementi a cui l'eliminazione si propaga, ad esempio elementi e forme incorporati. È possibile usare element.CanDelete() per determinare se è possibile eliminare un elemento.
Riordinare Non è possibile modificare l'ordinamento dei collegamenti in un giocatore di ruolo.
RolePlayer Non è possibile modificare il set di collegamenti originati in questo elemento. Ad esempio, i nuovi elementi non possono essere incorporati in questo elemento. Questo valore non influisce sui collegamenti per i quali questo elemento è la destinazione. Se questo elemento è un collegamento, l'origine e la destinazione non sono interessate.
Tutte le date OR bit per bit degli altri valori.

Criteri di blocco

Come autore di un linguaggio DSL, è possibile definire un criterio di blocco. Un criterio di blocco modera l'operazione di SetLocks(), in modo che sia possibile impedire l'impostazione di blocchi specifici o imporre l'impostazione di blocchi specifici. In genere, si userebbe un criterio di blocco per scoraggiare gli utenti o gli sviluppatori a violare accidentalmente l'uso previsto di un dsl, nello stesso modo in cui è possibile dichiarare una variabile private.

È anche possibile usare un criterio di blocco per impostare blocchi su tutti gli elementi dipendenti dal tipo dell'elemento. Questo comportamento è dovuto al fatto che SetLocks(Locks.None) viene sempre chiamato quando un elemento viene creato o deserializzato per la prima volta dal file.

Tuttavia, non è possibile usare un criterio per variare i blocchi di un elemento durante la sua vita. Per ottenere tale effetto, è necessario usare le chiamate a SetLocks().

Per definire un criterio di blocco:

  • Creare una classe che implementi ILockingPolicy.

  • Aggiungere questa classe ai servizi disponibili tramite DocData del linguaggio DSL.

Per definire un criterio di blocco

ILockingPolicy ha la definizione seguente:

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

Questi metodi vengono chiamati quando viene effettuata una chiamata a SetLocks() in un archivio, una partizione o ModelElement. In ogni metodo viene fornito un set proposto di blocchi. È possibile restituire il set proposto oppure aggiungere e sottrarre blocchi.

Ad esempio:

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;
    }

Per assicurarsi che gli utenti possano sempre eliminare elementi, anche se altre chiamate di codice SetLocks(Lock.Delete):

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

Per impedire la modifica in tutte le proprietà di ogni elemento di MyClass:

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

Per rendere i criteri disponibili come servizio

DslPackage Nel progetto aggiungere un nuovo file contenente codice simile all'esempio seguente:

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