云计算百科
云计算领域专业知识百科平台

第七章:进阶篇——事件监听、外部服务集成与性能优化

前六章已经让你具备了独立开发Revit插件的能力。但实际工程中,我们往往需要应对更复杂的场景:自动响应模型变化、与外部系统通信、处理大量数据时的性能瓶颈。本章作为进阶篇,将带你探索更高级的API应用。


7.1 Revit事件监听(Event Handling)

事件监听让你能够在特定操作发生时自动执行自定义代码,是实现自动化工作流的核心技术。

7.1.1 事件类型概览
事件类别常用事件触发时机典型应用
文档事件 DocumentOpened 打开文档时 自动加载配置、初始化环境
DocumentSaving 保存文档前 自动备份、数据校验
DocumentSaved 保存文档后 同步导出报告
DocumentClosing 关闭文档前 清理临时数据
应用事件 ApplicationInitialized Revit启动完成 注册全局热键
ApplicationClosing Revit关闭前 保存日志
视图事件 ViewActivated 切换视图时 自动应用视图模板
选择事件 SelectionChanged 选择集变化时 实时显示构件属性
命令事件 CommandExecuted 任意命令执行后 操作日志记录
7.1.2 实战:自动同步构件编号

场景:当用户修改了某个构件的"标记"参数后,自动更新其关联构件的编号。

csharp

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB.Events;

