transaction in dot net

博客介绍了数据库事务中的幻读、不可重复读和脏读问题。为解决这些问题,数据库实现了不同级别的事务隔离,SQL标准定义了四个隔离级别,SQL Server支持这些级别,默认是READ COMMITTED。此外,ADO.NET也支持多种事务隔离级别,后续还将介绍设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • Phantoms Transaction 1 reads a set of rows returned by a specified WHERE clause. Transaction 2 then inserts a new row, which also happens to satisfy the WHERE clause of the query previously used byTransaction 1. Transaction 1 then reads the rows again using the same query but now sees the additional row just inserted by Transaction 2. This new row is known as a "phantom," because to Transaction 1, this row seems to have magically appeared.

  • Nonrepeatable reads Transaction 1 reads a row, and Transaction 2 updates the same row just read by Transaction 1. Transaction 1 then reads the same row again and discovers that the row it read earlier is now different. This is known as a "nonrepeatable read," because the row originally read by Transaction 1 has been changed.

  • Dirty reads Transaction 1 updates a row but doesn't commit the update. Transaction 2 reads the updated row. Transaction 1 then performs a rollback, undoing the previous update. Now the row just read by Transaction 2 is no longer valid (or it's "dirty") because the update made by Transaction 1 wasn't committed when the row was read by Transaction 2.

To deal with these potential problems, databases implement various levels of transaction isolation to prevent concurrent transactions from interfering with each other. The SQL standard defines four isolation levels, which are shown in Table 14.3. These levels are shown in order of increasing isolation.

Table 14.3: SQL Standard Isolation Levels

ISOLATION LEVEL

DESCRIPTION

READ UNCOMMITTED

Phantoms, nonrepeatable reads, and dirty reads are permitted.

READ COMMITTED

Phantoms and nonrepeatable reads are permitted, but dirty reads are not. This is the default for SQL Server.

REPEATABLE READ

Phantoms are permitted, but nonrepeatable and dirty reads are not.

SERIALIZABLE

Phantoms, nonrepeatable reads, and dirty reads are not permitted. This is the default for the SQL standard.

SQL Server supports all of these transaction isolation levels. The default transaction isolation level defined by the SQL standard is SERIALIZABLE, but the default used by SQL Server is READ COMMITTED, which is acceptable for most applications.

Warning 

When you set the transaction isolation level to SERIALIZABLE, any rows you access within a subsequent transaction will be "locked," meaning that no other transaction can modify those rows. Even rows you retrieve using a SELECT statement will be locked. You must commit or roll back the transaction to release the locks and allow other transactions to access the same rows. Use SERIALIZABLE only when you must ensure that your transaction is isolated from other transactions. You'll learn more about this later in the section "Understanding SQL Server Locks."

In addition, ADO.NET supports a number of transaction isolation levels, which are defined in the System.Data.IsolationLevel enumeration. Table 14.4 shows the members of this enumeration.

Table 14.4: IsolationLevel Enumeration Members

ISOLATION LEVEL

DESCRIPTION

Chaos

Pending changes from more isolated transactions cannot be overwritten. SQL Server doesn't support this isolation level.

ReadCommitted

Phantoms and nonrepeatable reads are permitted, but dirty reads are not. This is the default.

ReadUncommitted

Phantoms, nonrepeatable reads, and dirty reads are permitted.

RepeatableRead

Phantoms are permitted, but nonrepeatable and dirty reads are not.

Serializable

Phantoms, nonrepeatable reads, and dirty reads are not permitted.

Unspecified

A different isolation level than the one specified is being used, but the level cannot be determined. SQL Server doesn't support this isolation level.

In the next two sections, you'll learn how to set the transaction isolation level using T-SQL and a SqlTransaction object.

