TheSwamp

Code Red => .NET => Topic started by: Atook on July 24, 2015, 01:20:57 PM

Title: LockDocument()
Post by: Atook on July 24, 2015, 01:20:57 PM
So I just ran into the problem of needing to lock the document before I can open the database for write. This may be obvious to some, but I’d love to get some confirmation or correction about my understanding of the issue.

I’ve got functions that get called from a [CommandMethod] and run great. If I call the same functions from a button on my palette, I get an eLockViolation. In order to get this code to run, I need to call the LockDocument() function, which I found via the interwebs in Lab6 of the Autodesk .NET tutorials from back in the day.

My guess is that CAD locks the document everytime a command is called, which is why I don’t need to call it at all if my functions have been called by commands. Is this correct?
If so, I only need to lock the document when calling a database write from a non command (such as a button click) or an updated field in a properties grid.
My current approach is to lock the document in the button click event as shown. This looks like a bad practice to me, but I’m not sure I need to imbed it deeper in my code. Are the interface events a good place to lock the document from? Is there some sort of document unlock/release I should call when I'm done?

Code - C#: [Select]
  1. private void btnValveInsert_Click(object sender, EventArgs e)
  2. {
  3.         Active.Document.LockDocument(DocumentLockMode.ProtectedAutoWrite, null, null, true);
  4.         ValveFactory.InsertDynamicValve(30);
  5. }
  6.  
  7. // VS
  8.  
  9. [CommandMethod("IR_InsValve")]
  10. public void InsertValve()
  11. {
  12.         ValveFactory.InsertDynamicValve(30);
  13. }
  14.  

On a slightly different note, when reviewing the LAB6 from the tutorial, I noticed that the following comment: //Whatever happens we must dispose the transaction. (This is in the Finally block).

Whenever I use a transaction, I commit() it when I’m done with it and have not been calling dispose(), and things have been running fine. What's the purpose of disposing of a transaction?
Title: Re: LockDocument()
Post by: Jeff H on July 24, 2015, 01:32:03 PM
This is a little class that ties transaction and document locking together

Code - C#: [Select]
  1. using System;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4.  
  5. namespace Autodesk.AutoCAD.Runtime
  6. {
  7.    public class LockedTransaction : Transaction
  8.     {
  9.        DocumentLock docLock;
  10.        public LockedTransaction(Transaction trx, DocumentLock docLock)
  11.            : base(trx.UnmanagedObject, trx.AutoDelete)
  12.        {
  13.            Interop.DetachUnmanagedObject(trx);
  14.            GC.SuppressFinalize(trx);
  15.            this.docLock = docLock;
  16.            
  17.        }
  18.  
  19.        protected override void Dispose(bool A_1)
  20.        {
  21.  
  22.            base.Dispose(A_1);
  23.            if (A_1)
  24.            {
  25.                docLock.Dispose();
  26.            }
  27.        }
  28.     }
  29.  
  30.  
  31. }
  32.  

Call it through a extension method
Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices.Core;
  2. using Autodesk.AutoCAD.Runtime;
  3.  
  4. namespace Autodesk.AutoCAD.ApplicationServices
  5. {
  6.    public static class TransactionManagerExtensions
  7.     {
  8.        public static LockedTransaction StarLockedTransaction(this TransactionManager tm)
  9.        {
  10.            DocumentLock doclock = Application.DocumentManager.MdiActiveDocument.LockDocument();
  11.            return new LockedTransaction(tm.StartTransaction(), doclock);
  12.        }
  13.  
  14.        public static LockedTransaction StarLockedTransaction(this TransactionManager tm, DocumentLockMode lockMode, string globalCommandName, string localCommandName, bool promptIfFails)
  15.        {
  16.            DocumentLock doclock = Application.DocumentManager.MdiActiveDocument.LockDocument(lockMode, globalCommandName, localCommandName, promptIfFails);
  17.            return new LockedTransaction(tm.StartTransaction(), doclock);
  18.        }
  19.     }
  20. }
  21.  
  22.  
  23.  
Title: Re: LockDocument()
Post by: Atook on July 24, 2015, 01:49:45 PM
Thanks Jeff. I'm always impressed with extension methods, I'm using some of Giles, and they seem to be an elegant solution to some of the boilerplate code I run into.

If I'm reading this correctly, it would make sense to instantiate a LockedTransaction anytime I open the database for write?

I look forward to trying this code out later this afternoon. Thanks again.
Title: Re: LockDocument()
Post by: huiz on July 24, 2015, 02:07:16 PM
I'm  not exactly sure why you need a document lock, but running code from a Palette needs a lock. Maybe because there is no connection between the Palette and the current document, since a Palette floats around in the application and not in a document.

There are other actions that need a document lock, for example if you switch between Layouts nothing really happens if you don't lock the document.

It does not hurt to always use a lock. So you can use a lock in each function you write.
Title: Re: LockDocument()
Post by: Atook on July 24, 2015, 02:38:37 PM
It does not hurt to always use a lock. So you can use a lock in each function you write.

Thanks for the reply huiz. Are you saying there isn't a considerable performance hit with locking the document for every transaction? If so, that's precisely what I'll do. Though I have to wonder why it's not built in to the transaction manager if that's the case.

@Jeff, thanks for sharing your extension classes.  Looks like unless it's readonly, I'll be using locked transactions.

To answer my own question publicly, I noted that the dispose() override was called after the transaction falls from scope, presumably during garbage collection. Meaning there's no reason for me to call it.
Title: Re: LockDocument()
Post by: BlackBox on July 24, 2015, 03:46:54 PM
The issue is Context in which a Method is called, as I understand it.

CommandMethod places application in a specific context that a Button or MenuItem Click event does not (inherently) - hence, you supply a call to 'using' LockDocument() and the issue is mitigated.

I use a lot of ContextMenuExtensions (read MenuItem.Click), and have never seen a noticeable difference in performance in my Transactions, but that's not to say that there isn't a hit per-se.

Cheers
Title: Re: LockDocument()
Post by: gile on July 24, 2015, 03:49:51 PM
Hi,

Autodesk provides some documentation (http://help.autodesk.com/view/ACD/2015/ENU/?guid=GUID-A2CD7540-69C5-4085-BCE8-2A8ACE16BFDD)about this.
Title: Re: LockDocument()
Post by: mohnston on July 27, 2015, 10:54:22 AM
The key is whether your application is modal or modeless.
If modeless then you will need to lock a document before making any changes to it. Since your app could potentially change any open document locking is necessary.
If modal then the document lock is implied.