namespace AdvancedTools
{
[Transaction(TransactionMode.Manual)]
public class EventMonitorApp : IExternalApplication
{
private Document _currentDoc;
private Dictionary<ElementId, string> _elementMarkCache = new Dictionary<ElementId, string>();

public Result OnStartup(UIControlledApplication application)
{
// 注册文档打开事件
application.ControlledApplication.DocumentOpened += OnDocumentOpened;

// 注册文档保存后事件(用于验证)
application.ControlledApplication.DocumentSaved += OnDocumentSaved;

// 注册选择变化事件(需要UIApplication)
// 注意:此事件需要在有UIDocument时才能使用,推荐在命令中动态注册
return Result.Succeeded;
}

public Result OnShutdown(UIControlledApplication application)
{
// 取消事件注册(防止内存泄漏)
application.ControlledApplication.DocumentOpened -= OnDocumentOpened;
application.ControlledApplication.DocumentSaved -= OnDocumentSaved;
return Result.Succeeded;
}

/// <summary>
/// 文档打开时初始化缓存
/// </summary>
private void OnDocumentOpened(object sender, DocumentOpenedEventArgs args)
{
_currentDoc = args.Document;

// 加载所有构件的"标记"到缓存
LoadMarkCache(_currentDoc);

// 注册文档修改事件(当参数变化时触发)
_currentDoc.DocumentChanged += OnDocumentChanged;
}

/// <summary>
/// 文档修改时检测"标记"参数变化
/// </summary>
private void OnDocumentChanged(object sender, DocumentChangedEventArgs args)
{
Document doc = sender as Document;
if (doc == null) return;

// 获取所有被修改的元素
ICollection<ElementId> modifiedIds = args.GetModifiedElementIds();
if (modifiedIds == null || modifiedIds.Count == 0) return;

List<ElementId> markChangedElements = new List<ElementId>();

foreach (ElementId id in modifiedIds)
{
Element elem = doc.GetElement(id);
if (elem == null) continue;

// 检查"标记"参数是否变化
string currentMark = ParameterHelper.GetParameterString(elem, "标记");

if (_elementMarkCache.TryGetValue(id, out string oldMark))
{
if (currentMark != oldMark)
{
// 标记发生了变化!
markChangedElements.Add(id);
Console.WriteLine($"构件 {elem.Id} 的标记从 '{oldMark}' 变为 '{currentMark}'");
}
}
}

// 如果有标记变化,执行自动联动逻辑
if (markChangedElements.Count > 0)
{
HandleMarkChange(doc, markChangedElements);
}

// 更新缓存
RefreshMarkCache(doc, modifiedIds);
}

/// <summary>
/// 处理标记变化:更新关联构件
/// </summary>
private void HandleMarkChange(Document doc, List<ElementId> changedIds)
{
using (Transaction trans = new Transaction(doc, "自动更新关联编号"))
{
trans.Start();

foreach (ElementId id in changedIds)
{
Element elem = doc.GetElement(id);
string newMark = ParameterHelper.GetParameterString(elem, "标记");

if (string.IsNullOrEmpty(newMark)) continue;

// 示例:如果修改了墙的标记,自动更新同一楼层的所有门
if (elem is Wall wall)
{
string levelName = GetElementLevelName(doc, wall);

// 查找同一楼层、同一区域的门
FilteredElementCollector collector = new FilteredElementCollector(doc);
var doors = collector
.OfCategory(BuiltInCategory.OST_Doors)
.Cast<FamilyInstance>()
.Where(d => GetElementLevelName(doc, d) == levelName)
.ToList();

foreach (FamilyInstance door in doors)
{
string doorMark = ParameterHelper.GetParameterString(door, "标记");
if (string.IsNullOrEmpty(doorMark))
{
// 自动生成门编号:W-墙标记-D-序号
string newDoorMark = $"{newMark}-D-{GetNextDoorNumber(doc, levelName)}";
ParameterHelper.SetParameterValue(door, "标记", newDoorMark);
}
}
}
}

trans.Commit();
}
}

private string GetElementLevelName(Document doc, Element elem)
{
Parameter levelParam = elem.get_Parameter(BuiltInParameter.LEVEL_PARAM);
if (levelParam != null && levelParam.HasValue)
{
ElementId levelId = levelParam.AsElementId();
if (levelId != null)
{
Element level = doc.GetElement(levelId);
return level?.Name ?? "未知";
}
}
return "未知";
}

private int GetNextDoorNumber(Document doc, string levelName)
{
// 统计该楼层已有门数量
FilteredElementCollector collector = new FilteredElementCollector(doc);
var doors = collector
.OfCategory(BuiltInCategory.OST_Doors)
.Cast<FamilyInstance>()
.Where(d => GetElementLevelName(doc, d) == levelName)
.Count();
return doors + 1;
}

private void LoadMarkCache(Document doc)
{
_elementMarkCache.Clear();

FilteredElementCollector collector = new FilteredElementCollector(doc);
var elements = collector
.OfClass(typeof(FamilyInstance))
.Cast<FamilyInstance>()
.Where(e => e.Category != null)
.ToList();

foreach (Element elem in elements)
{
string mark = ParameterHelper.GetParameterString(elem, "标记");
if (!string.IsNullOrEmpty(mark))
{
_elementMarkCache[elem.Id] = mark;
}
}
}

private void RefreshMarkCache(Document doc, ICollection<ElementId> ids)
{
foreach (ElementId id in ids)
{
Element elem = doc.GetElement(id);
if (elem == null) continue;

string mark = ParameterHelper.GetParameterString(elem, "标记");
if (!string.IsNullOrEmpty(mark))
{
_elementMarkCache[id] = mark;
}
else
{
_elementMarkCache.Remove(id);
}
}
}

private void OnDocumentSaved(object sender, DocumentSavedEventArgs args)
{
// 保存时记录日志
string logMessage = $"文档 {args.Document.Title} 于 {DateTime.Now} 被保存";
Console.WriteLine(logMessage);

// 可在此写入日志文件
WriteLog(logMessage);
}

private void WriteLog(string message)
{
string logPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"RevitLogs",
$"log_{DateTime.Now:yyyyMMdd}.txt"
);

System.IO.Directory.CreateDirectory(
System.IO.Path.GetDirectoryName(logPath)
);

System.IO.File.AppendAllText(logPath,
$"{DateTime.Now:HH:mm:ss} – {message}{Environment.NewLine}"
);
}
}
}

7.1.3 事件监听的最佳实践
最佳实践说明
避免耗时操作 事件处理中不要执行长时间任务,可改用异步或后台线程
注意事务管理 在事件中修改文档必须使用新的事务,不要使用正在执行的事务
及时取消注册 在 OnShutdown 中取消所有事件注册,防止内存泄漏
异常捕获 事件中一定要加 try-catch,否则异常可能导致Revit崩溃
避免无限循环 事件中修改文档可能再次触发相同事件,需设计防重入机制

7.2 外部服务集成(Web API调用)

现代BIM应用往往需要与外部系统(如项目管理平台、物料系统)进行数据交互。

7.2.1 场景:同步构件数据到云端

