本帖最后由 wang2006zhi 于 2025-11-5 14:40 编辑
  1. <div class="blockcode"><blockquote>/// <summary>
  2. /// 简化版链式关联管理器
  3. /// </summary>
  4. public static class AssociationManager
  5. {
  6.     #region 内部结构
  7.    
  8.     /// <summary>
  9.     /// 关联信息
  10.     /// </summary>
  11.     private class AssociationAction
  12.     {
  13.         public Action? Action { get; set; }
  14.         public string Tag { get; set; } = "";
  15.     }

  16.     // 关联存储:源对象ID -> 关联信息列表
  17.     private static readonly Dictionary<ObjectId, List<AssociationAction>> Associations = new();
  18.    
  19.     // 防止递归更新的标记集合
  20.     private static readonly HashSet<ObjectId> UpdatingObjects = new();
  21.    
  22.     // 初始化标志
  23.     private static bool _isInitialized;

  24.     #endregion

  25.     #region 公共API

  26.     /// <summary>
  27.     /// 创建对象关联
  28.     /// </summary>
  29.     /// <param name="sourceId">源对象ID</param>
  30.     /// <param name="action">无参数关联动作</param>
  31.     /// <param name="tag">关联标签</param>
  32.     public static void AssocObjects(ObjectId sourceId, Action? action, string tag = "")
  33.     {
  34.         EnsureInitialized();
  35.         
  36.         if (sourceId.IsNull || action == null)
  37.             return;

  38.         // 获取或创建关联列表
  39.         if (!Associations.ContainsKey(sourceId))
  40.             Associations[sourceId] = new List<AssociationAction>();

  41.         // 检查是否已存在相同标签的关联
  42.         var existing = Associations[sourceId].FirstOrDefault(a => a.Tag == tag);
  43.         if (existing != null)
  44.             existing.Action = action; // 更新现有关联
  45.         else // 添加新关联
  46.             Associations[sourceId].Add(new AssociationAction
  47.             {
  48.                 Action = action,
  49.                 Tag = tag
  50.             });
  51.     }

  52.     /// <summary>
  53.     /// 移除对象的所有关联
  54.     /// </summary>
  55.     public static void RemoveAssociations(ObjectId sourceId)
  56.     {
  57.         Associations.Remove(sourceId);
  58.     }

  59.     /// <summary>
  60.     /// 移除特定标签的关联
  61.     /// </summary>
  62.     public static void RemoveAssociation(ObjectId sourceId, string tag)
  63.     {
  64.         if (!Associations.TryGetValue(sourceId, out var association)) return;
  65.         
  66.         association.RemoveAll(a => a.Tag == tag);
  67.         if (Associations[sourceId].Count == 0)
  68.             Associations.Remove(sourceId);
  69.     }

  70.     /// <summary>
  71.     /// 手动触发关联更新
  72.     /// </summary>
  73.     /// <param name="sourceId">源对象ID</param>
  74.     public static void UpdateAssociations(ObjectId sourceId)
  75.     {
  76.         if (!Associations.ContainsKey(sourceId) || UpdatingObjects.Contains(sourceId))
  77.             return;

  78.         ExecuteAssociations(sourceId);
  79.     }

  80.     /// <summary>
  81.     /// 检查是否存在关联
  82.     /// </summary>
  83.     public static bool HasAssociations(ObjectId sourceId)
  84.     {
  85.         return Associations.ContainsKey(sourceId);
  86.     }

  87.     /// <summary>
  88.     /// 获取关联统计信息
  89.     /// </summary>
  90.     public static string GetStats()
  91.     {
  92.         var sourceCount = Associations.Count;
  93.         var totalAssociations = Associations.Values.Sum(list => list.Count);
  94.         return $"关联源对象: {sourceCount}, 总关联数: {totalAssociations}";
  95.     }

  96.     #endregion

  97.     #region 核心实现

  98.     /// <summary>
  99.     /// 确保管理器已初始化
  100.     /// </summary>
  101.     private static void EnsureInitialized()
  102.     {
  103.         if (_isInitialized) return;
  104.         
  105.         var doc = Acaop.DocumentManager.MdiActiveDocument;
  106.         if (doc == null) return;
  107.         // 移除旧的事件处理(防止重复注册)
  108.         doc.Database.ObjectModified -= OnObjectModified;
  109.         doc.Database.ObjectErased -= OnObjectErased;
  110.             
  111.         // 注册新的事件处理
  112.         doc.Database.ObjectModified += OnObjectModified;
  113.         doc.Database.ObjectErased += OnObjectErased;
  114.         _isInitialized = true;
  115.     }

  116.     /// <summary>
  117.     /// 执行关联动作
  118.     /// </summary>
  119.     private static void ExecuteAssociations(ObjectId sourceId)
  120.     {
  121.         if (!Associations.ContainsKey(sourceId) || UpdatingObjects.Contains(sourceId))
  122.             return;

  123.         try
  124.         {
  125.             UpdatingObjects.Add(sourceId);
  126.             
  127.             foreach (var actionInfo in Associations[sourceId].ToList())
  128.             {
  129.                 try
  130.                 {
  131.                     // 执行关联动作
  132.                     actionInfo.Action?.Invoke();
  133.                 }
  134.                 catch (Exception ex)
  135.                 {
  136.                     Debug.WriteLine($"关联执行失败 [{actionInfo.Tag}]: {ex.Message}");
  137.                 }
  138.             }
  139.         }
  140.         finally
  141.         {
  142.             UpdatingObjects.Remove(sourceId);
  143.         }
  144.     }

  145.     /// <summary>
  146.     /// 对象修改事件处理
  147.     /// </summary>
  148.     private static void OnObjectModified(object sender, ObjectEventArgs e)
  149.     {
  150.         if (!Associations.ContainsKey(e.DBObject.Id) ||
  151.             UpdatingObjects.Contains(e.DBObject.Id))
  152.             return;

  153.         ExecuteAssociations(e.DBObject.Id);
  154.     }

  155.     /// <summary>
  156.     /// 对象删除事件处理
  157.     /// </summary>
  158.     private static void OnObjectErased(object sender, ObjectErasedEventArgs e)
  159.     {
  160.         // 移除被删除对象的关联
  161.         RemoveAssociations(e.DBObject.Id);
  162.     }

  163.     #endregion
  164. }
