Bien utiliser les transactions dans AutoCAD .NET

CAD-Modell eines Bauteils auf dem Bildschirm mit Leistungskurve

CAO

Votre commande s'exécute sans message d'erreur – et rien n'a changé dans le dessin. Ou pire : après une erreur, la moitié des composants ont été déplacés, le reste se trouve encore à l'ancien emplacement. Ce sont deux erreurs dans l'utilisation des transactions, et toutes deux peuvent être évitées.

La transaction comme table du petit-déjeuner

Ouvrir un objet du dessin, c'est comme dévisser un bocal. Autrefois, on ouvrait et refermait chaque bocal séparément, avec Open et Close. La transaction, c'est la table du petit-déjeuner qui va avec : tout ce que vous ouvrez par son intermédiaire reste ouvert jusqu'à la fin de la transaction. Tous les bocaux sont alors refermés d'un coup. Avec Commit, les modifications sont conservées ; sans Commit, tout revient à l'état antérieur.

Le déroulement est toujours le même :

  1. db.TransactionManager.StartTransaction() démarre la transaction.
  2. tr.GetObject(id, OpenMode.ForRead) ou OpenMode.ForWrite ouvre un objet.
  3. tr.AddNewlyCreatedDBObject(objekt, true) déclare les nouveaux objets après leur ajout à la base de données.
  4. tr.Commit() valide toutes les modifications.
  5. Dispose termine la transaction. Si le Commit manque, tout est annulé.
[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);

        // Sans Commit, Dispose annule tout à la fin du bloc using

        tr.Commit();

    }

}

Le bloc using garantit que Dispose est appelé même si une exception survient en cours de route. Un Commit oublié ne provoque donc pas de plantage, mais une commande qui semble ne rien faire – un classique de nos formations.

Là où les choses tournent mal dans la pratique

Un Commit dans une transaction interne ne vaut que sous réserve

Les transactions peuvent être imbriquées. La transaction externe reprend les modifications de la transaction interne. Si la transaction externe est annulée, ce que la transaction interne avait déjà validé disparaît aussi. Nous avons trouvé ce schéma dans du code réel : une méthode utilitaire crée un calque dans sa propre transaction et valide proprement. Mais l'appelant ne valide jamais sa transaction externe, parce qu'il n'a « fait que lire ». Le calque disparaît à la fin, et personne ne sait pourquoi. Terminez en outre toujours les transactions imbriquées de l'intérieur vers l'extérieur. Qui termine d'abord la transaction externe risque un plantage sans le moindre message. Les blocs using règlent l'ordre d'eux-mêmes.

Commit dans le Dispose d'une classe personnelle

Certaines classes conservent la transaction dans un champ et appellent dans leur Dispose d'abord Commit, puis Dispose. Cela paraît bien rangé, mais c'est dangereux : Dispose s'exécute aussi lorsque le bloc using est quitté à cause d'une exception. Un travail à moitié fait est alors enregistré. Pour un cercle, c'est agaçant ; pour cent composants, c'est une catastrophe. La bonne solution est une classe qui valide explicitement :

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 validation est explicite – uniquement si tout a réussi

    public void Bestätige()

    {

        _tr.Commit();

    }



    // Dispose ne fait que ranger et ne valide rien

    public void Dispose()

    {

        _tr.Dispose();

    }

}

La commande crée la classe dans un bloc using, déplace tous les éléments et n'appelle Bestätige() qu'à la toute fin. Si une exception survient avant, le dessin reste inchangé.

La transaction que personne ne termine

Si Dispose est appelé manuellement à la fin et qu'une exception survient avant, la transaction reste ouverte. AutoCAD ne la supprime pas à la fin de la commande. Elle reste active d'une commande à l'autre et se retrouve dans les jambes de chaque commande suivante en tant que TopTransaction. Une transaction n'a donc jamais sa place dans un champ statique ou dans un objet qui vit plus longtemps que la commande.

La rapide OpenCloseTransaction

StartOpenCloseTransaction ressemble à une transaction, mais ouvre en interne avec Open et Close. Elle est plus rapide, mais a ses particularités : TopTransaction ne la connaît pas, et Abort n'annule pas les objets nouvellement créés. Ils restent orphelins dans la base de données. Utilisez-la là où l'on ne fait que lire ou modifier.

Ce que coûtent les transactions

Nous avons mesuré dans la console AutoCAD 2027 avec un terrain de 10 000 cellules de grille, soit 30 000 lignes. Les chiffres ne valent que pour notre ordinateur, mais les rapports sont instructifs : 10 000 transactions au lieu d'une ne coûtaient qu'environ 15 % de plus. L'OpenCloseTransaction était environ deux fois plus rapide. Le véritable frein, c'est l'écran : UpdateScreen après chaque cellule coûtait presque le triple, FlushGraphics après chaque cellule vingt fois plus. Qui veut afficher une progression ne rafraîchit donc pas après chaque élément, mais toutes les quelques centaines.

Erreurs typiques

  • Commit oublié : la commande s'exécute et semble ne rien faire.
  • Un Commit dans une transaction interne est considéré comme définitif – jusqu'à ce que la transaction externe soit annulée.
  • Un Commit dans le Dispose d'une classe personnelle enregistre un travail à moitié fait après une erreur.
  • Une transaction non terminée survit à la commande et perturbe toutes les suivantes.
  • GetObject fournit à chaque fois une nouvelle enveloppe .NET. Comparez les objets par leur ObjectId.
  • DowngradeOpen n'a aucun effet dans une transaction. Ouvrez directement dans le bon mode.
  • ObjectId.GetObject sans transaction en cours a fait planter AutoCAD 2027 lors de notre essai.

Comment nous vous aidons

  • Formation : les transactions, l'ouverture et la fermeture ainsi que la libération propre des ressources sont au cœur de la formation « AutoCAD .NET API avec C# ». Pour BricsCAD, il existe la formation « BricsCAD .NET API avec C# ».
  • Diagnostic du code transactions : nous examinons votre add-in à la recherche de Commit oubliés, de transactions imbriquées et de freins lors du dessin.
  • Projet : votre add-in est lent ou laisse des modifications à moitié faites ? Convenez d'un premier entretien.
  • Le livre : tous les essais et mesures sont décrits en détail dans l'ouvrage spécialisé d'Ebi F. Eilber « AutoCAD .NET API », dont la parution est prévue en 2027.

Source : Ebi F. Eilber, AutoCAD .NET API (ouvrage spécialisé, parution prévue en 2027)

CONTACT

Sélectionnez votre langue

Premier entretienprendre rendez-vousPremier entretienprendre rendez-vous