csharp

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Newtonsoft.Json;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace AdvancedTools
{
public class CloudSyncService
{
private static readonly HttpClient _httpClient = new HttpClient();
private const string API_BASE_URL = "https://your-api-server.com/api/v1";

static CloudSyncService()
{
_httpClient.Timeout = TimeSpan.FromSeconds(30);
_httpClient.DefaultRequestHeaders.Add("User-Agent", "RevitPlugin/1.0");
}

/// <summary>
/// 同步构件数据到云端
/// </summary>
public async Task<SyncResult> SyncElementsAsync(
Document doc,
List<ElementId> elementIds,
string apiToken)
{
SyncResult result = new SyncResult { SuccessCount = 0, FailedCount = 0 };

if (string.IsNullOrEmpty(apiToken))
{
result.ErrorMessage = "未提供API令牌";
return result;
}

// 设置认证头
if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
{
_httpClient.DefaultRequestHeaders.Remove("Authorization");
}
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiToken}");

foreach (ElementId id in elementIds)
{
Element elem = doc.GetElement(id);
if (elem == null) continue;

try
{
// 构建构件数据
ElementSyncData data = BuildSyncData(elem);
string jsonData = JsonConvert.SerializeObject(data);

HttpContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");

// 发送POST请求
HttpResponseMessage response = await _httpClient.PostAsync(
$"{API_BASE_URL}/elements/sync",
content
);

if (response.IsSuccessStatusCode)
{
result.SuccessCount++;
}
else
{
result.FailedCount++;
result.ErrorMessage = $"同步失败: {response.StatusCode} – {await response.Content.ReadAsStringAsync()}";
}
}
catch (Exception ex)
{
result.FailedCount++;
result.ErrorMessage = $"异常: {ex.Message}";
}
}

return result;
}

private ElementSyncData BuildSyncData(Element elem)
{
ElementSyncData data = new ElementSyncData
{
ElementId = elem.Id.IntegerValue,
UniqueId = elem.UniqueId,
Name = elem.Name,
Category = elem.Category?.Name,
Level = GetLevelName(elem),
Mark = ParameterHelper.GetParameterString(elem, "标记"),
TypeName = GetTypeName(elem),
SyncTime = DateTime.Now
};

// 获取位置信息
Location loc = elem.Location;
if (loc is LocationPoint pointLoc)
{
data.LocationX = pointLoc.Point.X;
data.LocationY = pointLoc.Point.Y;
data.LocationZ = pointLoc.Point.Z;
}
else if (loc is LocationCurve curveLoc)
{
XYZ start = curveLoc.Curve.GetEndPoint(0);
XYZ end = curveLoc.Curve.GetEndPoint(1);
data.LocationX = (start.X + end.X) / 2;
data.LocationY = (start.Y + end.Y) / 2;
data.LocationZ = (start.Z + end.Z) / 2;
}

// 获取几何参数
if (elem is FamilyInstance instance)
{
data.Width = GetParameterDouble(instance, BuiltInParameter.FAMILY_WIDTH_PARAM);
data.Height = GetParameterDouble(instance, BuiltInParameter.FAMILY_HEIGHT_PARAM);
}
else if (elem is Wall wall)
{
data.Width = wall.Width;
data.Height = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)?.AsDouble() ?? 0;
}

return data;
}

private string GetLevelName(Element elem)
{
Parameter levelParam = elem.get_Parameter(BuiltInParameter.LEVEL_PARAM);
if (levelParam != null && levelParam.HasValue)
{
Element level = elem.Document.GetElement(levelParam.AsElementId());
return level?.Name ?? "未知";
}
return "未知";
}

private string GetTypeName(Element elem)
{
if (elem is FamilyInstance instance)
{
return instance.Symbol?.Name ?? "未知";
}
else if (elem is Wall wall)
{
return wall.WallType?.Name ?? "未知";
}
return elem.GetType().Name;
}

private double GetParameterDouble(Element elem, BuiltInParameter param)
{
Parameter p = elem.get_Parameter(param);
return p?.AsDouble() ?? 0;
}

public class SyncResult
{
public int SuccessCount { get; set; }
public int FailedCount { get; set; }
public string ErrorMessage { get; set; }
}

private class ElementSyncData
{
public int ElementId { get; set; }
public string UniqueId { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public string Level { get; set; }
public string Mark { get; set; }
public string TypeName { get; set; }
public double LocationX { get; set; }
public double LocationY { get; set; }
public double LocationZ { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public DateTime SyncTime { get; set; }
}
}
}

