Author Topic: Error when use operation with transaction in parameter  (Read 3527 times)

0 Members and 1 Guest are viewing this topic.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Error when use operation with transaction in parameter
« on: August 31, 2010, 08:57:45 AM »
Hi all.

I have method:

Code: [Select]
//DBSearcher class
...
        public ObjectId[] GetPrimitives(Func<ObjectId, bool> predicate)
        {
            List<ObjectId> primitives = new List<ObjectId>();

            using (Transaction t = dwg.TransactionManager.StartTransaction()) // 'dwg' is Document
            {
                for (long i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++)// 'db' is Database
                {
                    ObjectId id = ObjectId.Null;
                    try
                    {
                        id = db.GetObjectId(false, new Handle(i), 0);
                    }
                    catch (System.Exception)
                    { continue; }
                    if (!id.IsErased && predicate(id)) primitives.Add(id);
                }
            }
            return primitives.ToArray();
        }
...

I use it method in my code:

Code: [Select]
...
            using (Transaction ts = dwg.TransactionManager.StartTransaction())
            {
                DBSearcher os = new DBSearcher();
                ObjectId[] k = os.GetPrimitives(x => ts.GetObject(x, OpenMode.ForRead) is Table); //There I get error: "Operation is not valid due to the current state of the object."              
            }
...

Error screen:



Why I get error?

If I replace
Code: [Select]
ts.GetObject(x, OpenMode.ForRead)on
Code: [Select]
x.GetObject(OpenMode.ForRead)then all work good.
« Last Edit: August 31, 2010, 09:16:48 AM by Andrey »

kaefer

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #1 on: August 31, 2010, 11:13:46 AM »
If I replace
Code: [Select]
ts.GetObject(x, OpenMode.ForRead)on
Code: [Select]
x.GetObject(OpenMode.ForRead)then all work good.
Hi Andrey,
no use to pretend that I have the slightest idea of what you are trying to achieve there.

But your error is easily explained as an acute case of nested transactions. You must not reference the outer one while your lambda expression runs inside the inner.
Repro code in f#:
Code: [Select]
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.Runtime

type acApp = Autodesk.AutoCAD.ApplicationServices.Application

let pred f arg =
    use t2 = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
    f arg

[<CommandMethod "Boom">]
let Boom() =
    let doc = acApp.DocumentManager.MdiActiveDocument
    let ed = doc.Editor
    let db = doc.Database
    use t1 = db.TransactionManager.StartTransaction()
    db.CurrentSpaceId
    |> pred (fun oid -> oid.GetObject OpenMode.ForRead :?> BlockTableRecord) // Ok
    |> fun btr -> ed.WriteMessage("\nSuccess: {0} ", btr.Name)

    db.CurrentSpaceId
    |> pred (fun oid -> t1.GetObject(oid, OpenMode.ForRead)) // Boom
    |> ignore
Cheers, Thorsten

Glenn R

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #2 on: August 31, 2010, 02:28:39 PM »
Should 'is Table' be 'as Table'...?

Andrey Bushman

  • Swamp Rat
  • Posts: 864
The question is solved
« Reply #3 on: September 01, 2010, 02:20:45 AM »
no use to pretend that I have the slightest idea of what you are trying to achieve there.
It method must return ObjectId array, which primitives is  correspond  for specified condition (parameter). Full code (with russian comments) here.
You must not reference the outer one while your lambda expression runs inside the inner.
Thank you.
Quote
Should 'is Table' be 'as Table'...?
No. lambda expression must return bool value.
« Last Edit: September 01, 2010, 05:55:52 AM by Andrey »

Glenn R

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #4 on: September 01, 2010, 06:54:12 AM »
Yes, I realise that after I posted - it's a predicate.

Are you trying to increment a handle to get all the symboltables? If so, why?

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Error when use operation with transaction in parameter
« Reply #5 on: September 01, 2010, 07:06:32 AM »
Are you trying to increment a handle to get all the symboltables? If so, why?
Whether there is an alternative? How I can receive all database objects via array?

Glenn R

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #6 on: September 01, 2010, 09:20:12 AM »
When you all database objects, do you literally mean 'everything' or just the graphical ones?

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Error when use operation with transaction in parameter
« Reply #7 on: September 01, 2010, 09:47:05 AM »
When you all database objects, do you literally mean 'everything' or just the graphical ones?
All what are inherited from DBObject (graphical). But if you can show as it is possible to generate an array from literally all or only graphic - I with a great interest will look at it.
« Last Edit: September 01, 2010, 09:57:45 AM by Andrey »

