Durable C# patterns for document access, transactions, entity inspection, block attributes, controlled writes, and error boundaries in AutoCAD.
This repository is a compact reference library. It is not a runnable evaluation kit and does not replace testing inside the licensed AutoCAD version targeted by an engagement.
Artifact class: Reference library.
Does the proposed AutoCAD automation approach respect the document, database, transaction, object-lifetime, and licensed-runtime boundaries required for safe implementation?
Use these patterns to shape a technical conversation or implementation spike. Use the CAD Guardian AutoCAD, AutoLISP, and .NET runnable evaluation kit when an evaluator needs public fixtures, commands, generated evidence, and an explicit first-funded-slice decision.
- AutoCAD .NET developers reviewing foundational API patterns.
- Software architects bridging drawing data into APIs, reports, ERP, GIS, or internal systems.
- CAD managers preparing a bounded automation conversation before private drawings are shared.
These snippets require Autodesk AutoCAD assemblies and a licensed AutoCAD runtime. Target the framework, SDK, and deployment model supported by the AutoCAD release in scope.
Production work may also require document locking, command flags, xref and side-database handling, unit and coordinate policy, layer standards, undo behavior, logging, configuration, deployment packaging, and representative drawing tests.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
public sealed class DocumentCommands
{
[CommandMethod("TSMITH_ACTIVE_DOCUMENT")]
public static void ShowActiveDocument()
{
Document? document = Application.DocumentManager.MdiActiveDocument;
if (document is null)
{
return;
}
Database database = document.Database;
Editor editor = document.Editor;
editor.WriteMessage($"\nDrawing: {database.Filename}");
}
}Why it matters: AutoCAD commands operate inside a document context. Resolve and validate that context before touching the database or editor.
[CommandMethod("TSMITH_ENTITY_INVENTORY")]
public static void InventoryModelSpace()
{
Document? document = Application.DocumentManager.MdiActiveDocument;
if (document is null)
{
return;
}
Database database = document.Database;
Editor editor = document.Editor;
var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
using Transaction transaction =
database.TransactionManager.StartOpenCloseTransaction();
var blockTable = (BlockTable)transaction.GetObject(
database.BlockTableId,
OpenMode.ForRead);
var modelSpace = (BlockTableRecord)transaction.GetObject(
blockTable[BlockTableRecord.ModelSpace],
OpenMode.ForRead);
foreach (ObjectId objectId in modelSpace)
{
if (transaction.GetObject(objectId, OpenMode.ForRead) is not Entity entity)
{
continue;
}
string typeName = entity.GetType().Name;
counts[typeName] = counts.GetValueOrDefault(typeName) + 1;
}
foreach ((string typeName, int count) in counts.OrderBy(pair => pair.Key))
{
editor.WriteMessage($"\n{typeName}: {count}");
}
}Why it matters: A read-only inventory is often the safest first slice. It exposes drawing composition before mutation is authorized. A read transaction does not need to be committed.
public sealed record BlockAttributeValue(
string BlockName,
string Tag,
string Value);
public static IReadOnlyList<BlockAttributeValue> ReadBlockAttributes(
Database database)
{
var values = new List<BlockAttributeValue>();
using Transaction transaction =
database.TransactionManager.StartOpenCloseTransaction();
var blockTable = (BlockTable)transaction.GetObject(
database.BlockTableId,
OpenMode.ForRead);
var modelSpace = (BlockTableRecord)transaction.GetObject(
blockTable[BlockTableRecord.ModelSpace],
OpenMode.ForRead);
foreach (ObjectId objectId in modelSpace)
{
if (transaction.GetObject(objectId, OpenMode.ForRead)
is not BlockReference blockReference)
{
continue;
}
foreach (ObjectId attributeId in blockReference.AttributeCollection)
{
if (transaction.GetObject(attributeId, OpenMode.ForRead)
is not AttributeReference attribute)
{
continue;
}
values.Add(new BlockAttributeValue(
blockReference.Name,
attribute.Tag,
attribute.TextString));
}
}
return values;
}Why it matters: Block attributes frequently carry equipment tags, asset IDs, drawing metadata, and title-block values that must be mapped into a controlled schema.
using Autodesk.AutoCAD.Geometry;
[CommandMethod("TSMITH_CREATE_LINE", CommandFlags.Session)]
public static void CreateLine()
{
Document? document = Application.DocumentManager.MdiActiveDocument;
if (document is null)
{
return;
}
using (document.LockDocument())
using (Transaction transaction =
document.Database.TransactionManager.StartTransaction())
{
var blockTable = (BlockTable)transaction.GetObject(
document.Database.BlockTableId,
OpenMode.ForRead);
var modelSpace = (BlockTableRecord)transaction.GetObject(
blockTable[BlockTableRecord.ModelSpace],
OpenMode.ForWrite);
using var line = new Line(
new Point3d(0, 0, 0),
new Point3d(10, 10, 0));
modelSpace.AppendEntity(line);
transaction.AddNewlyCreatedDBObject(line, true);
transaction.Commit();
}
document.Editor.WriteMessage("\nLine created.");
}Why it matters: Writes require an explicit transaction and, in session or modeless contexts, an explicit document lock. The command context and AutoCAD release determine the exact locking requirement.
[CommandMethod("TSMITH_SAFE_AUDIT")]
public static void RunSafeAudit()
{
Document? document = Application.DocumentManager.MdiActiveDocument;
if (document is null)
{
return;
}
try
{
IReadOnlyList<BlockAttributeValue> values =
ReadBlockAttributes(document.Database);
document.Editor.WriteMessage(
$"\nAttribute values found: {values.Count}");
}
catch (Autodesk.AutoCAD.Runtime.Exception exception)
{
document.Editor.WriteMessage(
$"\nAutoCAD audit failed: {exception.ErrorStatus}");
}
catch (System.Exception exception)
{
document.Editor.WriteMessage(
$"\nUnexpected audit failure: {exception.Message}");
}
}Why it matters: A production adapter should distinguish AutoCAD runtime failures from general application failures and route full details to an approved structured log rather than exposing sensitive context in the command line.
Before moving from reference code to a funded implementation, confirm:
- target AutoCAD version, SDK, framework, and deployment method;
- command, document-locking, and multi-document behavior;
- layer, block, attribute, unit, coordinate, xref, font, and plot dependencies;
- representative public or approved private fixtures;
- read-only versus mutation scope;
- logging, correlation, rollback, undo, and support ownership;
- output schema and downstream integration contract;
- installer, signing, security, and release requirements.
These snippets demonstrate API vocabulary and implementation judgment. They are not compiled release artifacts, customer code, performance evidence, a production add-in, or proof that a private drawing set is ready for automation.
No client drawings, credentials, private names, raw opportunity notes, or license-uncertain fixtures belong in this repository.
For a technical discussion, send this reference and name the exact operation under review.
For an evaluator deciding whether to fund an AutoCAD automation slice, send the CAD Guardian runnable evaluation kit instead.