7.2.2 带进度显示的同步命令

csharp

[Transaction(TransactionMode.ReadOnly)]
public class SyncToCloudCommand : IExternalCommand
{
public async Task<Result> ExecuteAsync(ExternalCommandData commandData,
ref string message, ElementSet elements)
{
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Document doc = uidoc.Document;

// 获取选中的元素
ICollection<ElementId> selectedIds = uidoc.Selection.GetElementIds();
if (selectedIds.Count == 0)
{
TaskDialog.Show("提示", "请先选中要同步的构件!");
return Result.Cancelled;
}

// 获取API令牌(实际使用中应从安全存储获取)
string apiToken = GetApiToken();

if (string.IsNullOrEmpty(apiToken))
{
TaskDialog.Show("错误", "未配置API令牌,请先登录!");
return Result.Failed;
}

// 显示进度对话框
ProgressDialog progress = new ProgressDialog("正在同步到云端…");
IntPtr revitHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
new WindowInteropHelper(progress).Owner = revitHandle;
progress.Show();

// 执行同步
CloudSyncService service = new CloudSyncService();
var result = await service.SyncElementsAsync(doc, selectedIds.ToList(), apiToken);

progress.Close();

// 显示结果
TaskDialog.Show("同步完成",
$"成功: {result.SuccessCount}\\n" +
$"失败: {result.FailedCount}\\n" +
(string.IsNullOrEmpty(result.ErrorMessage) ? "" : $"错误: {result.ErrorMessage}"));

return Result.Succeeded;
}

private string GetApiToken()
{
// 从配置文件或注册表读取
// 实际使用中建议使用加密存储
return System.Configuration.ConfigurationManager.AppSettings["ApiToken"];
}
}


7.3 性能优化策略

当处理大量数据(数千个构件)时,代码性能会显著影响用户体验。

7.3.1 性能瓶颈分析
操作耗时原因影响程度
遍历所有参数 每个参数都需反射调用 ⭐⭐⭐⭐⭐
频繁调用 doc.GetElement(id) I/O操作 ⭐⭐⭐⭐
未批量化的事务提交 每次修改都提交事务 ⭐⭐⭐⭐⭐
LINQ在大集合上的复杂查询 算法复杂度高 ⭐⭐⭐
7.3.2 优化技巧

技巧1:使用收集器的批量加载

csharp

// ❌ 不推荐:循环中逐个获取
foreach (ElementId id in ids)
{
Element elem = doc.GetElement(id); // 每次都访问文档
// 处理…
}

// ✅ 推荐:一次性批量加载
ICollection<Element> elements = new FilteredElementCollector(doc)
.WherePasses(new ElementIdSetFilter(ids.ToHashSet()))
.ToElements();

技巧2:减少参数访问次数

csharp

// ❌ 不推荐:每次读取单独调用
foreach (Element elem in elements)
{
string param1 = elem.LookupParameter("参数1")?.AsString();
string param2 = elem.LookupParameter("参数2")?.AsString();
}

// ✅ 推荐:缓存参数引用
foreach (Element elem in elements)
{
Parameter p1 = elem.LookupParameter("参数1");
Parameter p2 = elem.LookupParameter("参数2");
// 一次性读取
}

技巧3:合并事务

csharp

// ❌ 不推荐:每个元素单独事务
foreach (Element elem in elements)
{
using (Transaction trans = new Transaction(doc, "修改"))
{
trans.Start();
// 修改一个元素
trans.Commit();
}
}

// ✅ 推荐:所有修改用一个事务
using (Transaction trans = new Transaction(doc, "批量修改"))
{
trans.Start();
foreach (Element elem in elements)
{
// 修改所有元素
}
trans.Commit();
}

技巧4:使用并行处理(对于只读操作)

csharp

// 并行读取参数(仅用于只读操作)
Parallel.ForEach(elements, elem =>
{
string mark = ParameterHelper.GetParameterString(elem, "标记");
// 将结果存入线程安全集合
ConcurrentBag<ElementInfo> bag = new ConcurrentBag<ElementInfo>();
bag.Add(new ElementInfo { Id = elem.Id, Mark = mark });
});

7.3.3 性能优化的完整示例

csharp