using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Colors; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace ScaffoldPlugin { public class ScaffoldCommands { // 配置参数 private static readonly double[] BarLengths = { 300, 600, 900, 1200, 1500, 1800 }; private static readonly string BlockDirectory = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), “ScaffoldBlocks”); // 容差参数 private const double ToleranceValue = 0.1; // 几何计算容差 private const double SpacingTolerance = 150.0; // 横杆长度匹配容差 private const double MinAxisLength = 100.0; // 最小轴网线长度 private const double MinDistanceForBar = 50.0; // 最小横杆间距 private const double MaxGapFactor = 1.2; // 最大间隙因子 // 块名称定义 private const string PoleBlockName = "ScaffoldPole立杆"; private const string BarPrefix = "ScaffoldPole横杆"; // 图层定义 private const string PoleLayer = "盘扣-立杆"; private const string BarLayer = "盘扣-横杆"; private const string AxisLayer = "盘扣轴网"; private const string DebugLayer = "盘扣-调试"; // 调试用图层 // 主命令:布置脚手架 [CommandMethod("BuildPlatform", "BP", CommandFlags.Modal)] public void BuildPlatform() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 检查块目录 if (!Directory.Exists(BlockDirectory)) { Directory.CreateDirectory(BlockDirectory); ShowErrorDialog($"块目录已创建: {BlockDirectory}\n请添加块文件后重新运行命令"); return; } // 创建所需图层 CreateLayerIfNotExists(db, PoleLayer, 7); // 红色 CreateLayerIfNotExists(db, BarLayer, 1); // 红色 CreateLayerIfNotExists(db, AxisLayer, 3); // 绿色 CreateLayerIfNotExists(db, DebugLayer, 2); // 黄色调试层 // 选择轴网线 var axisLineIds = SelectAxisLines(ed); if (axisLineIds == null || axisLineIds.Count == 0) { ShowErrorDialog("未选择轴网线或操作已取消!"); return; } // 获取轴网线对象 List<Line> axisLines = GetAxisLinesFromIds(db, axisLineIds); if (axisLines.Count == 0) { ShowErrorDialog("未找到有效轴网线!"); return; } // 计算有效交点(只在线段延伸相交处) ed.WriteMessage("\n正在计算有效交点..."); List<Point3d> validPoints = GetValidIntersectionPoints(axisLines); if (validPoints.Count == 0) { ShowErrorDialog("未找到有效交点!"); return; } ed.WriteMessage($"\n找到 {validPoints.Count} 个有效交点"); // 加载块定义 ed.WriteMessage("\n正在加载块定义..."); if (!LoadBlockDefinitions(db, ed)) return; // 清理现有脚手架 ed.WriteMessage("\n开始清理重叠区域的现有脚手架..."); CleanOverlappingScaffold(db, validPoints, ed); // 布置立杆(只在有效交点) ed.WriteMessage("\n开始布置立杆..."); int poleCount = PlacePoles(db, validPoints); ed.WriteMessage($"\n已布置 {poleCount} 根立杆"); // 布置横杆(只在连续线段) ed.WriteMessage("\n开始布置横杆..."); int barCount = PlaceBars(db, validPoints, axisLines, ed); ed.WriteMessage($"\n已布置 {barCount} 根横杆"); // 结果反馈 if (poleCount == 0 && barCount == 0) { ShowErrorDialog("脚手架布置失败,未布置任何构件!"); } else { Application.ShowAlertDialog($"成功布置 {poleCount}根立杆和{barCount}根横杆!"); } } catch (System.Exception ex) { ShowErrorDialog($"发生错误: {ex.Message}\n详细请查看命令行日志"); ed.WriteMessage($"\n错误: {ex.Message}\n{ex.StackTrace}"); } } // 选择轴网线 private List<ObjectId> SelectAxisLines(Editor ed) { var lineIds = new List<ObjectId>(); var filter = new SelectionFilter(new[] { new TypedValue(0, "LINE"), new TypedValue(8, AxisLayer) }); var opts = new PromptSelectionOptions { MessageForAdding = "\n选择盘扣轴网线 (按Enter结束选择): ", MessageForRemoval = "\n移除不需要的轴网线: ", AllowDuplicates = false, RejectObjectsOnLockedLayers = true }; PromptSelectionResult selection = ed.GetSelection(opts, filter); if (selection.Status != PromptStatus.OK) { ed.WriteMessage("\n未选择任何轴网线或选择已取消"); return lineIds; } using (var trans = ed.Document.TransactionManager.StartTransaction()) { foreach (ObjectId id in selection.Value.GetObjectIds()) { if (id.ObjectClass.DxfName == "LINE") { Entity entity = trans.GetObject(id, OpenMode.ForRead) as Entity; if (entity != null && entity.Layer == AxisLayer) { if (entity is Line line && line.Length > MinAxisLength) { lineIds.Add(id); } } } } trans.Commit(); } ed.WriteMessage($"\n已选择 {lineIds.Count} 条有效轴网线"); return lineIds; } // 从ID获取轴网线对象 private List<Line> GetAxisLinesFromIds(Database db, List<ObjectId> lineIds) { List<Line> lines = new List<Line>(); using (Transaction tr = db.TransactionManager.StartTransaction()) { foreach (ObjectId id in lineIds) { Line line = tr.GetObject(id, OpenMode.ForRead) as Line; if (line != null && line.Length > MinAxisLength) { lines.Add(line.Clone() as Line); // 克隆以避免原始对象修改 } } tr.Commit(); } return lines; } // 计算有效交点 private List<Point3d> GetValidIntersectionPoints(List<Line> axisLines) { List<Point3d> validPoints = new List<Point3d>(); for (int i = 0; i < axisLines.Count; i++) { for (int j = i + 1; j < axisLines.Count; j++) { Line line1 = axisLines[i]; Line line2 = axisLines[j]; Point3dCollection intersections = new Point3dCollection(); line1.IntersectWith(line2, Intersect.ExtendBoth, intersections, IntPtr.Zero, IntPtr.Zero); foreach (Point3d pt in intersections) { if (IsValidIntersection(pt, line1, line2)) { validPoints.Add(pt); } } } } // 移除重复点 return validPoints.Distinct(new Point3dComparer(ToleranceValue)).ToList(); } // 点比较器(考虑容差) private class Point3dComparer : IEqualityComparer<Point3d> { private readonly double _tolerance; public Point3dComparer(double tolerance) { _tolerance = tolerance; } public bool Equals(Point3d p1, Point3d p2) { return p1.DistanceTo(p2) < _tolerance; } public int GetHashCode(Point3d p) { return p.X.GetHashCode() ^ p.Y.GetHashCode() ^ p.Z.GetHashCode(); } } // 检查是否为有效交点 private bool IsValidIntersection(Point3d pt, Line line1, Line line2) { return IsPointOnLineSegment(pt, line1, ToleranceValue) && IsPointOnLineSegment(pt, line2, ToleranceValue); } // 检查点是否在线段范围内 private bool IsPointOnLineSegment(Point3d pt, Line line, double tolerance) { Vector3d lineVec = line.EndPoint - line.StartPoint; Vector3d startToPt = pt - line.StartPoint; double lineLengthSquared = lineVec.DotProduct(lineVec); double dotProduct = lineVec.DotProduct(startToPt); return dotProduct >= -tolerance && dotProduct <= lineLengthSquared + tolerance; } // 加载块定义 private bool LoadBlockDefinitions(Database db, Editor ed) { try { using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // 加载立杆块 string polePath = Path.Combine(BlockDirectory, $"{PoleBlockName}.dwg"); if (!File.Exists(polePath)) { ShowErrorDialog($"立杆块文件未找到: {polePath}"); return false; } if (!bt.Has(PoleBlockName)) { using (Database sourceDb = new Database(false, true)) { sourceDb.ReadDwgFile(polePath, FileOpenMode.OpenForReadAndAllShare, false, null); db.Insert(PoleBlockName, sourceDb, true); ed.WriteMessage($"\n已加载立杆块: {PoleBlockName}"); } } // 加载横杆块 foreach (double length in BarLengths) { string barName = $"{BarPrefix}{length}mm"; string barPath = Path.Combine(BlockDirectory, $"{barName}.dwg"); if (!bt.Has(barName)) { if (File.Exists(barPath)) { using (Database sourceDb = new Database(false, true)) { sourceDb.ReadDwgFile(barPath, FileOpenMode.OpenForReadAndAllShare, false, null); db.Insert(barName, sourceDb, true); } ed.WriteMessage($"\n已加载横杆块: {barName}"); } else { ed.WriteMessage($"\n警告:横杆块文件未找到: {barPath}"); } } } tr.Commit(); } return true; } catch (System.Exception ex) { ShowErrorDialog($"加载块定义失败: {ex.Message}"); return false; } } // 清理现有脚手架 private void CleanOverlappingScaffold(Database db, List<Point3d> points, Editor ed) { if (points.Count == 0) return; // 计算包围盒 double minX = points.Min(p => p.X); double maxX = points.Max(p => p.X); double minY = points.Min(p => p.Y); double maxY = points.Max(p => p.Y); // 扩展边界 double padding = BarLengths.Max() * 1.5; Point3d minPt = new Point3d(minX - padding, minY - padding, 0); Point3d maxPt = new Point3d(maxX + padding, maxY + padding, 0); using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; // 创建选择过滤器 TypedValue[] filterValues = { new TypedValue(0, "INSERT"), new TypedValue(8, $"{PoleLayer},{BarLayer}") }; SelectionFilter filter = new SelectionFilter(filterValues); // 创建选择区域 Point3dCollection pts = new Point3dCollection(); pts.Add(minPt); pts.Add(new Point3d(maxPt.X, minPt.Y, 0)); pts.Add(maxPt); pts.Add(new Point3d(minPt.X, maxPt.Y, 0)); // 选择区域内的块 PromptSelectionResult psr = ed.Document.Editor.SelectCrossingPolygon(pts, filter); if (psr.Status == PromptStatus.OK) { int count = 0; foreach (ObjectId id in psr.Value.GetObjectIds()) { Entity ent = tr.GetObject(id, OpenMode.ForWrite) as Entity; if (ent != null) { ent.Erase(); count++; } } ed.WriteMessage($"\n已清理 {count} 个现有脚手架构件"); } tr.Commit(); } } // 布置立杆 private int PlacePoles(Database db, List<Point3d> points) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; if (!bt.Has(PoleBlockName)) { Application.ShowAlertDialog($"错误:立杆块 {PoleBlockName} 未加载!"); return 0; } ObjectId poleBlockId = bt[PoleBlockName]; foreach (Point3d pt in points) { if (double.IsNaN(pt.X) || double.IsNaN(pt.Y)) continue; BlockReference br = new BlockReference(pt, poleBlockId); br.Layer = PoleLayer; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } tr.Commit(); } return count; } // 布置横杆 private int PlaceBars(Database db, List<Point3d> validPoints, List<Line> axisLines, Editor ed) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; // 创建点空间索引 Point3dTree pointTree = new Point3dTree(validPoints, ToleranceValue * 10); // 处理所有轴网线 foreach (Line line in axisLines) { // 找到线上所有有效交点 List<Point3d> linePoints = pointTree.GetPointsOnLine(line); if (linePoints.Count < 2) { continue; } // 按沿线的距离排序点 linePoints = linePoints.OrderBy(p => p.DistanceTo(line.StartPoint)).ToList(); // 将点分组为连续段 List<List<Point3d>> segments = GroupPointsIntoSegments(linePoints, line, ed); // 在每个连续段上布置横杆 foreach (var segment in segments) { if (segment.Count < 2) continue; for (int i = 0; i < segment.Count - 1; i++) { Point3d start = segment[i]; Point3d end = segment[i + 1]; double distance = start.DistanceTo(end); if (distance < MinDistanceForBar) continue; // 查找最匹配的标准长度 double bestLength = FindBestBarLength(distance); if (bestLength < 0) continue; // 计算中点 Point3d midPoint = new Point3d( (start.X + end.X) / 2, (start.Y + end.Y) / 2, (start.Z + end.Z) / 2); // 计算旋转角度 Vector3d direction = end - start; double angle = direction.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis); // 处理角度象限问题 if (direction.Y < 0) angle = Math.PI * 2 - angle; // 生成块名 string blockName = $"{BarPrefix}{bestLength}mm"; if (!bt.Has(blockName)) { continue; } // 插入横杆 BlockReference br = new BlockReference(midPoint, bt[blockName]); br.Layer = BarLayer; br.Rotation = angle; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } } } tr.Commit(); } return count; } // 查找最佳横杆长度 private double FindBestBarLength(double distance) { double bestLength = -1; double minDiff = double.MaxValue; foreach (double len in BarLengths) { double diff = Math.Abs(len - distance); if (diff < minDiff && diff <= SpacingTolerance) { minDiff = diff; bestLength = len; } } return bestLength; } // 将点分组为连续段 private List<List<Point3d>> GroupPointsIntoSegments(List<Point3d> points, Line line, Editor ed) { List<List<Point3d>> segments = new List<List<Point3d>>(); if (points.Count < 2) return segments; List<Point3d> currentSegment = new List<Point3d> { points[0] }; double maxGap = BarLengths.Max() * MaxGapFactor; for (int i = 1; i < points.Count; i++) { double distance = points[i - 1].DistanceTo(points[i]); if (distance > maxGap) { if (currentSegment.Count > 1) { segments.Add(currentSegment); } currentSegment = new List<Point3d> { points[i] }; } else { currentSegment.Add(points[i]); } } if (currentSegment.Count > 1) { segments.Add(currentSegment); } return segments; } // 创建图层 private void CreateLayerIfNotExists(Database db, string layerName, short colorIndex) { using (Transaction tr = db.TransactionManager.StartTransaction()) { LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; if (!lt.Has(layerName)) { lt.UpgradeOpen(); LayerTableRecord ltr = new LayerTableRecord { Name = layerName, Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex), IsPlottable = true }; lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); } tr.Commit(); } } // 显示错误对话框 private void ShowErrorDialog(string message) { Application.ShowAlertDialog(message); } } // 空间索引树 public class Point3dTree { private readonly List<Point3d> points; private readonly double tolerance; public Point3dTree(List<Point3d> points, double tolerance) { this.points = points; this.tolerance = tolerance; } public List<Point3d> GetPointsOnLine(Line line) { List<Point3d> result = new List<Point3d>(); // 计算线段包围盒 Point3d min = new Point3d( Math.Min(line.StartPoint.X, line.EndPoint.X) - tolerance, Math.Min(line.StartPoint.Y, line.EndPoint.Y) - tolerance, 0); Point3d max = new Point3d( Math.Max(line.StartPoint.X, line.EndPoint.X) + tolerance, Math.Max(line.StartPoint.Y, line.EndPoint.Y) + tolerance, 0); // 快速筛选候选点 foreach (Point3d pt in points) { if (pt.X >= min.X && pt.X <= max.X && pt.Y >= min.Y && pt.Y <= max.Y) { if (IsPointOnLineSegment(pt, line, tolerance)) { result.Add(pt); } } } return result; } // 检查点是否在线段范围内 private bool IsPointOnLineSegment(Point3d pt, Line line, double tolerance) { Vector3d lineVec = line.EndPoint - line.StartPoint; Vector3d startToPt = pt - line.StartPoint; double lineLengthSquared = lineVec.DotProduct(lineVec); double dotProduct = lineVec.DotProduct(startToPt); return dotProduct >= -tolerance && dotProduct <= lineLengthSquared + tolerance; } } } 网格斜线段的杆件布置不在斜线上 根据问题重新编写完整代码
07-08
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Colors; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; // 使用别名解决 Exception 冲突 using SysException = System.Exception; using AecException = Autodesk.AutoCAD.Runtime.Exception; namespace ScaffoldPlugin { public class ScaffoldCommands { #region 常量配置 // 杆件长度配置 private static readonly double[] BarLengths = { 300, 600, 900, 1200, 1500, 1800 }; // 块目录路径 private static readonly string BlockDirectory = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ScaffoldBlocks"); // 容差参数 private const double ToleranceValue = 0.1; private const double SpacingTolerance = 150.0; private const double MinAxisLength = 100.0; private const double MinDistanceForBar = 50.0; private const double MaxGapFactor = 1.2; private const double SkipTolerance = 1.0; // 块名称定义 private const string PoleBlockName = "ScaffoldPole立杆"; private const string BarPrefix = "ScaffoldPole横杆"; // 图层定义 private const string PoleLayer = "盘扣-立杆"; private const string BarLayer = "盘扣-横杆"; private const string AxisLayer = "盘扣轴网"; private const string DebugLayer = "盘扣-调试"; #endregion #region BP命令 - 布置脚手架杆件 [CommandMethod("BuildPlatform", "BP", CommandFlags.Modal)] public void BuildPlatform() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 用户确认对话框 if (!ConfirmWithUser(ed, "⚠️ 框选布置杆件数量建议不要超过40000个\n是否继续?")) return; // 检查块目录 if (!CheckBlockDirectory()) return; // 创建必要图层 CreateLayers(db); // 选择轴网线 var axisLines = GetAxisLines(ed); if (axisLines.Count == 0) return; // 计算交点 List<Point3d> intersections = CalculateIntersections(axisLines); ed.WriteMessage($"\n共找到 {intersections.Count} 个有效交点"); // 加载块定义 if (!LoadBlockDefinitions(db, ed)) return; // 缓存现有实体 BuildExistingEntityCache(db); // 布置立杆 int poleCount = PlacePoles(db, intersections, ed); ed.WriteMessage($"\n已布置 {poleCount} 根立杆"); // 布置横杆 int barCount = PlaceBars(db, intersections, axisLines, ed); ed.WriteMessage($"\n已布置 {barCount} 根横杆"); // 结果反馈 if (poleCount > 0 || barCount > 0) { Application.ShowAlertDialog($"成功布置 {poleCount}根立杆和{barCount}根横杆!"); } else { ShowErrorDialog("脚手架布置失败,未布置任何构件!"); } } catch (AecException ex) // 使用别名处理AutoCAD异常 { HandleException(ed, ex); } catch (SysException ex) // 使用别名处理.NET异常 { HandleException(ed, ex); } } #endregion #region BPS命令 - 材料统计 [CommandMethod("GenerateMaterialStats", "BPS", CommandFlags.Modal)] public void GenerateMaterialStats() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 提示选择统计方式 PromptKeywordOptions modeOptions = new PromptKeywordOptions("\n选择统计方式 [n(S)/n(A)]"); modeOptions.Keywords.Add("S", "点选块统计"); modeOptions.Keywords.Add("A", "框选所有块统计"); modeOptions.Keywords.Default = "A"; modeOptions.AllowNone = true; PromptResult modeRes = ed.GetKeywords(modeOptions); if (modeRes.Status != PromptStatus.OK) return; bool isSingleBlockMode = (modeRes.StringResult == "S"); ObjectIdCollection blockIds = new ObjectIdCollection(); string targetBlockName = null; if (isSingleBlockMode) { // 点选块统计模式 PromptEntityOptions singleOpts = new PromptEntityOptions("\n请点选要统计的块参照: "); singleOpts.SetRejectMessage("\n请选择有效的块参照."); singleOpts.AddAllowedClass(typeof(BlockReference), false); PromptEntityResult singleRes = ed.GetEntity(singleOpts); if (singleRes.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockReference br = tr.GetObject(singleRes.ObjectId, OpenMode.ForRead) as BlockReference; if (br != null) { targetBlockName = br.Name; ed.WriteMessage($"\n已选择块: {targetBlockName}"); } tr.Commit(); } if (string.IsNullOrEmpty(targetBlockName)) { ed.WriteMessage("\n错误: 无法获取块名称"); return; } // 获取要统计的块选择集 PromptSelectionOptions selOpts = new PromptSelectionOptions(); selOpts.MessageForAdding = $"\n框选要统计的 {targetBlockName} 块: "; TypedValue[] filterValues = { new TypedValue(0, "INSERT"), new TypedValue(2, targetBlockName) }; PromptSelectionResult selRes = ed.GetSelection(selOpts, new SelectionFilter(filterValues)); if (selRes.Status != PromptStatus.OK) return; blockIds = new ObjectIdCollection(selRes.Value.GetObjectIds()); } else { // 框选所有块统计模式 PromptSelectionOptions selOpts = new PromptSelectionOptions(); selOpts.MessageForAdding = "\n框选要统计的所有块参照: "; TypedValue[] filterValues = { new TypedValue(0, "INSERT") }; PromptSelectionResult selRes = ed.GetSelection(selOpts, new SelectionFilter(filterValues)); if (selRes.Status != PromptStatus.OK) return; blockIds = new ObjectIdCollection(selRes.Value.GetObjectIds()); } // 统计块数量 Dictionary<string, int> stats = CountBlocks(db, blockIds); if (stats.Count == 0) { ed.WriteMessage("\n选定区域中没有可统计的杆件"); return; } // 构建表格数据 List<TableRowData> tableData = BuildTableData(stats, isSingleBlockMode); // 插入统计表格 InsertStatTable(db, ed, tableData, isSingleBlockMode ? targetBlockName : "多种块类型"); ed.WriteMessage("\n材料统计表已成功插入!"); } catch (AecException ex) { HandleException(ed, ex); } catch (SysException ex) { HandleException(ed, ex); } } #endregion #region BP命令辅助方法 private bool ConfirmWithUser(Editor ed, string message) { PromptKeywordOptions options = new PromptKeywordOptions(message); options.Keywords.Add("Y"); options.Keywords.Add("N"); options.AllowNone = false; return ed.GetKeywords(options).StringResult == "Y"; } private bool CheckBlockDirectory() { if (!Directory.Exists(BlockDirectory)) { Directory.CreateDirectory(BlockDirectory); ShowErrorDialog($"块目录已创建: {BlockDirectory}\n请添加块文件后重新运行命令"); return false; } return true; } private void CreateLayers(Database db) { CreateLayerIfNotExists(db, PoleLayer, 7); CreateLayerIfNotExists(db, BarLayer, 1); CreateLayerIfNotExists(db, AxisLayer, 3); CreateLayerIfNotExists(db, DebugLayer, 2); } private List<Line> GetAxisLines(Editor ed) { var filter = new SelectionFilter(new[] { new TypedValue(0, "LINE"), new TypedValue(8, AxisLayer) }); PromptSelectionResult selection = ed.GetSelection( new PromptSelectionOptions { MessageForAdding = "\n选择盘扣轴网线: " }, filter ); if (selection.Status != PromptStatus.OK) return new List<Line>(); List<Line> lines = new List<Line>(); using (Transaction tr = ed.Document.TransactionManager.StartTransaction()) { foreach (ObjectId id in selection.Value.GetObjectIds()) { Line line = tr.GetObject(id, OpenMode.ForRead) as Line; if (line != null && line.Length > MinAxisLength) { lines.Add(line.Clone() as Line); } } tr.Commit(); } ed.WriteMessage($"\n已选择 {lines.Count} 条有效轴网线"); return lines; } private List<Point3d> CalculateIntersections(List<Line> lines) { var results = new HashSet<Point3d>(new Point3dComparer(ToleranceValue)); for (int i = 0; i < lines.Count; i++) { for (int j = i + 1; j < lines.Count; j++) { Point3dCollection pts = new Point3dCollection(); lines[i].IntersectWith(lines[j], Intersect.OnBothOperands, pts, IntPtr.Zero, IntPtr.Zero); foreach (Point3d pt in pts) { if (IsPointOnLineSegment(pt, lines[i], ToleranceValue) && IsPointOnLineSegment(pt, lines[j], ToleranceValue)) { results.Add(pt); } } } } return results.ToList(); } private bool IsPointOnLineSegment(Point3d pt, Line line, double tolerance) { Vector3d lineVec = line.EndPoint - line.StartPoint; Vector3d startToPt = pt - line.StartPoint; double dotProduct = lineVec.DotProduct(startToPt); if (dotProduct < -tolerance || dotProduct > lineVec.LengthSqrd + tolerance) return false; double distSq = startToPt.LengthSqrd - (dotProduct * dotProduct) / lineVec.LengthSqrd; return distSq <= tolerance * tolerance; } private bool LoadBlockDefinitions(Database db, Editor ed) { using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // 加载立杆块 string polePath = Path.Combine(BlockDirectory, $"{PoleBlockName}.dwg"); if (!File.Exists(polePath)) { ShowErrorDialog($"立杆块文件未找到: {polePath}"); return false; } if (!bt.Has(PoleBlockName)) { LoadBlockFromFile(db, polePath, PoleBlockName); ed.WriteMessage($"\n已加载立杆块: {PoleBlockName}"); } // 加载横杆块 foreach (double len in BarLengths) { string barName = $"{BarPrefix}{len}mm"; string barPath = Path.Combine(BlockDirectory, $"{barName}.dwg"); if (!bt.Has(barName) && File.Exists(barPath)) { LoadBlockFromFile(db, barPath, barName); ed.WriteMessage($"\n已加载横杆块: {barName}"); } } tr.Commit(); return true; } } private void LoadBlockFromFile(Database db, string filePath, string blockName) { using (Database sourceDb = new Database(false, true)) { sourceDb.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndAllShare, false, null); db.Insert(blockName, sourceDb, true); } } private void BuildExistingEntityCache(Database db) { _existingEntitiesCache = new HashSet<(string, Point3d, double)>(new EntityKeyComparer()); using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead) as BlockTableRecord; foreach (ObjectId id in btr) { BlockReference br = tr.GetObject(id, OpenMode.ForRead) as BlockReference; if (br != null && (br.Name == PoleBlockName || br.Name.StartsWith(BarPrefix))) { _existingEntitiesCache.Add((br.Name, br.Position, br.Rotation)); } } } } private int PlacePoles(Database db, List<Point3d> points, Editor ed) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; if (!bt.Has(PoleBlockName)) { ShowErrorDialog($"错误:立杆块 {PoleBlockName} 未加载!"); return 0; } ObjectId poleBlockId = bt[PoleBlockName]; foreach (Point3d pt in points) { if (double.IsNaN(pt.X) || double.IsNaN(pt.Y)) continue; if (IsCached(PoleBlockName, pt, 0)) continue; BlockReference br = new BlockReference(pt, poleBlockId); br.Layer = PoleLayer; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } tr.Commit(); } return count; } private int PlaceBars(Database db, List<Point3d> points, List<Line> axisLines, Editor ed) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; foreach (Line axisLine in axisLines) { List<Point3d> linePoints = points.Where(p => IsPointOnLineSegment(p, axisLine, ToleranceValue)).ToList(); if (linePoints.Count < 2) continue; linePoints = SortPointsByLineParam(axisLine, linePoints); List<Segment> segments = GroupPointsIntoSegments(linePoints); foreach (Segment segment in segments) { for (int i = 0; i < segment.Points.Count - 1; i++) { Point3d start = segment.Points[i]; Point3d end = segment.Points[i + 1]; double distance = start.DistanceTo(end); if (distance < MinDistanceForBar) continue; double bestLength = FindBestBarLength(distance); if (bestLength < 0) continue; string blockName = $"{BarPrefix}{bestLength}mm"; if (!bt.Has(blockName)) continue; Point3d midPoint = new Point3d( (start.X + end.X) / 2, (start.Y + end.Y) / 2, (start.Z + end.Z) / 2); Vector3d direction = (end - start).GetNormal(); double angle = Math.Atan2(direction.Y, direction.X); if (IsCached(blockName, midPoint, angle)) continue; BlockReference br = new BlockReference(midPoint, bt[blockName]); br.Layer = BarLayer; br.Rotation = angle; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } } } tr.Commit(); } return count; } private double FindBestBarLength(double distance) { double bestLength = -1; double minDiff = double.MaxValue; foreach (double len in BarLengths) { double diff = Math.Abs(len - distance); if (diff < minDiff && diff <= SpacingTolerance) { minDiff = diff; bestLength = len; } } return bestLength; } private List<Point3d> SortPointsByLineParam(Line line, List<Point3d> points) { Vector3d lineVec = line.EndPoint - line.StartPoint; double lineLength = lineVec.Length; return points .Select(p => new { Point = p, Param = lineVec.DotProduct(p - line.StartPoint) / lineLength }) .OrderBy(x => x.Param) .Select(x => x.Point) .ToList(); } private List<Segment> GroupPointsIntoSegments(List<Point3d> points) { List<Segment> segments = new List<Segment>(); if (points.Count < 2) return segments; double maxGap = BarLengths.Max() * MaxGapFactor; Segment currentSegment = new Segment { Points = { points[0] } }; for (int i = 1; i < points.Count; i++) { double distance = points[i - 1].DistanceTo(points[i]); if (distance > maxGap) { if (currentSegment.Points.Count > 1) segments.Add(currentSegment); currentSegment = new Segment { Points = { points[i] } }; } else { currentSegment.Points.Add(points[i]); } } if (currentSegment.Points.Count > 1) segments.Add(currentSegment); return segments; } private void CreateLayerIfNotExists(Database db, string layerName, short colorIndex) { using (Transaction tr = db.TransactionManager.StartTransaction()) { LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; if (lt.Has(layerName)) return; lt.UpgradeOpen(); LayerTableRecord ltr = new LayerTableRecord { Name = layerName, Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex) }; lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); tr.Commit(); } } private bool IsCached(string blockName, Point3d pos, double angle) { return _existingEntitiesCache?.Contains((blockName, pos, angle)) ?? false; } #endregion #region BPS命令辅助方法 private Dictionary<string, int> CountBlocks(Database db, ObjectIdCollection ids) { var stats = new Dictionary<string, int>(); using (Transaction tr = db.TransactionManager.StartTransaction()) { foreach (ObjectId id in ids) { BlockReference br = tr.GetObject(id, OpenMode.ForRead) as BlockReference; if (br == null) continue; string blockName = br.Name; stats[blockName] = stats.TryGetValue(blockName, out int count) ? count + 1 : 1; } tr.Commit(); } return stats; } private List<TableRowData> BuildTableData(Dictionary<string, int> stats, bool isSingleBlockMode) { var tableData = new List<TableRowData>(); if (isSingleBlockMode) { // 点选模式 - 只有一行数据 var item = stats.First(); tableData.Add(new TableRowData { MaterialName = GetMaterialName(item.Key), Length = GetLengthFromName(item.Key), Quantity = item.Value.ToString() }); } else { // 框选模式 - 按块名排序 foreach (var item in stats.OrderBy(kvp => kvp.Key)) { tableData.Add(new TableRowData { MaterialName = GetMaterialName(item.Key), Length = GetLengthFromName(item.Key), Quantity = item.Value.ToString() }); } } return tableData; } private string GetMaterialName(string blockName) { if (blockName.Contains("横杆")) return "横杆"; if (blockName.Contains("立杆")) return "立杆"; if (blockName.Contains("斜拉杆")) return "斜拉杆"; if (blockName.Contains("顶托")) return "顶托"; if (blockName.Contains("底托")) return "底托"; return "其他杆件"; } private string GetLengthFromName(string blockName) { Match match = Regex.Match(blockName, @"\d+"); return match.Success ? match.Value : "\\"; } private void InsertStatTable(Database db, Editor ed, List<TableRowData> data, string tableTitle) { using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; // 获取表格插入点 PromptPointResult ppr = ed.GetPoint("\n请选择表格插入点: "); if (ppr.Status != PromptStatus.OK) return; Point3d insertPoint = ppr.Value; // 创建表格对象 Table table = new Table(); table.Position = insertPoint; table.SetSize(data.Count + 2, 4); // 标题行+表头行+数据行 table.SetRowHeight(3.0); table.SetColumnWidth(0, 6.0); table.SetColumnWidth(1, 18.0); table.SetColumnWidth(2, 18.0); table.SetColumnWidth(3, 18.0); table.Layer = "0"; // 合并标题行单元格 table.MergeCells(CellRange.Create(table, 0, 0, 0, 3)); // 设置标题行 table.Cells[0, 0].TextString = $"{tableTitle} 材料统计表"; table.Cells[0, 0].Alignment = CellAlignment.MiddleCenter; table.Cells[0, 0].TextHeight = 3.0; // 设置表头行 table.Cells[1, 0].TextString = "序号"; table.Cells[1, 1].TextString = "材料名称"; table.Cells[1, 2].TextString = "杆件长度(mm)"; table.Cells[1, 3].TextString = "合计数量/根"; // 设置表头样式(全部居中) for (int col = 0; col < 4; col++) { table.Cells[1, col].Alignment = CellAlignment.MiddleCenter; table.Cells[1, col].TextHeight = 2.5; } // 填充数据行 for (int i = 0; i < data.Count; i++) { int row = i + 2; table.Cells[row, 0].TextString = (i + 1).ToString(); table.Cells[row, 1].TextString = data[i].MaterialName ?? "\\"; table.Cells[row, 2].TextString = data[i].Length ?? "\\"; table.Cells[row, 3].TextString = data[i].Quantity ?? "\\"; // 设置数据行样式(全部居中) for (int col = 0; col < 4; col++) { table.Cells[row, col].Alignment = CellAlignment.MiddleCenter; table.Cells[row, col].TextHeight = 2.5; } } btr.AppendEntity(table); tr.AddNewlyCreatedDBObject(table, true); tr.Commit(); } } #endregion #region 通用辅助方法 private void ShowErrorDialog(string message) { Application.ShowAlertDialog(message); } private void HandleException(Editor ed, SysException ex) { string errorType = ".NET"; ed.WriteMessage($"\n{errorType}错误: {ex.Message}"); ShowErrorDialog($"发生{errorType}异常: {ex.Message}\n详细请查看命令行日志"); } private void HandleException(Editor ed, AecException ex) { string errorType = "AutoCAD"; ed.WriteMessage($"\n{errorType}错误: {ex.Message} (状态: {ex.ErrorStatus})"); ShowErrorDialog($"发生{errorType}异常: {ex.Message}\n详细请查看命令行日志"); } #endregion #region 数据结构 private class TableRowData { public string MaterialName { get; set; } public string Length { get; set; } public string Quantity { get; set; } } private class Segment { public List<Point3d> Points { get; set; } = new List<Point3d>(); } private class Point3dComparer : IEqualityComparer<Point3d> { private readonly double _tolerance; public Point3dComparer(double tolerance) => _tolerance = tolerance; public bool Equals(Point3d p1, Point3d p2) => p1.DistanceTo(p2) < _tolerance; public int GetHashCode(Point3d p) => p.X.GetHashCode() ^ p.Y.GetHashCode() ^ p.Z.GetHashCode(); } private class EntityKeyComparer : IEqualityComparer<(string, Point3d, double)> { public bool Equals((string, Point3d, double) x, (string, Point3d, double) y) => x.Item1 == y.Item1 && x.Item2.DistanceTo(y.Item2) < SkipTolerance && Math.Abs(x.Item3 - y.Item3) < 0.01; public int GetHashCode((string, Point3d, double) obj) => obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode() ^ obj.Item3.GetHashCode(); } private HashSet<(string blockName, Point3d pos, double angle)> _existingEntitiesCache; #endregion } } 点选”点选块统计”和”框选所有块统计”不起作用 按原代码修改一套完整的代码
最新发布
07-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值