第 4 章 數據庫基礎2: 添加自定義數據在這一章中,我們將創建一個新的字典對象,它用來表示我們雇員就職的 ‘acme 公司‘(呵呵,當然是虛構的一家公司)的部門。這個“部門”字典對象將包含一個表示部門經理的記錄。我們還會加入代碼到雇員創建過程,這個過程會加入一個索引到雇員工作的部門。我們要說明的是如何在dwg文件中創建自定義數據,包括“每個圖形”的自定義數據和“每個實體”的自定義數據。“每個圖形”的自定義數據是指只在整個圖形中加入一次的數據,它表示對象可以引用的單一類型或特性。“每個實體”的自定義數據是指是為特定的對象或數據庫中的實體加入的數據。在下面的示例中,我們將加入“每個圖形”的自定義數據到命名對象字典(簡稱nod)。nod存在于每一個dwg文件中。“每個實體”的自定義數據加入到一個名為“擴展字典”的字典(可選)中,它表示每一個雇員。每一個由dbobject派生的對象都擁有存儲自定義數據的擴展字典。而在我們的示例中將包含這種自定義數據如名字、薪水和部門。因此這一章的重點是字典對象和擴展記錄(xrecord),它們是我們用來表示自定義數據的容器。首先讓我們來創建表示公司的條目。在本章的前幾個步驟中,我們將創建如下所示的部門層次結構: nod-命名對象字典 acme_division-自定義公司字典 銷售(sales) -部門字典 部門經理-部門條目 請打開lab4文件夾下的lab4工程,或接著lab3的代碼。<!--[if !supportlists]-->1) <!--[endif]-->我們首先要做的是定義一個新的函數,它用來在命名對象字典(nod)中創建公司字典對象。為這個函數取名為createdivision(),,并使用命令屬性來定義createdivision命令。下面是這個函數的代碼,它的形式非常簡單,只是用來在nod中創建一個acme_division(用來表示公司) [commandmethod("createdivision")] public void createdivision() { database db = hostapplicationservices.workingdatabase; transaction trans = db.transactionmanager.starttransaction(); try { //首先,獲取nod…… dbdictionary nod = (dbdictionary)trans.getobject(db.namedobjectsdictionaryid, openmode.forwrite); //定義一個公司級別的字典 dbdictionary acmedict; try { //如果acme_division不存在,則轉到catch塊,這里什么也不做 acmedict = (dbdictionary)trans.getobject(nod.getat("acme_division"), openmode.forread); } catch { //如果acme_division不存在,則創建它并把它加入到nod中…… acmedict = new dbdictionary(); nod.setat("acme_division", acmedict); trans.addnewlycreateddbobject(acmedict, true); } trans.commit(); } finally { trans.dispose(); }} 請仔細閱讀一下上面的代碼塊的結構,可以通過注釋來了解相關的細節。特別要注意的是我們是如何用一個try-catch塊來處理acme_division是否存在?如果acme_division字典不存在,getobject()將會拋出異常,catch塊被執行,它會創建一個新的字典。運行這個函數來看它是否可行。可以使用數據庫查看工具來檢查字典是否已被加入(建議使用arx sdk的arxdbg工具)<!--[if !supportlists]-->2) <!--[endif]-->接下來,我們要在acme_division字典中加入銷售(sales)條目。銷售(sales)條目同樣也是一個字典。由于銷售(sales)字典與acme_division字典的關系如同acme_division字典與nod,所以代碼是類似的。定義下面的代碼部分在acme_division字典中創建一個名為’sales’的字典。代碼提示: dbdictionary divdict; try { divdict = (dbdictionary)trans.getobject(acmedict.getat("sales"), openmode.forwrite); } catch … 運行函數來看‘sales’條目是否已加入到acme_division字典。<!--[if !supportlists]-->3) <!--[endif]-->現在我們要在這個字典中加入一個特殊的記錄,它可以包含任意的自定義數據。我們要加入的數據類型為擴展記錄(xrecord),它可以包含任何東西,因此我們可以讓它包含resultbuffer類的對象(就是有些人可能非常熟悉的‘resbuf’)。resultbuffer可以存儲不同類型的預定義數據。擴展記錄存儲任意數目的resultbuffer關系列表,因此可能會很大。下表是可以包含在resultbuffer中一些數據類型(位于database類的dxfcode枚舉中): start0 text1 xrefpath1 shapename2 blockname2 attributetag2 symboltablename2 mstylename2 symtablerecname2 attributeprompt3 dimstylename3 linetypeprose3 textfontfile3 在下面的代碼部分,我們將創建只包含一個resultbuffer的擴展記錄。這個resultbuffer包含一個單一的的字符串值,它表示’sales’部門的部門經理的名字。我們使用和加入字典一樣的方法加入擴展記錄。唯一的區別是擴展記錄與字典的不同: mgrxrec = new xrecord(); mgrxrec.data = new resultbuffer(new typedvalue((int)dxfcode.text, "randolph p. brokwell")); 請看一下我們是怎樣使用new來創建一個新的擴展記錄。但我們也使用了new來創建一個resultbuffer,傳入的參數是一個名為‘typedvalue’的對象。‘typedvalue’對象和c++中resbuf的成員‘restype’是類似的。這個對象一般表示一個特定類型的dxf值,我們使用它來組裝諸如擴展數據或擴展記錄之類的通用數據容器。在這里,我們簡單地使用dxfcode.text鍵值和“randolph p. brokwell”數據值來定義一個typedvalue,然后把它作為單一的參數傳入resultbuffer構造函數(由new來調用)中。xrecord的’data’屬性實際上正是擴展記錄鏈的第一個resultbuffer,我們使用它來表示擴展記錄鏈是從什么地方開始的。所以接下來的代碼塊看起來和前面兩個非常相似:xrecord mgrxrec; try { mgrxrec = (xrecord)trans.getobject(divdict.getat("department manager"), openmode.forwrite); } catch { mgrxrec = new xrecord(); mgrxrec.data = new resultbuffer(new typedvalue((int)dxfcode.text, "randolph p. brokwell")); divdict.setat("department manager", mgrxrec); trans.addnewlycreateddbobject(mgrxrec, true); } 運行函數并使用數據庫查看工具來確定部門經理已被加入到’sales’字典。 4) 我們已經定義了公司字典,現在我們要把每個雇員的數據加入到前一章定義的塊索引中。我們要加入的數據是:名字、薪水和雇員所屬的部門。要加入這些數據,我們要同前幾個步驟一樣使用擴展記錄。因為我們要加入三個條目,所以我們要使擴展記錄可以把這些數據聯系在一起。一般來說,擴展記錄只能存在于字典中。而我們要為每個雇員加入這些數據(就是本章開頭所講的“每個圖形”的自定義數據和“每個實體”的自定義數據),那應該怎么做呢?答案就是:每一個對象或autocad中的實體實際上都有一個名為’擴展字典(extension dictionary)’的可選字典。我們可以把擴展記錄直接加入到這個字典中。請回到我們在上一章創建的createemployee()函數。這個函數是我們創建塊索引的地方。讓我們像前面的步驟一樣來創建一個新的擴展記錄。因為我們要加入3個條目,因此我們既可以使用resultbuffer的add方法(它會在擴展記錄鏈中加入一個鏈接),也可以利用resultbuffer的構造函數(它的一種構造函數可以輸入可變數量的參數)。無論用哪一種方法,請在createemployee()函數中使用resultbuffer來創建一個新的xrecord,resultbuffer包括以下的類型和值: text – “earnest shackleton” (或是你選擇的其它雇員的名字) real – 72000 或者更多的薪水j text – “sales” 雇員所在的部門 5) 要把上面的擴展記錄加入到塊索引,我們必須把它加入到擴展字典。通常這個字典是不存在的,除非它被明確地創建,塊索引就是這種情況。要給一個對象創建擴展字典,你要調用它的成員‘createextensiondictionary()’。這個函數不返回任何值,所以要訪問它創建的擴展字典,你還得使用對象的‘extensiondictionary’屬性。你可以使用類似于以下的代碼來創建并訪問擴展字典: br.createextensiondictionary(); dbdictionary brextdict = (dbdictionary)trans.getobject(br.extensiondictionary, openmode.forwrite, false); 由于擴展字典也是字典,我們可以和第3步一樣在擴展字典中加入擴展記錄。請完成有關的代碼來創建和訪問塊索引的擴展字典,加入你在第4步中創建的擴展記錄,然后把擴展記錄加入到事務處理。6) 返回到nod……因為在nod中創建公司字典只需要一次(就像創建employee塊一樣),因此我們應該把createdivision函數的命令屬性去掉,而在createemployeedefinition()中調用這個函數。請自己完成這些改變。當所有這些都做完后,當create命令第一次運行的時候,所有的函數都會被調用。7) 下面的步驟和上面的無關。我們將創建一個函數來遍歷模型空間,以用來查找加入的employee對象(這里其實是塊索引)的數目。在vb.net 或c#中,我們可以把模型空間塊表記錄(modelspace blocktablerecord)當作一個集合,這樣就可以使用for each(c#是foreach)來遍歷它。請仔細研究一下下面的代碼片斷: vb.net: dim id as objectid ‘ 首先,定義一個for循環要使用的objectid變量。 for each id in btr dim ent as entity = trans.getobject(id, openmode.forread, false) '打開當前的對象! c#: foreach (objectid id in btr) { entity ent = (entity)trans.getobject(id, openmode.forread, false); //打開當前的對象! 一旦我們獲得模型空間對象,你們就可以定義一個objectid變量,然后把它用于for each循環(c#是foreach)。現在,我們需要使用一些方法來篩選雇員。我們知道模型空間中的對象都是實體,但不全是雇員。我們需要使用一些方法來加以區分。在這里,我們可以使用vb.net的typeof關鍵字并用ctype進行類型轉換(c#是gettype函數和typeof):vb.net: if typeof ent is blockreference then dim br as blockreference = ctype(ent, blockreference) … c#: if(ent.gettype() == typeof(blockreference)) blockreference br = (blockreference)ent; 上面講的概念對于autocad編程是很重要的,因為容器對象經常包含不同類型的對象。你會在autocad程序的開發中經常碰到這種類型轉化。請定義一個名為employeecount()的函數,函數的結構如上所示,它用來統計模型空間中的塊索引的數目。這個函數不會輸出任何東西,但你可以使用逐步調試程序來查看整數變量的增加(每發現一個塊索引對象)。8) 接下來,為了把結果輸出到命令行,我們需要使用application.documentmanager.mdiactivedocument.editor對象的服務。要使用它,請加入下面的代碼: imports autodesk.autocad.editorinputimports autodesk.autocad.applicationservices 在函數的內部:editor ed = application.documentmanager.mdiactivedocument.editor;
最后,在循環的后面確定找到了多少個塊索引:ed.writemessage("employees found: " + nemployeecount.tostring()); 第四章結束下面的代碼片斷演示了怎樣獲取employee對象的所有內容,包括acme_division字典中的部門經理的名字。這部分要在后面的章節中使用,但因為它和本章有關,因此我們把它放在本章作介紹。如果有時間的話,請閱讀一下其中的代碼來看看它是怎么使用的。它可以被直接放到你的類中并可以運行。命令的名字是printoutemployee。listemployee()函數接收一個objectid參數,它通過一個ref類型的字符串數組返回值(包含相應的雇員數據)。調用它的printoutemployee()函數只是用來在命令行中輸出這些數據。我們需要一個遍歷并顯示所有雇員數據的命令。public static void listemployee(objectid employeeid, ref string[] saemployeelist) { int nemployeedatacount = 0; database db = hostapplicationservices.workingdatabase; transaction trans = db.transactionmanager.starttransaction(); //開始事務處理。 try { entity ent = (entity)trans.getobject(employeeid, openmode.forread, false); //打開當前對象! if (ent.gettype() == typeof(blockreference)) { //不是所有的塊索引都有雇員數據,所以我們要處理錯誤 bool bhasourdict = true; xrecord employeexrec = null; try { blockreference br = (blockreference)ent; dbdictionary extdict = (dbdictionary)trans.getobject(br.extensiondictionary, openmode.forread, false); employeexrec = (xrecord)trans.getobject(extdict.getat("employeedata"), openmode.forread, false); } catch { bhasourdict = false; //出現了錯誤……字典或擴展記錄不能訪問 } if (bhasourdict) //如果獲得擴展字典,而又有擴展記錄…… { // 為雇員列表分配內存 saemployeelist = new string[4]; //加入雇員的名字 typedvalue resbuf = employeexrec.data.asarray()[0]; saemployeelist.setvalue(string.format("{0}/n", resbuf.value), nemployeedatacount); nemployeedatacount += 1; //加入雇員的薪水 resbuf = employeexrec.data.asarray()[1]; saemployeelist.setvalue(string.format("{0}/n", resbuf.value), nemployeedatacount); nemployeedatacount += 1; //加入雇員所在的部門 resbuf = employeexrec.data.asarray()[2]; string str = (string)resbuf.value; saemployeelist.setvalue(string.format("{0}/n", resbuf.value), nemployeedatacount); nemployeedatacount += 1; //現在,讓我們從公司字典中獲取老板的名字 //在nod中找到. dbdictionary nod = (dbdictionary)trans.getobject(db.namedobjectsdictionaryid, openmode.forread, false); dbdictionary acmedict = (dbdictionary)trans.getobject(nod.getat("acme_division"), openmode.forread); //注意我們直接使用擴展數據... dbdictionary salesdict = (dbdictionary)trans.getobject(acmedict.getat((string)employeexrec.data.asarray()[2].value), openmode.forread); xrecord salesxrec = (xrecord)trans.getobject(salesdict.getat("department manager"), openmode.forread); //最后,把雇員的數據輸出到命令行 resbuf = salesxrec.data.asarray()[0]; saemployeelist.setvalue(string.format("{0}/n", resbuf.value), nemployeedatacount); nemployeedatacount += 1; } } trans.commit(); } finally { trans.dispose(); } } [commandmethod("printoutemployee")] public static void printoutemployee() { editor ed = application.documentmanager.mdiactivedocument.editor; //聲明我們將在下面使用的工具... database db = hostapplicationservices.workingdatabase; transaction trans = db.transactionmanager.starttransaction(); try { //首先,獲取塊表和模型空間塊表記錄blocktable bt = (blocktable)trans.getobject(hostapplicationservices.workingdatabase.blocktableid, openmode.forread); blocktablerecord btr = (blocktablerecord)trans.getobject(bt[blocktablerecord.modelspace], openmode.forread); //現在,我們需要把內容輸出到命令行。這里可以有一個對象幫助我們: //下面的部分,我們將遍歷模型空間: foreach (objectid id in btr) { entity ent = (entity)trans.getobject(id, openmode.forread, false); //打開當前對象! if (ent is blockreference) { string[] saemployeelist = null;// 這是正確的...定義新的列表。 listemployee(id, ref saemployeelist); if ((saemployeelist.length == 4)) { ed.writemessage("employee name: {0}", saemployeelist[0]); ed.writemessage("employee salary: {0}", saemployeelist[1]); ed.writemessage("employee division: {0}", saemployeelist[2]); ed.writemessage("division manager: {0}", saemployeelist[3]); } } } } finally { } }