/// <summary>
/// 高性能的批量参数读取器
/// </summary>
public class HighPerformanceParameterReader
{
private Document _doc;
private Dictionary<ElementId, Dictionary<string, string>> _cache;

public HighPerformanceParameterReader(Document doc)
{
_doc = doc;
_cache = new Dictionary<ElementId, Dictionary<string, string>>();
}

/// <summary>
/// 批量读取多个元素的多个参数(性能优化版)
/// </summary>
public Dictionary<ElementId, Dictionary<string, string>> ReadParameters(
List<ElementId> elementIds,
List<string> paramNames)
{
// 1. 批量获取元素(减少I/O)
var elements = new FilteredElementCollector(_doc)
.WherePasses(new ElementIdSetFilter(elementIds.ToHashSet()))
.Cast<Element>()
.ToDictionary(e => e.Id);

// 2. 并行读取参数(针对只读操作)
var result = new ConcurrentDictionary<ElementId, Dictionary<string, string>>();

Parallel.ForEach(elements.Values, elem =>
{
var paramValues = new Dictionary<string, string>();

// 批量查找参数
foreach (string paramName in paramNames)
{
Parameter param = elem.LookupParameter(paramName);
if (param != null && param.HasValue)
{
paramValues[paramName] = param.AsString() ?? "";
}
}

result[elem.Id] = paramValues;
});

return result.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}

/// <summary>
/// 批量修改参数(优化的单事务方式)
/// </summary>
public void BatchSetParameters(
Dictionary<ElementId, Dictionary<string, string>> updates,
Action<int, int> progressCallback = null)
{
int total = updates.Count;
int processed = 0;

using (Transaction trans = new Transaction(_doc, "批量修改参数"))
{
trans.Start();

foreach (var kvp in updates)
{
ElementId id = kvp.Key;
Element elem = _doc.GetElement(id);
if (elem == null) continue;

foreach (var paramUpdate in kvp.Value)
{
Parameter param = elem.LookupParameter(paramUpdate.Key);
if (param != null && !param.IsReadOnly)
{
param.Set(paramUpdate.Value);
}
}

processed++;
progressCallback?.Invoke(processed, total);
}

trans.Commit();
}
}
}


7.4 异步编程与Revit

Revit API本身是单线程的,但你可以结合异步操作改善用户体验。

7.4.1 异步命令实现

csharp

[Transaction(TransactionMode.ReadOnly)]
public class AsyncCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData,
ref string message, ElementSet elements)
{
// 使用async/await模式
var task = ExecuteAsync(commandData, ref message, elements);
task.Wait(); // 等待异步完成
return task.Result;
}

private async Task<Result> ExecuteAsync(ExternalCommandData commandData,
ref string message, ElementSet elements)
{
// 模拟长时间操作
await Task.Delay(5000);

// 在后台线程处理数据(只读操作)
var result = await Task.Run(() =>
{
// 这里执行只读操作
return ProcessData(commandData);
});

// 回到主线程更新UI
// 注意:Revit操作必须回到主线程
return result;
}

private Result ProcessData(ExternalCommandData commandData)
{
// 只读数据处理
return Result.Succeeded;
}
}

7.4.2 使用BackgroundWorker显示进度

csharp

using System.ComponentModel;
using System.Threading;

public class ProgressCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData,
ref string message, ElementSet elements)
{
var backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = false
};

backgroundWorker.DoWork += (sender, e) =>
{
var worker = sender as BackgroundWorker;
var data = e.Argument as ProcessData;

// 执行耗时操作
for (int i = 0; i < data.Total; i++)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}

// 处理单个元素
ProcessElement(data.Elements[i]);

// 报告进度
int percent = (i + 1) * 100 / data.Total;
worker.ReportProgress(percent, $"处理第 {i + 1}/{data.Total} 个");
}

e.Result = data;
};

backgroundWorker.ProgressChanged += (sender, e) =>
{
// 更新UI进度
string status = e.UserState as string;
UpdateProgress(e.ProgressPercentage, status);
};

backgroundWorker.RunWorkerCompleted += (sender, e) =>
{
if (e.Error != null)
{
ShowError(e.Error.Message);
}
else if (e.Cancelled)
{
ShowMessage("操作已取消");
}
else
{
ShowMessage("操作完成!");
}
};

// 启动后台任务
var data = new ProcessData { Elements = GetElements(), Total = 100 };
backgroundWorker.RunWorkerAsync(data);

return Result.Succeeded;
}
}