封装参数为:AssociationManager.AssocObjects(textId1, () =>{});
  1. namespace HTJ.Arx;

  2. /// <summary>
  3. /// 简化版链式关联管理器 - 使用无参数Action,支持1->2->3链式关联,防止闭环
  4. /// </summary>
  5. public static class AssociationManager
  6. {
  7.     #region 内部结构

  8.     /// <summary>
  9.     /// 关联信息
  10.     /// </summary>
  11.     private class AssociationAction
  12.     {
  13.         public ObjectId TargetId { get; set; }
  14.         public Action? Action { get; set; }
  15.         public string Tag { get; set; } = "";
  16.     }

  17.     // 关联存储:源对象ID -> 关联信息列表
  18.     private static readonly Dictionary<ObjectId, List<AssociationAction>> Associations = new();

  19.     // 防止递归更新的标记集合
  20.     private static readonly HashSet<ObjectId> UpdatingObjects = new();

  21.     // 初始化标志
  22.     private static bool _isInitialized;

  23.     #endregion

  24.     #region 公共API

  25.     /// <summary>
  26.     /// 创建对象关联
  27.     /// </summary>
  28.     /// <param name="sourceId">源对象ID</param>
  29.     /// <param name="targetId">目标对象ID</param>
  30.     /// <param name="action">无参数关联动作</param>
  31.     /// <param name="tag">关联标签</param>
  32.     /// <exception cref="InvalidOperationException">检测到闭环关联时抛出</exception>
  33.     public static void AssocObjects(ObjectId sourceId, ObjectId targetId,
  34.         Action? action, string tag = "")
  35.     {
  36.         EnsureInitialized();

  37.         if (sourceId.IsNull || targetId.IsNull || action == null)
  38.             return;

  39.         // 检查是否会造成闭环
  40.         if (WouldCreateCycle(sourceId, targetId)) throw new InvalidOperationException("创建此关联会导致闭环,这是不允许的");

  41.         // 获取或创建关联列表
  42.         if (!Associations.ContainsKey(sourceId)) Associations[sourceId] = new List<AssociationAction>();

  43.         // 检查是否已存在相同标签的关联
  44.         var existing = Associations[sourceId].FirstOrDefault(a => a.Tag == tag);
  45.         if (existing != null)
  46.             existing.Action = action; // 更新现有关联
  47.         else
  48.             // 添加新关联
  49.             Associations[sourceId].Add(new AssociationAction
  50.             {
  51.                 TargetId = targetId,
  52.                 Action = action,
  53.                 Tag = tag
  54.             });
  55.     }

  56.     /// <summary>
  57.     /// 移除对象的所有关联
  58.     /// </summary>
  59.     private static void RemoveAssociations(ObjectId sourceId)
  60.     {
  61.         Associations.Remove(sourceId);
  62.     }

  63.     /// <summary>
  64.     /// 移除特定标签的关联
  65.     /// </summary>
  66.     public static void RemoveAssociation(ObjectId sourceId, string tag)
  67.     {
  68.         if (!Associations.TryGetValue(sourceId, out var association)) return;
  69.         association.RemoveAll(a => a.Tag == tag);
  70.         if (Associations[sourceId].Count == 0) Associations.Remove(sourceId);
  71.     }

  72.     /// <summary>
  73.     /// 手动触发关联更新
  74.     /// </summary>
  75.     /// <param name="sourceId">源对象ID</param>
  76.     public static void UpdateAssociations(ObjectId sourceId)
  77.     {
  78.         if (!Associations.ContainsKey(sourceId) || UpdatingObjects.Contains(sourceId))
  79.             return;
  80.         ExecuteAssociations(sourceId);
  81.     }

  82.     /// <summary>
  83.     /// 检查是否存在关联
  84.     /// </summary>
  85.     public static bool HasAssociations(ObjectId sourceId)
  86.     {
  87.         return Associations.ContainsKey(sourceId);
  88.     }

  89.     /// <summary>
  90.     /// 获取关联统计信息
  91.     /// </summary>
  92.     public static string GetStats()
  93.     {
  94.         var sourceCount = Associations.Count;
  95.         var totalAssociations = Associations.Values.Sum(list => list.Count);

  96.         return $"关联源对象: {sourceCount}, 总关联数: {totalAssociations}";
  97.     }

  98.     #endregion

  99.     #region 核心实现

  100.     /// <summary>
  101.     /// 确保管理器已初始化
  102.     /// </summary>
  103.     private static void EnsureInitialized()
  104.     {
  105.         if (_isInitialized) return;

  106.         var doc = Acaop.DocumentManager.MdiActiveDocument;
  107.         if (doc != null)
  108.         {
  109.             doc.Database.ObjectModified += OnObjectModified;
  110.             doc.Database.ObjectErased += OnObjectErased;
  111.             _isInitialized = true;
  112.         }
  113.     }

  114.     /// <summary>
  115.     /// 检查是否会造成闭环
  116.     /// </summary>
  117.     private static bool WouldCreateCycle(ObjectId sourceId, ObjectId targetId)
  118.     {
  119.         // 如果目标对象已经是源对象的祖先,则形成闭环
  120.         return IsAncestor(targetId, sourceId, new HashSet<ObjectId>());
  121.     }

  122.     /// <summary>
  123.     /// 检查targetId是否是sourceId的祖先
  124.     /// </summary>
  125.     private static bool IsAncestor(ObjectId sourceId, ObjectId targetId, HashSet<ObjectId> visited)
  126.     {
  127.         if (sourceId == targetId)
  128.             return true;

  129.         if (!visited.Add(sourceId))
  130.             return false;

  131.         if (!Associations.TryGetValue(sourceId, out var association)) return false;
  132.         foreach (var action in association)
  133.             if (IsAncestor(action.TargetId, targetId, visited))
  134.                 return true;

  135.         return false;
  136.     }

  137.     /// <summary>
  138.     /// 执行关联动作
  139.     /// </summary>
  140.     private static void ExecuteAssociations(ObjectId sourceId)
  141.     {
  142.         if (!Associations.ContainsKey(sourceId) || UpdatingObjects.Contains(sourceId))
  143.             return;

  144.         try
  145.         {
  146.             UpdatingObjects.Add(sourceId);

  147.             foreach (var actionInfo in Associations[sourceId].ToList())
  148.                 try
  149.                 {
  150.                     // 执行关联动作
  151.                     actionInfo.Action?.Invoke();
  152.                     // 递归执行链式关联
  153.                     if (Associations.ContainsKey(actionInfo.TargetId))
  154.                         ExecuteAssociations(actionInfo.TargetId);
  155.                 }
  156.                 catch (Exception ex)
  157.                 {
  158.                     Debug.WriteLine($"关联执行失败 [{actionInfo.Tag}]: {ex.Message}");
  159.                 }
  160.         }
  161.         finally
  162.         {
  163.             UpdatingObjects.Remove(sourceId);
  164.         }
  165.     }

  166.     /// <summary>
  167.     /// 对象修改事件处理
  168.     /// </summary>
  169.     private static void OnObjectModified(object sender, ObjectEventArgs e)
  170.     {
  171.         if (!Associations.ContainsKey(e.DBObject.Id) ||
  172.             UpdatingObjects.Contains(e.DBObject.Id))
  173.             return;

  174.         ExecuteAssociations(e.DBObject.Id);
  175.     }

  176.     /// <summary>
  177.     /// 对象删除事件处理
  178.     /// </summary>
  179.     private static void OnObjectErased(object sender, ObjectErasedEventArgs e)
  180.     {
  181.         // 移除被删除对象的关联
  182.         RemoveAssociations(e.DBObject.Id);

  183.         // 移除指向被删除对象的所有关联
  184.         foreach (var sourceId in Associations.Keys.ToList())
  185.         {
  186.             Associations[sourceId].RemoveAll(action => action.TargetId == e.DBObject.Id);
  187.             if (Associations[sourceId].Count == 0) Associations.Remove(sourceId);
  188.         }
  189.     }

  190.     #endregion
  191. }
