Usare correttamente le transazioni in AutoCAD .NET

CAD-Modell eines Bauteils auf dem Bildschirm mit Leistungskurve

CAD

Il Suo comando viene eseguito senza alcun messaggio di errore – e nel disegno non è cambiato nulla. O peggio: dopo un errore metà dei componenti è stata spostata, il resto è ancora al vecchio posto. Entrambi sono errori nell'uso delle transazioni, ed entrambi si possono evitare.

La transazione come tavola della colazione

Aprire un oggetto del disegno è come svitare un barattolo. Un tempo si apriva e si richiudeva ogni barattolo singolarmente, con Open e Close. La transazione è la tavola della colazione: tutto ciò che apre tramite essa resta aperto finché la transazione termina. Allora tutti i barattoli vengono chiusi in una volta sola. Con Commit le modifiche vengono mantenute, senza Commit tutto torna com'era prima.

Lo svolgimento è sempre lo stesso:

  1. db.TransactionManager.StartTransaction() avvia la transazione.
  2. tr.GetObject(id, OpenMode.ForRead) oppure OpenMode.ForWrite apre un oggetto.
  3. I nuovi oggetti vengono registrati con tr.AddNewlyCreatedDBObject(objekt, true) dopo essere stati inseriti nel database.
  4. tr.Commit() rende effettive tutte le modifiche.
  5. Dispose termina la transazione. Se manca il Commit, tutto viene annullato.
[CommandMethod("MeinKreis")]

public void ZeichneKreis()

{

    var db = Application.DocumentManager.MdiActiveDocument.Database;

    using (var tr = db.TransactionManager.StartTransaction())

    {

        var modellbereich = (BlockTableRecord)tr.GetObject(

            SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);

        var kreis = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 10.0);

        modellbereich.AppendEntity(kreis);

        tr.AddNewlyCreatedDBObject(kreis, true);

        // Senza Commit, Dispose alla fine del blocco using annulla tutto

        tr.Commit();

    }

}

Il blocco using fa sì che Dispose venga chiamato anche quando a metà strada viene sollevata un'eccezione. Un Commit dimenticato non è quindi un crash, ma un comando che apparentemente non fa nulla – un classico nei nostri corsi di formazione.

Dove nella pratica le cose vanno storte

Il commit in una transazione interna vale solo con riserva

Le transazioni si possono annidare. Quella esterna accoglie le modifiche di quella interna. Se quella esterna viene interrotta, sparisce anche ciò che quella interna aveva già confermato. Abbiamo trovato questo schema in codice reale: un metodo di supporto crea un layer in una transazione propria e conferma correttamente. Il chiamante però non conferma mai la sua transazione esterna, perché «ha solo letto». Alla fine il layer sparisce di nuovo, e nessuno sa perché. Termini inoltre le transazioni annidate sempre dall'interno verso l'esterno. Chi termina prima quella esterna rischia un crash senza alcun messaggio. I blocchi using si occupano dell'ordine da soli.

Commit nel Dispose di una classe propria

Alcune classi tengono la transazione come campo e nel loro Dispose chiamano prima Commit e poi Dispose. Sembra ordinato, ma è pericoloso: Dispose viene eseguito anche quando si esce dal blocco using a causa di un'eccezione. Allora viene salvato un lavoro a metà. Con un cerchio è fastidioso, con cento componenti è una catastrofe. Corretta è una classe che conferma in modo esplicito:

public sealed class Bearbeitung : IDisposable

{

    private readonly Transaction _tr;



    public Bearbeitung(Database db)

    {

        _tr = db.TransactionManager.StartTransaction();

    }



    public void Verschiebe(ObjectId elementId, Vector3d versatz)

    {

        var element = (Entity)_tr.GetObject(elementId, OpenMode.ForWrite);

        element.TransformBy(Matrix3d.Displacement(versatz));

    }



    // La conferma è esplicita – solo se tutto è andato a buon fine

    public void Bestätige()

    {

        _tr.Commit();

    }



    // Dispose fa solo pulizia e non conferma nulla

    public void Dispose()

    {

        _tr.Dispose();

    }

}

Il comando crea la classe in un blocco using, sposta tutti gli elementi e chiama Bestätige() solo alla fine. Se prima viene sollevata un'eccezione, il disegno resta invariato.

La transazione che nessuno termina

Se Dispose viene chiamato a mano alla fine e prima viene sollevata un'eccezione, la transazione resta aperta. AutoCAD non la elimina alla fine del comando. Resta attiva da un comando all'altro e si trova tra i piedi di ogni comando successivo come TopTransaction. Una transazione non va quindi mai in un campo statico o in un oggetto che vive più a lungo del comando.

La veloce OpenCloseTransaction

StartOpenCloseTransaction sembra una transazione, ma internamente apre con Open e Close. È più veloce, ma ha delle particolarità: TopTransaction non la conosce, e Abort non annulla gli oggetti appena creati. Restano come orfani nel database. La usi dove si legge o si modifica soltanto.

Quanto costano le transazioni

Abbiamo misurato nella console di AutoCAD 2027 con un terreno di 10.000 celle di griglia, cioè 30.000 linee. I numeri valgono solo per il nostro computer, ma le proporzioni sono istruttive: 10.000 transazioni invece di una costavano solo il 15 percento circa in più. La OpenCloseTransaction era circa due volte più veloce. Il vero freno è lo schermo: UpdateScreen dopo ogni cella costava quasi il triplo, FlushGraphics dopo ogni cella venti volte tanto. Chi vuole mostrare l'avanzamento non aggiorna quindi dopo ogni elemento, ma ogni qualche centinaio.

Errori tipici

  • Commit dimenticato: il comando viene eseguito e apparentemente non fa nulla.
  • Un Commit in una transazione interna viene considerato definitivo – finché quella esterna non viene interrotta.
  • Un Commit nel Dispose di una classe propria salva un lavoro a metà dopo un errore.
  • Una transazione non terminata sopravvive al comando e disturba tutti quelli successivi.
  • GetObject restituisce ogni volta un nuovo involucro .NET. Confronti gli oggetti tramite il loro ObjectId.
  • DowngradeOpen non ha effetto in una transazione. Apra subito nel modo giusto.
  • ObjectId.GetObject senza una transazione in corso ha fatto andare in crash AutoCAD 2027 nella nostra prova.

Come La aiutiamo

  • Formazione: transazioni, apertura e chiusura e corretto rilascio delle risorse sono il nucleo del corso «AutoCAD .NET API con C#». Per BricsCAD c'è il corso «BricsCAD .NET API con C#».
  • Code check delle transazioni: verifichiamo il Suo add-in alla ricerca di commit dimenticati, transazioni annidate e freni nel disegno.
  • Progetto: il Suo add-in è lento o lascia modifiche a metà? Fissi un primo colloquio.
  • Il libro: tutte le prove e le misurazioni sono descritte in dettaglio nel libro tecnico di Ebi F. Eilber «AutoCAD .NET API», la cui uscita è prevista per il 2027.

Fonte: Ebi F. Eilber, AutoCAD .NET API (libro tecnico, uscita prevista nel 2027)

CONTACT

Seleziona la tua lingua

Primo colloquiofissare oraPrimo colloquiofissare ora