7.5 错误处理与日志系统

一个健壮的插件必须有完善的错误处理和日志系统。

7.5.1 自定义异常类

csharp

namespace AdvancedTools.Exceptions
{
/// <summary>
/// 插件基础异常
/// </summary>
public class PluginException : Exception
{
public string ErrorCode { get; set; }

public PluginException() : base() { }

public PluginException(string message) : base(message) { }

public PluginException(string message, Exception inner) : base(message, inner) { }

public PluginException(string errorCode, string message) : base(message)
{
ErrorCode = errorCode;
}
}

/// <summary>
/// 参数相关异常
/// </summary>
public class ParameterException : PluginException
{
public string ParameterName { get; set; }
public ElementId ElementId { get; set; }

public ParameterException(string paramName, ElementId elementId, string message)
: base("PARAM_001", message)
{
ParameterName = paramName;
ElementId = elementId;
}
}

/// <summary>
/// 网络/API相关异常
/// </summary>
public class ApiException : PluginException
{
public int StatusCode { get; set; }

public ApiException(int statusCode, string message)
: base($"API_{statusCode}", message)
{
StatusCode = statusCode;
}
}
}

7.5.2 日志管理器

csharp

using System;
using System.IO;
using System.Threading;

namespace AdvancedTools.Logging
{
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private string _logDirectory;
private string _currentLogFile;

public enum LogLevel
{
Debug,
Info,
Warning,
Error,
Fatal
}

private Logger()
{
_logDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyRevitPlugin",
"Logs"
);

Directory.CreateDirectory(_logDirectory);
_currentLogFile = Path.Combine(_logDirectory,
$"log_{DateTime.Now:yyyyMMdd}.txt");
}

public static Logger Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new Logger();
}
}
}
return _instance;
}
}

public void Debug(string message)
{
Log(LogLevel.Debug, message);
}

public void Info(string message)
{
Log(LogLevel.Info, message);
}

public void Warning(string message)
{
Log(LogLevel.Warning, message);
}

public void Error(string message, Exception ex = null)
{
string fullMessage = ex != null
? $"{message}{Environment.NewLine}异常: {ex.Message}{Environment.NewLine}堆栈: {ex.StackTrace}"
: message;
Log(LogLevel.Error, fullMessage);
}

public void Fatal(string message, Exception ex = null)
{
string fullMessage = ex != null
? $"{message}{Environment.NewLine}异常: {ex.Message}{Environment.NewLine}堆栈: {ex.StackTrace}"
: message;
Log(LogLevel.Fatal, fullMessage);
}

private void Log(LogLevel level, string message)
{
string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level.ToString().ToUpper()}] {message}";

try
{
// 写入文件(异步写入提高性能)
ThreadPool.QueueUserWorkItem(_ =>
{
lock (_lock)
{
File.AppendAllText(_currentLogFile, logEntry + Environment.NewLine);
}
});
}
catch
{
// 日志写入失败不应影响主程序
}
}

/// <summary>
/// 清理7天前的日志文件
/// </summary>
public void CleanOldLogs()
{
try
{
var files = Directory.GetFiles(_logDirectory, "log_*.txt");
foreach (string file in files)
{
var fileInfo = new FileInfo(file);
if (fileInfo.CreationTime < DateTime.Now.AddDays(-7))
{
File.Delete(file);
}
}
}
catch { }
}
}

/// <summary>
/// 全局异常处理器
/// </summary>
public class GlobalExceptionHandler
{
public static void Setup()
{
// 捕获未处理的异常
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
Exception ex = args.ExceptionObject as Exception;
Logger.Instance.Fatal("程序发生未处理异常", ex);
};

// 捕获线程异常
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += (sender, args) =>
{
Logger.Instance.Error("任务异常", args.Exception);
args.SetObserved();
};
}
}
}


7.6 本章小结

主题核心内容应用价值
事件监听 文档/应用/视图事件 自动化工作流、实时同步
Web API集成 HttpClient + JSON序列化 BIM与外部系统互联
性能优化 批量操作、缓存、并行 处理大规模数据
异步编程 async/await, BackgroundWorker 改善用户体验
日志系统 结构化日志、异常捕获 生产环境运维
赞(0)
未经允许不得转载:网硕互联帮助中心 » 第七章:进阶篇——事件监听、外部服务集成与性能优化
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!