Glenn R

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #8 on: September 01, 2010, 10:19:12 AM »
DBObject is non-graphical - think of a layer record. All graphical entities derive from Entity, which in turn derives from DBObject.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Error when use operation with transaction in parameter
« Reply #9 on: September 01, 2010, 10:23:54 AM »
DBObject is non-graphical - think of a layer record. All graphical entities derive from Entity, which in turn derives from DBObject.
Well. But there is a different way of obtaining of an array?

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Error when use operation with transaction in parameter
« Reply #10 on: September 01, 2010, 10:57:37 AM »
Here is a start I put in a list if you wanted more flexability then converted it to an array
I am sure Glenn will let you know if this wrong or a better way of doing it
Code: [Select]

 [CommandMethod("AllModelAndPaper")]
        public void AllDbobjects()
        {
           Database db = HostApplicationServices.WorkingDatabase;
           Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
           List<ObjectId> objIdList = new List<ObjectId>();
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable  bt = (BlockTable)db.BlockTableId.GetObject(OpenMode.ForRead);
                foreach (ObjectId xobjID in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)xobjID.GetObject(OpenMode.ForRead);
                    if (btr.IsLayout)
                    {
                        foreach (ObjectId objID in btr)
                        {
                            objIdList.Add(objID);                             
                        }
                    }
                   ObjectId[] objIDArray = objIdList.ToArray();                   
                    foreach (ObjectId id in objIDArray)                       
                    {
                        ed.WriteMessage(string.Format("\n ObjectID = {0} Type = {1} Handle = {2}",
                            id.ToString(),id.ObjectClass.DxfName ,id.Handle.ToString()));
                    }
                }
                tr.Commit();
            }
        }

Glenn R

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #11 on: September 01, 2010, 11:07:53 AM »
That's what I was thinking...or you could go this way as well:

Code: [Select]
[CommandMethod("TCGS", "IterateLayouts", "IterateLayoutsLocal", CommandFlags.Modal)]
public void MyCommand()
{
    Document doc = acadApp.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        DBDictionary layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead, false);

        foreach (DBDictionaryEntry layoutDictEnt in layoutDict)
        {
            doc.Editor.WriteMessage("{0}Layout name: {1}", Environment.NewLine, layoutDictEnt.Key);
            Layout layout = (Layout)tr.GetObject(layoutDictEnt.Value, OpenMode.ForRead, false);
           
            // grab the block table record for the layout as this will have
            // every graphical entity in it...

            BlockTableRecord layoutBTR = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead, false);

            // loop the BTR...
            foreach (ObjectId btrEntId in layoutBTR)
            {
                Entity ent = (Entity)tr.GetObject(btrEntId, OpenMode.ForRead, false);
                Type entType = ent.GetType();

                doc.Editor.WriteMessage("{0}Entity type: {1}", Environment.NewLine, entType);
            }
        }

        tr.Commit();
    }
}

bear in mind that with these methodologies, you will miss 'nested' blocks...


Jeff H

  • Needs a day job
  • Posts: 6150
Re: Error when use operation with transaction in parameter
« Reply #12 on: September 01, 2010, 11:13:52 AM »
Glenn's is more efficient it cuts out searching through a bunch of blocks.

kaefer

  • Guest
Re: Error when use operation with transaction in parameter
« Reply #13 on: September 02, 2010, 07:18:08 AM »
Glenn's is more efficient it cuts out searching through a bunch of blocks.
Andreys' approach, enumerating all handles inside the drawing database range, is certainly more creative.

Another idea would be Reflection-based traversal. The ObjectARX 2009 package contains an example under samples\dotNet\DrawingBrowser, albeit with rudimentary control logic based on the Object type (tests for ObjectId and RXClass only). I haven't found it in the newer packages.

To get max functionality out of this, something along the following lines is required.
Code: [Select]
match o with  // o is System.Object
| :? ObjectId as oid when not oid.IsNull -> ... // follow RXObject returned by oid.GetObject()
| :? System.Collections.DictionaryEntry
| :? DBDictionaryEntry -> // do something with key and value
| :? RXObject as rx ->
    ... // get relevant properties of RXObject and act on them appropriately
    try
        let ie = o :?> System.Collections.IEnumerable
        for (i, o) in ie |> Seq.cast<_> |> Seq.mapi (fun i o -> i, o) do
            ... // now for the nested objects, if rx implements IEnumerable
    with _ -> ()
| :? string -> // since string is seq<char> it needs to be special cased
| :? System.Collections.IEnumerable -> ... // other sequences
| _ -> ...
Cheers, Thorsten