最近在开发一个工具的过程中发现一些很坑的细节。 在大多时候我们向管子中间插入一个法兰或一个法兰连接的管件时会自动匹配出另外一片法兰。而且配对出来的法兰为Mating Part，相对应的与之配对的管件为Base Part。如下图，如果你向管子上插入的是盲板法兰，前面那片法兰会自动匹配生成，那么这片法兰就是Mating Part，但是如果你先手动插入一片法兰，然后再在法兰后面手动插入一片盲板法兰，那么这两个管件就都是Base Part。 注意1：Mating Part可以手动改成Base Part，但是Base Part不可以手动改成Mating Part。 注意2：Mating Part和Base Part归属于同一个Feature，这个Feature的属性由Base Part决定。 我这次开发的工具是按指定距离调整两个法兰端面之间的距离，用到了移动Feature的操作，所以当我来移动这两片法兰的时候第一步就是要先找到这两片法兰所对应的Feature。但是法兰可能是Mating Part也可能是Base Part，如果是Mating Part的时候相对来说简单些，找到对应的Feature直接移动就可以。但是如果法兰是Base Part，你就要判断一下另外一片与之配对的法兰是Mating还是Base，如果是Mating，跟前面情况一样，如果另外一片法兰是Base，那你只移动这一片法兰还不行，另外一片和它配对的法兰也需要跟着移动，所以这时候你就要找到这两个Feature同时进行移动才行。 好，接下来还有更坑的。 我们继续加入一个可变因素，当你两片法兰正好是断Run的地方（现实中确实很多变等级变Run设置在法兰连接处）。产生这种场景有两个常用方法，第一个，确保已有的两片法兰均为Base Part，手动断Run；第二个，我们在已有的一片法兰 开放端接着画管子，画管子的时候指定新的Run，这个时候自动匹配生成的法兰为Mating Part，但它归属于新Run。 我们先看第一个方法中的法兰，这种好理解点，两片法兰都是Base Part，各自对应一个Along Leg Feature，然后因为是变Run，这里还会产生两个End Feature，在每个法兰的法兰面端。坑点来了，我们在移动的时候不仅要移动每个法兰所对应的Along Leg Feature，还要移动所有的End Feature！ 第二个方法变的Run是个深坑，自动匹配出来的法兰虽然仍是Mating Flange，但在它和Base Part不在同一个Run，所以它们也不可能归属于同一个Feature了，而且这时候，这片Mating Flang对应的Feature是一个End Feature，Base Part所对应的还是Along Leg Feature，后面还是跟着一个End Feature在它的法兰端面处。这种情况，我们调整法兰位置就需要调整这三个Feature的位置！ 总结：S3D背后的关联关系太多，任何一个小的开发可能要考虑到各种工况，不断去完善。这是3D二次开发的一个最大的特点（坑点）。
S3D中Codelist类型的属性无法直接获取到他们的长短描述，我们可以将这个过程封装成标准的提取方法，方便使用。获取短描述，我们是要先获取到Codelist属性，然后要知道这个Codelist在后台数据库中的表名称和表空间类型（这两项可以在 Metadata Browser 中查询），一起传递到方法内，即可取到短描述。Public Function GetCodelistShortDesc(ByVal oPropCodeList As PropertyValueCodelist, ByVal sCodeListName As String, ByVal sCodeListSpace As String) As String Dim oMetaMgr As MetadataManager = MiddleServiceProvider.SiteMgr.ActiveSite.ActivePlant.PlantModel.MetadataMgr Dim oCodelistInfo As CodelistInformation Dim oCodelistItem As CodelistItem GetCodelistShortDesc = "" Try oCodelistInfo = oMetaMgr.GetCodelistInfo(sCodeListName, sCodeListSpace) oCodelistItem = oCodelistInfo.GetCodelistItem(oPropCodeList.PropValue) GetCodelistShortDesc = oCodelistItem.Name Catch ex As Exception MsgBox("GetCodelistShortDesc函数异常: " & ex.Message) End Try End Function获取长描述比较简单，我们就代替原有的GetProperty方法，直接传递对象、接口，属性进来，就能获取到长描述了。当然，获取短描述其实也可以参考类似方法，但要传递的参数就多了，看你们自己喜欢。Public Function GetCodelistLongDesc(ByVal oBO As BusinessObject, ByVal sInterfaceName As String, ByVal sAttributeName As String) As String Dim sDesc As String = "" Dim oPropCodelist As PropertyValueCodelist Try oPropCodelist = oBO.GetPropertyValue(sInterfaceName, sAttributeName) sDesc = oPropCodelist.PropertyInfo.CodeListInfo.GetCodelistItem(oPropCodelist.PropValue).DisplayName Catch ex As Exception End Try Return sDesc End Function后记：某些直接通过封装好的方法获取的Codelist属性可以直接后缀加.ToString，直接得到它的短描述了。但是并不是普适的方法，上述获取方法是比较严谨的途径。
我们使用.net写循环获取大量数据存在一个DataTable以后，可能需要对这些数据进行一些后处理，前端呈现，或供第三方软件使用，这个时候就需要数据库来中转存储这些数据。如何快速地将DataTable中的大量数据批量导入数据库中，就是今天我遇到的一个问题，经过一番查询，发现了一个SQLBulkCopy的方法，很是好用。SqlBulkCopy是.NETFramework 2.0新增的类，位于命名空间System.Data.SqlClient下，主要提供把其它数据源的数据有效批量的加载到SQL Server表中的功能。即使表结构完全不同，也可以通过字段间的对应关系，顺利的将数据导过来。using System.Data; using System.Data.SqlClient;public static void DataTableToSQLServer(DataTable dt) { string connectionString = @"Persist Security Info=False;Initial Catalog=dbname;Data Source=172.11.111.111; User ID=sa; Password=pwd"; using (SqlConnection destinationConnection = new SqlConnection(connectionString)) { destinationConnection.Open(); using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection)) { try { bulkCopy.DestinationTableName = "callLog";//要插入的表的表名 bulkCopy.BatchSize = dt.Rows.Count; bulkCopy.ColumnMappings.Add("id", "id");//第一个"id"是dt中的字段名，第二个"id"表中的字段名 bulkCopy.ColumnMappings.Add("note_id", "note_id"); bulkCopy.ColumnMappings.Add("call_start_at", "call_start_at"); bulkCopy.WriteToServer(dt); System.Windows.Forms.MessageBox.Show("插入成功："+ dt.Rows.Count+"行"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } }3D中我们可以通过下面代码获得数据库连接Private ActivePlant As Plant = MiddleServiceProvider.SiteMgr.ActiveSite.ActivePlant Private oMetaMgr As MetadataManager = ActivePlant.PlantModel.MetadataMgr Private oModName As String = MiddleServiceProvider.SiteMgr.ActiveSite.ActivePlant.PlantModel.Name Private sModConn As SQLDBConnection = New SQLDBConnection(ClientServiceProvider.WorkingSet.ActiveConnection.Server, oModName) Dim SqlConn As SqlConnection = New SqlConnection(sModConn.Connection.ConnectionString)