封装参数为:AssociationManager.AssocObjects(sId,tId, () =>{});

  1.     [CommandMethod("tt8")]
  2.     public void Tt8()
  3.     {
  4.         if (!Env.Editor.SelId(out ObjectId textId1,RxClassEx.DbText))
  5.             return;
  6.         if (!Env.Editor.SelId(out ObjectId circleId,RxClassEx.Circle))
  7.             return;
  8.         if (!Env.Editor.SelId(out ObjectId textId2,RxClassEx.DbText))
  9.             return;
  10.         // 创建图形属性关联
  11.         AssociationManager.AssocObjects(textId1,circleId,() =>
  12.         {
  13.             using var tr = new DBTrans(docLock: false);
  14.             if (tr.GetObject(textId1) is not DBText text1)
  15.                 return;
  16.             if (tr.GetObject(circleId,OpenMode.ForWrite) is not Circle circle)
  17.                 return;
  18.             circle.Radius=text1.TextString.ToDouble();
  19.             circle.ColorIndex=1;
  20.             if (tr.GetObject(textId2,OpenMode.ForWrite) is not DBText text2)
  21.                 return;
  22.             text2.TextString = $"半径: {circle.Radius:F2}";
  23.         },"1->2");
  24.         // 创建关联:文字显示圆的半径和面积
  25.     AssociationManager.AssocObjects(circleId,textId2,() =>
  26.     {
  27.         using var tr = new DBTrans(docLock: false);
  28.         if (tr.GetObject(circleId) is not Circle circle)
  29.             return;
  30.         if (tr.GetObject(textId2,OpenMode.ForWrite) is not DBText text2)
  31.             return;
  32.         text2.TextString = $"半径: {circle.Radius:F2}";
  33.     },"2->3");
测试2


网友答: 关于AssocAction,CAD有自带的,ifox群有我的代码示例

网友答:
  1. namespace HTJ.Arx;

  2. /// <summary>
  3. /// 关联动作体封装
  4. /// BY2025.11;wang2006zhi
  5. /// </summary>
  6. /// <remarks>
  7. /// 使用示例:
  8. /// <code>
  9. /// var logic = new Dictionary{
  10. ///     [textId] = trans => { /* 文字更新逻辑 */ },
  11. ///     [circleId] = trans => { /* 圆更新逻辑 */ }
  12. /// };
  13. /// AssocActionEx.Create(logic);
  14. /// </code>
  15. /// </remarks>
  16. public class AssocActionEx : AssocActionBody
  17. {
  18.     #region 私有字段

  19.     private ObjectId _assocActionId = ObjectId.Null;
  20.     private readonly List<ObjectId> _sourceObjectIds = [];
  21.     private readonly Dictionary<ObjectId, Action<AssocObjectTransaction>> _objectUpdateActions = [];
  22.     private readonly List<Action<AssocObjectTransaction>> _globalUpdateActions = [];
  23.     private Func<AssocActionEx>? _cloneFactory;
  24.     private bool _cloneFlag;

  25.     #endregion

  26.     #region 公共接口

  27.     /// <summary>
  28.     /// 创建关联动作
  29.     /// </summary>
  30.     /// <param name="objectSpecificLogic">
  31.     /// 对象特定逻辑字典,Key为对象ID(自动作为依赖对象),Value为对应的更新逻辑
  32.     /// </param>
  33.     /// <param name="cloneFactory">深克隆时的创建工厂,用于对象复制时创建新的关联动作实例</param>
  34.     /// <returns>创建的关联动作实例</returns>
  35.     /// <exception cref="ArgumentNullException">当逻辑字典为空时抛出</exception>
  36.     /// <exception cref="ArgumentException">当逻辑字典为空时抛出</exception>
  37.     public static AssocActionEx Create(Dictionary<ObjectId, Action<AssocObjectTransaction>> objectSpecificLogic,
  38.         Func<AssocActionEx>? cloneFactory = null)
  39.     {
  40.         if (objectSpecificLogic == null) throw new ArgumentNullException(nameof(objectSpecificLogic));
  41.         if (objectSpecificLogic.Count == 0) throw new ArgumentException(@"对象特定逻辑字典不能为空", nameof(objectSpecificLogic));

  42.         var action = new AssocActionEx();

  43.         if (cloneFactory != null) action._cloneFactory = cloneFactory;

  44.         // 直接从字典的键获取依赖对象,并设置更新逻辑
  45.         action.SetupDependenciesAndLogic(objectSpecificLogic);

  46.         return action;
  47.     }

  48.     /// <summary>
  49.     /// 添加全局更新逻辑(对所有依赖对象生效)
  50.     /// </summary>
  51.     /// <param name="logic">全局更新逻辑,会在每次评估时执行</param>
  52.     /// <returns>当前实例以支持链式调用</returns>
  53.     /// <exception cref="ArgumentNullException">当逻辑为空时抛出</exception>
  54.     public AssocActionEx AddGlobalLogic(Action<AssocObjectTransaction> logic)
  55.     {
  56.         _globalUpdateActions.Add(logic ?? throw new ArgumentNullException(nameof(logic)));
  57.         return this;
  58.     }

  59.     /// <summary>
  60.     /// 设置深克隆工厂函数
  61.     /// </summary>
  62.     /// <param name="factory">创建新实例的工厂函数,在对象被复制时调用</param>
  63.     /// <returns>当前实例以支持链式调用</returns>
  64.     /// <exception cref="ArgumentNullException">当工厂函数为空时抛出</exception>
  65.     public AssocActionEx SetCloneFactory(Func<AssocActionEx> factory)
  66.     {
  67.         _cloneFactory = factory ?? throw new ArgumentNullException(nameof(factory));
  68.         return this;
  69.     }

  70.     #endregion

  71.     #region 内部实现

  72.     /// <summary>
  73.     /// 设置依赖关系并创建关联动作
  74.     /// </summary>
  75.     /// <param name="objectSpecificLogic">对象特定逻辑字典</param>
  76.     private void SetupDependenciesAndLogic(Dictionary<ObjectId, Action<AssocObjectTransaction>> objectSpecificLogic)
  77.     {
  78.         var ids = objectSpecificLogic.Keys.ToList();
  79.         // 设置依赖关系
  80.         SetupDependencies(ids);
  81.         // 设置对象特定逻辑
  82.         foreach (var kv in objectSpecificLogic) _objectUpdateActions[kv.Key] = kv.Value;
  83.     }

  84.     /// <summary>
  85.     /// 获取需要处理的对象列表
  86.     /// </summary>
  87.     /// <param name="transaction">关联事务</param>
  88.     /// <returns>需要更新的对象ID列表</returns>
  89.     private List<ObjectId> GetPendingObjects(AssocObjectTransaction transaction)
  90.     {
  91.         var objectIdCollection = GetDependencies(true, true);
  92.         var pendingObjectIds = new List<ObjectId>(objectIdCollection.Count);

  93.         // 遍历所有依赖项,检查哪些需要更新
  94.         foreach (ObjectId dependencyId in objectIdCollection)
  95.         {
  96.             using var dependency =
  97.                 (AssocDependency)transaction.GetDBObject(dependencyId, OpenMode.ForRead, false, true);

  98.             // 如果依赖项状态不是"已更新",则添加到待处理列表
  99.             if (dependency.Status != AssocStatus.IsUpToDateAssocStatus)
  100.             {
  101.                 pendingObjectIds.Add(dependency.DependentOnObject);
  102.             }
  103.         }

  104.         return pendingObjectIds;
  105.     }

  106.     /// <summary>
  107.     /// 获取或创建关联动作对象
  108.     /// </summary>
  109.     /// <param name="owningObjectId">所属对象ID</param>
  110.     /// <param name="tr">数据库事务</param>
  111.     /// <returns>关联动作对象</returns>
  112.     private AssocAction GetOrCreateAssocAction(ObjectId owningObjectId, DBTrans tr)
  113.     {
  114.         // 如果已经存在关联动作,直接返回
  115.         if (!_assocActionId.IsNull) return tr.GetObject<AssocAction>(_assocActionId, OpenMode.ForWrite)!;

  116.         var database = tr.Database;

  117.         // 获取或创建关联网络
  118.         var networkObjectId = AssocNetwork.GetInstanceFromObject(owningObjectId, true, true, "");
  119.         var network = tr.GetObject<AssocNetwork>(networkObjectId, OpenMode.ForWrite)!;

  120.         // 创建关联动作
  121.         var assocAction = new AssocAction();
  122.         var actionBodyObjectId = database.AddDBObject(this);

  123.         // 注册新创建的对象到事务中
  124.         tr.Transaction.AddNewlyCreatedDBObject(this, true);
  125.         _assocActionId = database.AddDBObject(assocAction);
  126.         assocAction.ActionBody = actionBodyObjectId;
  127.         network.AddAction(_assocActionId, true);

  128.         return assocAction;
  129.     }

  130.     /// <summary>
  131.     /// 评估逻辑 - 执行所有注册的更新动作
  132.     /// </summary>
  133.     /// <remarks>
  134.     /// 这是关联动作的核心方法,当依赖对象发生变化时自动调用
  135.     /// </remarks>
  136.     public override void EvaluateOverride()
  137.     {
  138.         try
  139.         {
  140.             var assocEvaluationCallback = currentEvaluationCallback();

  141.             // 检查评估模式是否为"修改对象"模式
  142.             if (assocEvaluationCallback.EvaluationMode() != AssocEvaluationMode.ModifyObjectsAssocEvaluationMode)
  143.             {
  144.                 Status = AssocStatus.FailedToEvaluateAssocStatus;
  145.                 return;
  146.             }

  147.             // 检查是否存在被删除或损坏的依赖项
  148.             if (HasAnyErasedOrBrokenDependencies())
  149.             {
  150.                 Status = AssocStatus.ErasedAssocStatus;
  151.                 return;
  152.             }

  153.             using var transaction = new AssocObjectTransaction(this);

  154.             // 获取需要更新的对象
  155.             var pendingObjects = GetPendingObjects(transaction);
  156.             if (pendingObjects.Count == 0)
  157.             {
  158.                 Status = AssocStatus.IsUpToDateAssocStatus;
  159.                 return;
  160.             }

  161.             // 评估依赖项状态
  162.             EvaluateDependencies();

  163.             // 执行对象特定的更新逻辑
  164.             ExecuteObjectSpecificLogic(pendingObjects, transaction);

  165.             // 执行全局更新逻辑
  166.             ExecuteGlobalLogic(transaction);

  167.             // 标记状态为已更新
  168.             Status = AssocStatus.IsUpToDateAssocStatus;
  169.         }
  170.         catch (Exception ex)
  171.         {
  172.             // 记录异常并标记为评估失败
  173.             $"关联动作评估失败: {ex.Message}".Print();
  174.             Status = AssocStatus.FailedToEvaluateAssocStatus;
  175.         }
  176.     }

  177.     /// <summary>
  178.     /// 执行对象特定的更新逻辑
  179.     /// </summary>
  180.     /// <param name="pendingObjects">待处理对象列表</param>
  181.     /// <param name="transaction">关联事务</param>
  182.     private void ExecuteObjectSpecificLogic(List<ObjectId> pendingObjects, AssocObjectTransaction transaction)
  183.     {
  184.         foreach (var objectId in pendingObjects)
  185.         {
  186.             if (!_objectUpdateActions.TryGetValue(objectId, out var objectLogic)) continue;
  187.             try
  188.             {
  189.                 objectLogic(transaction);
  190.             }
  191.             catch (Exception ex)
  192.             {
  193.                $"对象特定逻辑执行失败 {objectId}: {ex.Message}".Print();;
  194.             }
  195.         }
  196.     }

  197.     /// <summary>
  198.     /// 执行全局更新逻辑
  199.     /// </summary>
  200.     /// <param name="transaction">关联事务</param>
  201.     private void ExecuteGlobalLogic(AssocObjectTransaction transaction)
  202.     {
  203.         foreach (var globalLogic in _globalUpdateActions)
  204.         {
  205.             try
  206.             {
  207.                 globalLogic(transaction);
  208.             }
  209.             catch (Exception ex)
  210.             {
  211.                 $"全局逻辑执行失败: {ex.Message}".Print();
  212.             }
  213.         }
  214.     }

  215.     /// <summary>
  216.     /// 深克隆处理 - 当关联对象被复制时调用
  217.     /// </summary>
  218.     /// <param name="idMap">对象ID映射关系</param>
  219.     /// <param name="additionalObjectsToClone">额外需要克隆的对象集合</param>
  220.     /// <remarks>
  221.     /// 此方法确保当依赖对象被复制时,关联动作也会被正确复制
  222.     /// </remarks>
  223.     public override void AddMoreObjectsToDeepCloneOverride(IdMapping idMap, ObjectIdCollection additionalObjectsToClone)
  224.     {
  225.         if (_cloneFlag) return;
  226.         _cloneFlag = true;
  227.         try
  228.         {
  229.             using var tr = new DBTrans();
  230.             // 收集已克隆的对象ID
  231.             var (clonedIds, originalIds) = CollectClonedIds(idMap);
  232.             // 克隆缺失的依赖对象
  233.             CloneMissingDependencies(clonedIds, originalIds, tr);
  234.             // 创建新的关联动作实例并复制逻辑
  235.             CreateClonedActionBody(clonedIds, idMap);
  236.         }
  237.         finally
  238.         {
  239.             _cloneFlag = false;
  240.         }
  241.     }

  242.     /// <summary>
  243.     /// 收集已克隆的对象ID
  244.     /// </summary>
  245.     /// <param name="idMap">ID映射表</param>
  246.     /// <returns>克隆后的ID列表和原始ID列表</returns>
  247.     private (List<ObjectId> clonedIds, List<ObjectId> originalIds) CollectClonedIds(IdMapping idMap)
  248.     {
  249.         var length = idMap.GetValues().Count;
  250.         var clonedIds = new List<ObjectId>(length);
  251.         var originalIds = new List<ObjectId>(length);

  252.         foreach (IdPair idPair in idMap)
  253.         {
  254.             clonedIds.Add(idPair.Value);
  255.             originalIds.Add(idPair.Key);
  256.         }

  257.         return (clonedIds, originalIds);
  258.     }

  259.     /// <summary>
  260.     /// 克隆缺失的依赖对象
  261.     /// </summary>
  262.     /// <param name="clonedIds">已克隆的ID列表</param>
  263.     /// <param name="originalIds">原始ID列表</param>
  264.     /// <param name="tr">数据库事务</param>
  265.     private void CloneMissingDependencies(List<ObjectId> clonedIds, List<ObjectId> originalIds, DBTrans tr)
  266.     {
  267.         // 检查是否有缺失的依赖对象需要克隆
  268.         if (clonedIds.Count >= _sourceObjectIds.Count) return;

  269.         var missingIds = _sourceObjectIds.Except(originalIds).ToArray();
  270.         if (missingIds.Length == 0) return;

  271.         // 执行深克隆操作
  272.         var objectIdCollection = new ObjectIdCollection(missingIds);
  273.         using var idMapping = new IdMapping();

  274.         tr.Database.DeepCloneObjects(objectIdCollection, tr.CurrentSpace.ObjectId, idMapping, false);

  275.         // 添加新克隆的对象ID到列表中
  276.         foreach (IdPair idPair in idMapping)
  277.         {
  278.             clonedIds.Add(idPair.Value);
  279.         }
  280.     }

  281.     private void CreateClonedActionBody(List<ObjectId> clonedIds, IdMapping idMap)
  282.     {
  283.         // 创建新的动作体实例
  284.         var newBody = _cloneFactory?.Invoke() ?? new AssocActionEx();

  285.         // 设置依赖关系(使用克隆后的ID列表)
  286.         newBody.SetupDependencies(clonedIds);

  287.         // 复制对象特定逻辑(映射到克隆后的对象ID)
  288.         foreach (var kv in _objectUpdateActions)
  289.         {
  290.             var originalId = kv.Key;
  291.             var logic = kv.Value;
  292.             var clonedId = FindClonedId(originalId, idMap);
  293.             if (clonedId.IsValid) newBody._objectUpdateActions[clonedId] = logic;
  294.         }

  295.         // 复制全局逻辑
  296.         newBody._globalUpdateActions.AddRange(_globalUpdateActions);
  297.     }

  298.     /// <summary>
  299.     /// 设置依赖关系
  300.     /// </summary>
  301.     /// <param name="ids">依赖对象ID列表</param>
  302.     private void SetupDependencies(IList<ObjectId> ids)
  303.     {
  304.         if (ids.Count == 0) return;

  305.         using var tr = new DBTrans();
  306.         var first = ids[0];
  307.         var database = first.Database;

  308.         // 验证数据库状态
  309.         if (database == null)
  310.         {
  311.             "数据库不可用".Print();
  312.             return;
  313.         }
  314.         // 获取或创建关联动作
  315.         var assocAction = GetOrCreateAssocAction(first, tr);

  316.         // 在事务中创建依赖关系
  317.         using var transaction = database.TransactionManager.StartTransaction();

  318.         for (int i = 0; i < ids.Count; i++)
  319.         {
  320.             var entityId = ids;
  321.             if (!IsObjectSuitableForAssociation(entityId, tr))
  322.             {
  323.                 $"对象 {entityId} 不适合作为关联依赖,已跳过".Print();
  324.                 continue;
  325.             }
  326.             _sourceObjectIds.Add(entityId);

  327.             // 创建依赖项
  328.             var dependency = new AssocDependency();
  329.             var dependencyId = database.AddDBObject(dependency);

  330.             // 配置依赖项属性
  331.             dependency.AttachToObject(new CompoundObjectId(entityId));
  332.             dependency.IsReadDependency = true;
  333.             dependency.IsWriteDependency = true;
  334.             dependency.Order = i;

  335.             // 将依赖项添加到关联动作
  336.             assocAction.AddDependency(dependencyId, true);
  337.         }

  338.         transaction.Commit();
  339.     }

  340.     /// <summary>
  341.     /// 检查对象是否适合作为关联依赖
  342.     /// </summary>
  343.     private bool IsObjectSuitableForAssociation(ObjectId objectId, DBTrans tr)
  344.     {
  345.         try
  346.         {
  347.             if (objectId.IsNull || !objectId.IsValid)
  348.                 return false;

  349.             using var obj = tr.Transaction.GetObject(objectId, OpenMode.ForRead, false, true);
  350.         
  351.             // 检查对象类型
  352.             if (obj is BlockReference || obj is BlockTableRecord)
  353.             {
  354.                 "块引用和块表记录不适合作为关联依赖".Print();
  355.                 return false;
  356.             }

  357.             // 检查对象是否已被擦除
  358.             if (obj.IsErased)
  359.             {
  360.                 "对象已被擦除".Print();
  361.                 return false;
  362.             }

  363.             return true;
  364.         }
  365.         catch
  366.         {
  367.             return false;
  368.         }
  369.     }
  370.     /// <summary>
  371.     /// 查找克隆后的对象ID
  372.     /// </summary>
  373.     /// <param name="originalId">原始对象ID</param>
  374.     /// <param name="idMap">ID映射表</param>
  375.     /// <returns>克隆后的对象ID,如果找不到则返回ObjectId.Null</returns>
  376.     private static ObjectId FindClonedId(ObjectId originalId, IdMapping idMap)
  377.     {
  378.         foreach (IdPair idPair in idMap)
  379.         {
  380.             if (idPair.Key == originalId) return idPair.Value;
  381.         }

  382.         return ObjectId.Null;
  383.     }

  384.     #endregion
  385. }


网友答:
  1. namespace HTJ.Arx;

  2. /// <summary>
  3. /// 封装的动作体基类
  4. /// BY 徐蕾来自IFOX
  5. /// </summary>
  6. public abstract class AssocActionEx : AssocActionBody
  7. {
  8.     /// <summary>
  9.     /// 所在的动作的objectid
  10.     /// </summary>
  11.     private ObjectId AssocActionId { get; set; } = ObjectId.Null;

  12.     /// <summary>
  13.     /// 外部传入的对象id缓存
  14.     /// </summary>
  15.     private readonly List<ObjectId> _sourceObjectIds = [];
  16.    
  17.     /// <summary>
  18.     /// 添加依赖到动作中
  19.     /// </summary>
  20.     /// <param name="tr"></param>
  21.     /// <param name="isRead"></param>
  22.     /// <param name="isWrite"></param>
  23.     /// <param name="ids"></param>
  24.     public virtual void AddDependency(IList<ObjectId> ids, DBTrans tr, bool isRead = true, bool isWrite = true)
  25.     {
  26.         if (!ids.Any()) return;
  27.         var first = ids.First();
  28.         var database = first.Database;
  29.         // 假设 GetAssocAction 已经通过事务获取对象
  30.         AssocAction assocAction = GetAssocAction(first, tr);
  31.         // 确保在事务内部执行所有操作
  32.         using Transaction transaction = database.TransactionManager.StartTransaction();
  33.         for (int i = 0; i < ids.Count; i++)
  34.         {
  35.             var entity = ids[i];
  36.             _sourceObjectIds.Add(entity);
  37.             
  38.             var dependency = new AssocDependency();
  39.             var dependecyId = database.AddDBObject(dependency);
  40.             
  41.             dependency.AttachToObject(new CompoundObjectId(entity));
  42.             dependency.IsReadDependency = isRead;
  43.             dependency.IsWriteDependency = isWrite;
  44.             dependency.Order = i;
  45.             
  46.             assocAction.AddDependency(dependecyId, true);
  47.         }
  48.         transaction.Commit();
  49.     }


  50.     /// <summary>
  51.     /// 生成子类实例的抽象方法,用于实体复制事件中的占位
  52.     /// </summary>
  53.     /// <returns></returns>
  54.     protected abstract AssocActionEx CreateAssocActionBody();
  55.    
  56.     /// <summary>
  57.     /// 具体的更新实体逻辑
  58.     /// </summary>
  59.     /// <param name="objectId"></param>
  60.     /// <param name="aTransaction"></param>
  61.     protected abstract void UpdateEntity(ObjectId objectId, AssocObjectTransaction aTransaction);
  62.    
  63.     /// <summary>
  64.     /// 获取动作
  65.     /// </summary>
  66.     /// <param name="owningObjectId">要查找关联网络的对象的 OwnerId(通常是块表记录或字典的 ID)</param>
  67.     /// <param name="tr"></param>
  68.     /// <returns></returns>
  69.     /// <exception cref="InvalidOperationException"></exception>
  70.     private AssocAction GetAssocAction(ObjectId owningObjectId, DBTrans tr)
  71.     {
  72.         if (!AssocActionId.IsNull) return tr.GetObject<AssocAction>(AssocActionId, OpenMode.ForWrite)!;
  73.         var database = tr.Database;
  74.         //获取或创建关联网络的objectid
  75.         //第一个false表示没有就创建新的
  76.         //第二个true表示加入顶级网路(不明所以)
  77.         var networkObjectid = AssocNetwork.GetInstanceFromObject(owningObjectId, true, true, "");
  78.         // 以写入模式获取网络对象
  79.         var network = tr.GetObject<AssocNetwork>(networkObjectid, OpenMode.ForWrite)!;
  80.         // 创建关联动作对象
  81.         var assocAction = new AssocAction();

  82.         // 将动作体添加到数据库
  83.         var actionBodyObjectid = database.AddDBObject(this);
  84.         // 在事务中注册新创建的动作体
  85.         tr.Transaction.AddNewlyCreatedDBObject(this, true);
  86.         // 将动作添加到数据库
  87.         AssocActionId = database.AddDBObject(assocAction);
  88.         // 关联动作对象设置其动作体
  89.         assocAction.ActionBody = actionBodyObjectid;
  90.         // 将动作添加到网络中
  91.         network.AddAction(AssocActionId, true);
  92.         return assocAction;
  93.     }

  94.     /// <summary>
  95.     /// 获取实体上包含的动作
  96.     /// </summary>
  97.     /// <param name="entity"></param>
  98.     /// <param name="tr"></param>
  99.     /// <returns></returns>
  100.     /// <exception cref="InvalidOperationException"></exception>
  101.     private AssocAction GetAssocAction(Entity entity, DBTrans tr)
  102.     {
  103.         return !entity.ObjectId.IsOk() ? throw new InvalidOperationException() : GetAssocAction(entity.OwnerId, tr);
  104.     }
  105.    
  106.     /// <summary>
  107.     /// 重写 AssocActionBody 的 EvaluateOverride 方法,用于自定义关联动作的评估逻辑
  108.     /// </summary>
  109.     public override void EvaluateOverride()
  110.     {
  111.         // 获取当前关联评估的回调接口
  112.         var assocEvaluationCallback = currentEvaluationCallback();

  113.         // 检查评估模式是否为"修改对象"模式,如果不是则标记为失败并退出
  114.         if (assocEvaluationCallback.EvaluationMode() != AssocEvaluationMode.ModifyObjectsAssocEvaluationMode)
  115.         {
  116.             Status = AssocStatus.FailedToEvaluateAssocStatus;
  117.             return;
  118.         }

  119.         // 检查是否存在被删除或损坏的依赖项,如果是则标记为"已删除"状态并退出
  120.         if (HasAnyErasedOrBrokenDependencies())
  121.         {
  122.             Status = AssocStatus.ErasedAssocStatus;
  123.             return;
  124.         }

  125.         // 创建一个关联对象事务(自动释放资源)
  126.         using var transaction = new AssocObjectTransaction(this);

  127.         // 获取所有依赖项(包括直接和间接依赖)
  128.         var objectIdCollection = GetDependencies(true, true);

  129.         //标记是否需要更新
  130.         bool isUpToDateAssocStatus = true;

  131.         //待处理的实体id集合
  132.         List<ObjectId> pendingObjectids = [];

  133.         foreach (ObjectId objectId in objectIdCollection)
  134.         {
  135.             // 以只读方式打开依赖项(AssocDependency 对象)
  136.             using AssocDependency dependency = (AssocDependency)transaction.GetDBObject(objectId, OpenMode.ForRead, false, true);
  137.             if (dependency.Status is AssocStatus.IsUpToDateAssocStatus) continue;
  138.             isUpToDateAssocStatus = false;
  139.             pendingObjectids.Add(dependency.DependentOnObject);
  140.         }

  141.         // 显式评估所有依赖项的状态
  142.         EvaluateDependencies();

  143.         // 如果依赖项都已是最新状态,则直接标记为最新并退出
  144.         if (isUpToDateAssocStatus)
  145.         {
  146.             Status = AssocStatus.IsUpToDateAssocStatus;
  147.             return;
  148.         }

  149.         //各个子类实现相应的动作
  150.         //UpdateEntity(pendingObjectids, transaction);

  151.         foreach (ObjectId objectId in pendingObjectids)
  152.         {
  153.             UpdateEntity(objectId, transaction);
  154.         }

  155.         // 标记当前状态为"已更新"
  156.         Status = AssocStatus.IsUpToDateAssocStatus;
  157.     }


  158.     private bool _cloneFlag;

  159.     public override void AddMoreObjectsToDeepCloneOverride(IdMapping idMap, ObjectIdCollection additionalObjectsToClone)
  160.     {
  161.         if (_cloneFlag) return;

  162.         using var tr = new DBTrans();

  163.         List<ObjectId> ids = [];
  164.         List<ObjectId> objectids = [];
  165.         foreach (IdPair idPair in idMap)
  166.         {
  167.             ids.Add(idPair.Value);
  168.             objectids.Add(idPair.Key);
  169.         }

  170.         if (ids.Count < _sourceObjectIds.Count)
  171.         {
  172.             var except = _sourceObjectIds.Except(objectids).ToArray();
  173.             var objectIdCollection = new ObjectIdCollection(except);
  174.             using IdMapping idMapping = [];

  175.             _cloneFlag = true;
  176.             //深度克隆
  177.             tr.Database.DeepCloneObjects(objectIdCollection, tr.CurrentSpace.ObjectId, idMapping, false);
  178.             _cloneFlag = false;

  179.             foreach (IdPair idPair in idMapping)
  180.             {
  181.                 ids.Add(idPair.Value);
  182.             }
  183.         }
  184.         // 创建自定义动作体实例
  185.         var body = this.CreateAssocActionBody();
  186.         //添加依赖
  187.         body.AddDependency(ids, tr);

  188.     }
  189.    
  190. }记录一下


网友答: 发个图片看看呀

网友答: 本帖最后由 wang2006zhi 于 2025-11-5 13:16 编辑
  1. 演示
复制代码


网友答:
d1742647821 发表于 2025-11-4 11:44
关于AssocAction,CAD有自带的,ifox群有我的代码示例

经过提醒,在IFOX群里找到了相关示例。。。基于此,本文不再设置付费主题观看

网友答: 本帖最后由 你有种再说一遍 于 2025-11-4 15:59 编辑

标注关联有没有做过?我发现官方在标注关联是有bug的,例如跨空间标注关联,然后我们要附着岂不是有相同bug?

网友答:
你有种再说一遍 发表于 2025-11-4 15:49
标注关联有没有做过?我发现官方在标注关联是有bug的,例如跨空间标注关联,然后我们要附着岂不是有相同bug?

没有,目前没这方面的业务需求

网友答: 作者怎么被封啦???也没有看他刷帖啊
  • 上一篇:[求助]基于中望WebCAD的二次开发
  • 下一篇:没有了