阶段三:删除未使用的WPF Services
删除的文件: - src/UI/WPF/Services/DataBindingBestPractices.cs (0次外部引用) - src/UI/WPF/Services/CrossViewModelSynchronizer.cs (仅被BestPractices使用) - src/UI/WPF/Collections/VirtualizedObservableCollection.cs (仅被BestPractices引用) 更新: - 从NavisworksTransportPlugin.csproj中移除引用 编译验证通过 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b048235657
commit
52bb3da0eb
@ -154,7 +154,8 @@
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" AStarTestRunner.csproj /p:Configuration=Debug /p:Platform=AnyCPU)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" AStarTestRunner.csproj)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" NavisworksTransport.UnitTests.csproj)",
|
||||
"Bash(\"bin\\Debug\\AStarTestRunner.exe\")"
|
||||
"Bash(\"bin\\Debug\\AStarTestRunner.exe\")",
|
||||
"Bash(git restore:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"additionalDirectories": [
|
||||
|
||||
@ -227,15 +227,12 @@
|
||||
|
||||
<!-- UI - WPF Collections -->
|
||||
<Compile Include="src\UI\WPF\Collections\ThreadSafeObservableCollection.cs" />
|
||||
<Compile Include="src\UI\WPF\Collections\VirtualizedObservableCollection.cs" />
|
||||
|
||||
<!-- UI - WPF Interfaces -->
|
||||
<Compile Include="src\UI\WPF\Interfaces\IPropertyChangeNotifier.cs" />
|
||||
|
||||
<!-- UI - WPF Services -->
|
||||
<Compile Include="src\UI\WPF\Services\BindingExpressionOptimizer.cs" />
|
||||
<Compile Include="src\UI\WPF\Services\CrossViewModelSynchronizer.cs" />
|
||||
<Compile Include="src\UI\WPF\Services\DataBindingBestPractices.cs" />
|
||||
<Compile Include="src\UI\WPF\Services\DataBindingPerformanceMonitor.cs" />
|
||||
<Compile Include="src\UI\WPF\Services\SmartDataBindingOptimizer.cs" />
|
||||
|
||||
|
||||
@ -1,982 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
using NavisworksTransport.UI.WPF.Services;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 虚拟化的可观察集合 - 为大数据集合提供高性能的WPF绑定支持
|
||||
/// 只在内存中保存可见项目,按需加载数据,支持智能预加载和缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T">集合元素类型</typeparam>
|
||||
public class VirtualizedObservableCollection<T> : IList<T>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||
{
|
||||
#region 字段和属性
|
||||
|
||||
// 数据提供器
|
||||
private readonly IVirtualDataProvider<T> _dataProvider;
|
||||
private readonly DataBindingPerformanceMonitor _performanceMonitor;
|
||||
|
||||
// 虚拟化设置
|
||||
private int _pageSize = 100;
|
||||
private int _cacheSize = 500;
|
||||
private int _preloadDistance = 50;
|
||||
|
||||
// 缓存管理
|
||||
private readonly Dictionary<int, T> _itemCache = new Dictionary<int, T>();
|
||||
private readonly Dictionary<int, DateTime> _cacheTimestamps = new Dictionary<int, DateTime>();
|
||||
private readonly object _cacheLock = new object();
|
||||
|
||||
// 状态管理
|
||||
private int _totalCount = 0;
|
||||
private bool _isLoading = false;
|
||||
private readonly object _loadingLock = new object();
|
||||
|
||||
// 视口管理
|
||||
private int _viewportStart = 0;
|
||||
private int _viewportSize = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 集合变更事件
|
||||
/// </summary>
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 属性变更事件
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 总项目数量
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get => _totalCount;
|
||||
private set
|
||||
{
|
||||
if (_totalCount != value)
|
||||
{
|
||||
_totalCount = value;
|
||||
OnPropertyChanged(nameof(Count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 页面大小 - 每次从数据提供器加载的项目数量
|
||||
/// </summary>
|
||||
public int PageSize
|
||||
{
|
||||
get => _pageSize;
|
||||
set
|
||||
{
|
||||
if (value > 0 && _pageSize != value)
|
||||
{
|
||||
_pageSize = value;
|
||||
OnPropertyChanged(nameof(PageSize));
|
||||
InvalidateCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存大小 - 内存中保存的最大项目数量
|
||||
/// </summary>
|
||||
public int CacheSize
|
||||
{
|
||||
get => _cacheSize;
|
||||
set
|
||||
{
|
||||
if (value > 0 && _cacheSize != value)
|
||||
{
|
||||
_cacheSize = value;
|
||||
OnPropertyChanged(nameof(CacheSize));
|
||||
CleanupCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预加载距离 - 距离视口边缘多远开始预加载
|
||||
/// </summary>
|
||||
public int PreloadDistance
|
||||
{
|
||||
get => _preloadDistance;
|
||||
set
|
||||
{
|
||||
if (value >= 0 && _preloadDistance != value)
|
||||
{
|
||||
_preloadDistance = value;
|
||||
OnPropertyChanged(nameof(PreloadDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前视口起始位置
|
||||
/// </summary>
|
||||
public int ViewportStart
|
||||
{
|
||||
get => _viewportStart;
|
||||
set
|
||||
{
|
||||
if (_viewportStart != value)
|
||||
{
|
||||
_viewportStart = Math.Max(0, Math.Min(value, Math.Max(0, Count - _viewportSize)));
|
||||
OnPropertyChanged(nameof(ViewportStart));
|
||||
TriggerPreload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视口大小 - 可见区域的项目数量
|
||||
/// </summary>
|
||||
public int ViewportSize
|
||||
{
|
||||
get => _viewportSize;
|
||||
set
|
||||
{
|
||||
if (value > 0 && _viewportSize != value)
|
||||
{
|
||||
_viewportSize = value;
|
||||
OnPropertyChanged(nameof(ViewportSize));
|
||||
TriggerPreload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在加载数据
|
||||
/// </summary>
|
||||
public bool IsLoading => _isLoading;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存命中率
|
||||
/// </summary>
|
||||
public double CacheHitRatio { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为只读集合
|
||||
/// </summary>
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
|
||||
/// <summary>
|
||||
/// 初始化虚拟化集合
|
||||
/// </summary>
|
||||
/// <param name="dataProvider">数据提供器</param>
|
||||
public VirtualizedObservableCollection(IVirtualDataProvider<T> dataProvider)
|
||||
{
|
||||
_dataProvider = dataProvider ?? throw new ArgumentNullException(nameof(dataProvider));
|
||||
_performanceMonitor = DataBindingPerformanceMonitor.Instance;
|
||||
|
||||
// 订阅数据提供器事件
|
||||
if (dataProvider is INotifyPropertyChanged notifyProvider)
|
||||
{
|
||||
notifyProvider.PropertyChanged += DataProvider_PropertyChanged;
|
||||
}
|
||||
|
||||
// 初始化总数量
|
||||
InitializeTotalCountAsync();
|
||||
|
||||
LogManager.Info($"VirtualizedObservableCollection<{typeof(T).Name}>已初始化");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 索引器和基本访问
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置指定索引处的元素
|
||||
/// </summary>
|
||||
/// <param name="index">元素索引</param>
|
||||
/// <returns>指定位置的元素</returns>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
return GetItemAsync(index).Result;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
_ = SetItemAsync(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取指定索引的项目
|
||||
/// </summary>
|
||||
/// <param name="index">项目索引</param>
|
||||
/// <returns>项目或null</returns>
|
||||
public async Task<T> GetItemAsync(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
return default(T);
|
||||
|
||||
using (_performanceMonitor.BeginCollectionUpdate(typeof(VirtualizedObservableCollection<T>), "GetItem", 1))
|
||||
{
|
||||
// 检查缓存
|
||||
lock (_cacheLock)
|
||||
{
|
||||
if (_itemCache.TryGetValue(index, out var cachedItem))
|
||||
{
|
||||
_cacheTimestamps[index] = DateTime.UtcNow;
|
||||
UpdateCacheHitRatio(true);
|
||||
return cachedItem;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCacheHitRatio(false);
|
||||
|
||||
// 异步加载项目
|
||||
var item = await LoadItemAsync(index);
|
||||
|
||||
// 缓存项目
|
||||
lock (_cacheLock)
|
||||
{
|
||||
_itemCache[index] = item;
|
||||
_cacheTimestamps[index] = DateTime.UtcNow;
|
||||
|
||||
// 清理过期缓存
|
||||
if (_itemCache.Count > _cacheSize)
|
||||
{
|
||||
CleanupCache();
|
||||
}
|
||||
}
|
||||
|
||||
// 触发预加载
|
||||
TriggerPreload(index);
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步设置指定索引的项目
|
||||
/// </summary>
|
||||
/// <param name="index">项目索引</param>
|
||||
/// <param name="item">新项目</param>
|
||||
public async Task SetItemAsync(int index, T item)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
return;
|
||||
|
||||
using (_performanceMonitor.BeginCollectionUpdate(typeof(VirtualizedObservableCollection<T>), "SetItem", 1))
|
||||
{
|
||||
// 更新数据提供器
|
||||
await _dataProvider.SetItemAsync(index, item);
|
||||
|
||||
// 更新缓存
|
||||
lock (_cacheLock)
|
||||
{
|
||||
_itemCache[index] = item;
|
||||
_cacheTimestamps[index] = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// 触发变更通知
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
|
||||
NotifyCollectionChangedAction.Replace, item, default(T), index));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据加载和缓存管理
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载单个项目
|
||||
/// </summary>
|
||||
/// <param name="index">项目索引</param>
|
||||
/// <returns>加载的项目</returns>
|
||||
private async Task<T> LoadItemAsync(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_loadingLock)
|
||||
{
|
||||
_isLoading = true;
|
||||
}
|
||||
OnPropertyChanged(nameof(IsLoading));
|
||||
|
||||
return await _dataProvider.GetItemAsync(index);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载项目失败 [索引: {index}]: {ex.Message}");
|
||||
return default(T);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_loadingLock)
|
||||
{
|
||||
_isLoading = false;
|
||||
}
|
||||
OnPropertyChanged(nameof(IsLoading));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步批量加载项目
|
||||
/// </summary>
|
||||
/// <param name="startIndex">起始索引</param>
|
||||
/// <param name="count">加载数量</param>
|
||||
/// <returns>加载的项目列表</returns>
|
||||
private async Task<IEnumerable<T>> LoadRangeAsync(int startIndex, int count)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_loadingLock)
|
||||
{
|
||||
_isLoading = true;
|
||||
}
|
||||
OnPropertyChanged(nameof(IsLoading));
|
||||
|
||||
var endIndex = Math.Min(startIndex + count, Count);
|
||||
var actualCount = endIndex - startIndex;
|
||||
|
||||
if (actualCount <= 0)
|
||||
return Enumerable.Empty<T>();
|
||||
|
||||
return await _dataProvider.GetRangeAsync(startIndex, actualCount);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"批量加载项目失败 [范围: {startIndex}-{startIndex + count}]: {ex.Message}");
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_loadingLock)
|
||||
{
|
||||
_isLoading = false;
|
||||
}
|
||||
OnPropertyChanged(nameof(IsLoading));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发智能预加载
|
||||
/// </summary>
|
||||
/// <param name="accessedIndex">最近访问的索引</param>
|
||||
private void TriggerPreload(int? accessedIndex = null)
|
||||
{
|
||||
if (_preloadDistance <= 0)
|
||||
return;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确定预加载范围
|
||||
var centerIndex = accessedIndex ?? (_viewportStart + _viewportSize / 2);
|
||||
var preloadStart = Math.Max(0, centerIndex - _preloadDistance);
|
||||
var preloadEnd = Math.Min(Count - 1, centerIndex + _preloadDistance);
|
||||
|
||||
// 找出需要预加载的索引
|
||||
var indexesToPreload = new List<int>();
|
||||
lock (_cacheLock)
|
||||
{
|
||||
for (int i = preloadStart; i <= preloadEnd; i++)
|
||||
{
|
||||
if (!_itemCache.ContainsKey(i))
|
||||
{
|
||||
indexesToPreload.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (indexesToPreload.Count == 0)
|
||||
return;
|
||||
|
||||
// 按页面分组预加载
|
||||
var pages = indexesToPreload.GroupBy(i => i / _pageSize);
|
||||
foreach (var page in pages)
|
||||
{
|
||||
var pageStart = page.Key * _pageSize;
|
||||
var pageItems = await LoadRangeAsync(pageStart, _pageSize);
|
||||
|
||||
// 缓存加载的项目
|
||||
lock (_cacheLock)
|
||||
{
|
||||
var itemArray = pageItems.ToArray();
|
||||
for (int i = 0; i < itemArray.Length; i++)
|
||||
{
|
||||
var index = pageStart + i;
|
||||
if (index < Count && !_itemCache.ContainsKey(index))
|
||||
{
|
||||
_itemCache[index] = itemArray[i];
|
||||
_cacheTimestamps[index] = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Debug($"预加载完成: {indexesToPreload.Count}个项目");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"预加载失败: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理过期缓存
|
||||
/// </summary>
|
||||
private void CleanupCache()
|
||||
{
|
||||
lock (_cacheLock)
|
||||
{
|
||||
if (_itemCache.Count <= _cacheSize)
|
||||
return;
|
||||
|
||||
// 按访问时间排序,移除最久未访问的项目
|
||||
var itemsToRemove = _cacheTimestamps
|
||||
.OrderBy(kvp => kvp.Value)
|
||||
.Take(_itemCache.Count - _cacheSize)
|
||||
.Select(kvp => kvp.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var index in itemsToRemove)
|
||||
{
|
||||
_itemCache.Remove(index);
|
||||
_cacheTimestamps.Remove(index);
|
||||
}
|
||||
|
||||
LogManager.Debug($"缓存清理完成: 移除{itemsToRemove.Count}个项目");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有缓存
|
||||
/// </summary>
|
||||
private void InvalidateCache()
|
||||
{
|
||||
lock (_cacheLock)
|
||||
{
|
||||
_itemCache.Clear();
|
||||
_cacheTimestamps.Clear();
|
||||
CacheHitRatio = 0;
|
||||
}
|
||||
|
||||
LogManager.Debug("缓存已清空");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新缓存命中率
|
||||
/// </summary>
|
||||
/// <param name="isHit">是否命中缓存</param>
|
||||
private void UpdateCacheHitRatio(bool isHit)
|
||||
{
|
||||
// 使用简单的移动平均计算命中率
|
||||
const double alpha = 0.1; // 平滑因子
|
||||
var hitValue = isHit ? 1.0 : 0.0;
|
||||
CacheHitRatio = CacheHitRatio * (1 - alpha) + hitValue * alpha;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 集合操作实现
|
||||
|
||||
/// <summary>
|
||||
/// 添加项目到集合末尾
|
||||
/// </summary>
|
||||
/// <param name="item">要添加的项目</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
InsertAsync(Count, item).Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步添加项目
|
||||
/// </summary>
|
||||
/// <param name="item">要添加的项目</param>
|
||||
public async Task AddAsync(T item)
|
||||
{
|
||||
await InsertAsync(Count, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置插入项目
|
||||
/// </summary>
|
||||
/// <param name="index">插入位置</param>
|
||||
/// <param name="item">要插入的项目</param>
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
InsertAsync(index, item).Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步在指定位置插入项目
|
||||
/// </summary>
|
||||
/// <param name="index">插入位置</param>
|
||||
/// <param name="item">要插入的项目</param>
|
||||
public async Task InsertAsync(int index, T item)
|
||||
{
|
||||
if (index < 0 || index > Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
using (_performanceMonitor.BeginCollectionUpdate(typeof(VirtualizedObservableCollection<T>), "Insert", 1))
|
||||
{
|
||||
// 在数据提供器中插入
|
||||
await _dataProvider.InsertAsync(index, item);
|
||||
|
||||
// 更新总数量
|
||||
Count++;
|
||||
|
||||
// 调整缓存索引
|
||||
lock (_cacheLock)
|
||||
{
|
||||
var itemsToReindex = _itemCache.Where(kvp => kvp.Key >= index).ToList();
|
||||
foreach (var kvp in itemsToReindex)
|
||||
{
|
||||
_itemCache.Remove(kvp.Key);
|
||||
_itemCache[kvp.Key + 1] = kvp.Value;
|
||||
|
||||
if (_cacheTimestamps.TryGetValue(kvp.Key, out var timestamp))
|
||||
{
|
||||
_cacheTimestamps.Remove(kvp.Key);
|
||||
_cacheTimestamps[kvp.Key + 1] = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存新插入的项目
|
||||
_itemCache[index] = item;
|
||||
_cacheTimestamps[index] = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// 触发变更通知
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
|
||||
NotifyCollectionChangedAction.Add, item, index));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定项目
|
||||
/// </summary>
|
||||
/// <param name="item">要移除的项目</param>
|
||||
/// <returns>是否成功移除</returns>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
var index = IndexOf(item);
|
||||
if (index >= 0)
|
||||
{
|
||||
RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定位置的项目
|
||||
/// </summary>
|
||||
/// <param name="index">要移除的项目位置</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
RemoveAtAsync(index).Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步移除指定位置的项目
|
||||
/// </summary>
|
||||
/// <param name="index">要移除的项目位置</param>
|
||||
public async Task RemoveAtAsync(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
using (_performanceMonitor.BeginCollectionUpdate(typeof(VirtualizedObservableCollection<T>), "RemoveAt", 1))
|
||||
{
|
||||
// 获取要移除的项目
|
||||
var removedItem = await GetItemAsync(index);
|
||||
|
||||
// 从数据提供器中移除
|
||||
await _dataProvider.RemoveAtAsync(index);
|
||||
|
||||
// 更新总数量
|
||||
Count--;
|
||||
|
||||
// 调整缓存索引
|
||||
lock (_cacheLock)
|
||||
{
|
||||
_itemCache.Remove(index);
|
||||
_cacheTimestamps.Remove(index);
|
||||
|
||||
var itemsToReindex = _itemCache.Where(kvp => kvp.Key > index).ToList();
|
||||
foreach (var kvp in itemsToReindex)
|
||||
{
|
||||
_itemCache.Remove(kvp.Key);
|
||||
_itemCache[kvp.Key - 1] = kvp.Value;
|
||||
|
||||
if (_cacheTimestamps.TryGetValue(kvp.Key, out var timestamp))
|
||||
{
|
||||
_cacheTimestamps.Remove(kvp.Key);
|
||||
_cacheTimestamps[kvp.Key - 1] = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 触发变更通知
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
|
||||
NotifyCollectionChangedAction.Remove, removedItem, index));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空集合
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
ClearAsync().Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步清空集合
|
||||
/// </summary>
|
||||
public async Task ClearAsync()
|
||||
{
|
||||
using (_performanceMonitor.BeginCollectionUpdate(typeof(VirtualizedObservableCollection<T>), "Clear", Count))
|
||||
{
|
||||
// 清空数据提供器
|
||||
await _dataProvider.ClearAsync();
|
||||
|
||||
// 更新状态
|
||||
Count = 0;
|
||||
InvalidateCache();
|
||||
|
||||
// 触发变更通知
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查集合是否包含指定项目
|
||||
/// </summary>
|
||||
/// <param name="item">要检查的项目</param>
|
||||
/// <returns>是否包含</returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return IndexOf(item) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找项目的索引
|
||||
/// </summary>
|
||||
/// <param name="item">要查找的项目</param>
|
||||
/// <returns>项目索引,如果不存在返回-1</returns>
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
// 首先在缓存中查找
|
||||
lock (_cacheLock)
|
||||
{
|
||||
foreach (var kvp in _itemCache)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(kvp.Value, item))
|
||||
{
|
||||
return kvp.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果缓存中没有,使用数据提供器查找
|
||||
return _dataProvider.IndexOfAsync(item).Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 复制集合项目到数组
|
||||
/// </summary>
|
||||
/// <param name="array">目标数组</param>
|
||||
/// <param name="arrayIndex">起始索引</param>
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
|
||||
if (arrayIndex < 0 || arrayIndex + Count > array.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
array[arrayIndex + i] = this[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举器实现
|
||||
|
||||
/// <summary>
|
||||
/// 获取枚举器
|
||||
/// </summary>
|
||||
/// <returns>枚举器</returns>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return new VirtualizedEnumerator(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取非泛型枚举器
|
||||
/// </summary>
|
||||
/// <returns>枚举器</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 虚拟化枚举器 - 按需加载数据
|
||||
/// </summary>
|
||||
private class VirtualizedEnumerator : IEnumerator<T>
|
||||
{
|
||||
private readonly VirtualizedObservableCollection<T> _collection;
|
||||
private int _currentIndex = -1;
|
||||
|
||||
public VirtualizedEnumerator(VirtualizedObservableCollection<T> collection)
|
||||
{
|
||||
_collection = collection;
|
||||
}
|
||||
|
||||
public T Current { get; private set; }
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_currentIndex + 1 < _collection.Count)
|
||||
{
|
||||
_currentIndex++;
|
||||
Current = _collection.GetItemAsync(_currentIndex).Result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_currentIndex = -1;
|
||||
Current = default(T);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Current = default(T);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件处理
|
||||
|
||||
/// <summary>
|
||||
/// 初始化总数量
|
||||
/// </summary>
|
||||
private async void InitializeTotalCountAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var totalCount = await _dataProvider.GetTotalCountAsync();
|
||||
Count = totalCount;
|
||||
LogManager.Info($"虚拟化集合初始化完成,总数量: {totalCount}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取总数量失败: {ex.Message}");
|
||||
Count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据提供器属性变更处理
|
||||
/// </summary>
|
||||
private async void DataProvider_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "TotalCount")
|
||||
{
|
||||
try
|
||||
{
|
||||
var newCount = await _dataProvider.GetTotalCountAsync();
|
||||
if (newCount != Count)
|
||||
{
|
||||
Count = newCount;
|
||||
InvalidateCache();
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新总数量失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发集合变更事件
|
||||
/// </summary>
|
||||
/// <param name="e">事件参数</param>
|
||||
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发属性变更事件
|
||||
/// </summary>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 诊断和统计
|
||||
|
||||
/// <summary>
|
||||
/// 获取虚拟化统计信息
|
||||
/// </summary>
|
||||
/// <returns>统计信息</returns>
|
||||
public VirtualizationStatistics GetStatistics()
|
||||
{
|
||||
lock (_cacheLock)
|
||||
{
|
||||
return new VirtualizationStatistics
|
||||
{
|
||||
TotalCount = Count,
|
||||
CachedItemsCount = _itemCache.Count,
|
||||
CacheHitRatio = CacheHitRatio,
|
||||
PageSize = PageSize,
|
||||
CacheSize = CacheSize,
|
||||
PreloadDistance = PreloadDistance,
|
||||
ViewportStart = ViewportStart,
|
||||
ViewportSize = ViewportSize,
|
||||
IsLoading = IsLoading
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成诊断报告
|
||||
/// </summary>
|
||||
/// <returns>诊断报告</returns>
|
||||
public string GenerateDiagnosticReport()
|
||||
{
|
||||
var stats = GetStatistics();
|
||||
var report = new System.Text.StringBuilder();
|
||||
|
||||
report.AppendLine("=== 虚拟化集合诊断报告 ===");
|
||||
report.AppendLine($"集合类型: {typeof(T).Name}");
|
||||
report.AppendLine($"总项目数: {stats.TotalCount:N0}");
|
||||
report.AppendLine($"缓存项目数: {stats.CachedItemsCount:N0}");
|
||||
report.AppendLine($"缓存命中率: {stats.CacheHitRatio:P2}");
|
||||
report.AppendLine($"页面大小: {stats.PageSize}");
|
||||
report.AppendLine($"缓存大小: {stats.CacheSize}");
|
||||
report.AppendLine($"预加载距离: {stats.PreloadDistance}");
|
||||
report.AppendLine($"视口位置: {stats.ViewportStart}-{stats.ViewportStart + stats.ViewportSize}");
|
||||
report.AppendLine($"加载状态: {(stats.IsLoading ? "加载中" : "空闲")}");
|
||||
|
||||
var memoryEfficiency = stats.TotalCount > 0 ? (double)stats.CachedItemsCount / stats.TotalCount : 0;
|
||||
report.AppendLine($"内存效率: {memoryEfficiency:P2} ({stats.CachedItemsCount}/{stats.TotalCount})");
|
||||
|
||||
return report.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 虚拟数据提供器接口
|
||||
|
||||
/// <summary>
|
||||
/// 虚拟数据提供器接口 - 为虚拟化集合提供数据访问抽象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型</typeparam>
|
||||
public interface IVirtualDataProvider<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取总项目数量
|
||||
/// </summary>
|
||||
/// <returns>总数量</returns>
|
||||
Task<int> GetTotalCountAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定索引的项目
|
||||
/// </summary>
|
||||
/// <param name="index">索引</param>
|
||||
/// <returns>项目</returns>
|
||||
Task<T> GetItemAsync(int index);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定范围的项目
|
||||
/// </summary>
|
||||
/// <param name="startIndex">起始索引</param>
|
||||
/// <param name="count">数量</param>
|
||||
/// <returns>项目列表</returns>
|
||||
Task<IEnumerable<T>> GetRangeAsync(int startIndex, int count);
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定索引的项目
|
||||
/// </summary>
|
||||
/// <param name="index">索引</param>
|
||||
/// <param name="item">新项目</param>
|
||||
Task SetItemAsync(int index, T item);
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置插入项目
|
||||
/// </summary>
|
||||
/// <param name="index">插入位置</param>
|
||||
/// <param name="item">要插入的项目</param>
|
||||
Task InsertAsync(int index, T item);
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定位置的项目
|
||||
/// </summary>
|
||||
/// <param name="index">位置</param>
|
||||
Task RemoveAtAsync(int index);
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有项目
|
||||
/// </summary>
|
||||
Task ClearAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 查找项目索引
|
||||
/// </summary>
|
||||
/// <param name="item">要查找的项目</param>
|
||||
/// <returns>索引,如果不存在返回-1</returns>
|
||||
Task<int> IndexOfAsync(T item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 虚拟化统计信息
|
||||
/// </summary>
|
||||
public class VirtualizationStatistics
|
||||
{
|
||||
public int TotalCount { get; set; }
|
||||
public int CachedItemsCount { get; set; }
|
||||
public double CacheHitRatio { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int CacheSize { get; set; }
|
||||
public int PreloadDistance { get; set; }
|
||||
public int ViewportStart { get; set; }
|
||||
public int ViewportSize { get; set; }
|
||||
public bool IsLoading { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,964 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Data;
|
||||
using NavisworksTransport.UI.WPF.ViewModels;
|
||||
using NavisworksTransport.UI.WPF.Collections;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据绑定最佳实践指南和验证工具
|
||||
/// 提供数据绑定性能优化的最佳实践指导和自动化验证
|
||||
/// </summary>
|
||||
public static class DataBindingBestPractices
|
||||
{
|
||||
#region 最佳实践规则定义
|
||||
|
||||
/// <summary>
|
||||
/// 最佳实践规则集合
|
||||
/// </summary>
|
||||
public static readonly List<BestPracticeRule> Rules = new List<BestPracticeRule>
|
||||
{
|
||||
// ViewModel相关规则
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "VM01",
|
||||
Category = BestPracticeCategory.ViewModel,
|
||||
Title = "继承ViewModelBase基类",
|
||||
Description = "所有ViewModel应继承ViewModelBase以获得线程安全和性能优化功能",
|
||||
Severity = RuleSeverity.Error,
|
||||
CheckAction = CheckViewModelBaseInheritance
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "VM02",
|
||||
Category = BestPracticeCategory.ViewModel,
|
||||
Title = "使用智能属性设置方法",
|
||||
Description = "使用SetPropertySmart方法替代传统SetProperty以获得更好的性能",
|
||||
Severity = RuleSeverity.Warning,
|
||||
CheckAction = CheckSmartPropertyUsage
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "VM03",
|
||||
Category = BestPracticeCategory.ViewModel,
|
||||
Title = "启用性能监控",
|
||||
Description = "在开发和测试阶段启用性能监控以识别性能瓶颈",
|
||||
Severity = RuleSeverity.Information,
|
||||
CheckAction = CheckPerformanceMonitoring
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "VM04",
|
||||
Category = BestPracticeCategory.ViewModel,
|
||||
Title = "避免频繁属性更新",
|
||||
Description = "对于频繁更新的属性,使用延迟更新或批量更新机制",
|
||||
Severity = RuleSeverity.Warning,
|
||||
CheckAction = CheckFrequentPropertyUpdates
|
||||
},
|
||||
|
||||
// 集合相关规则
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "COL01",
|
||||
Category = BestPracticeCategory.Collections,
|
||||
Title = "使用线程安全集合",
|
||||
Description = "在多线程环境中使用ThreadSafeObservableCollection而不是ObservableCollection",
|
||||
Severity = RuleSeverity.Error,
|
||||
CheckAction = CheckThreadSafeCollections
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "COL02",
|
||||
Category = BestPracticeCategory.Collections,
|
||||
Title = "大集合使用虚拟化",
|
||||
Description = "超过1000项的集合应使用VirtualizedObservableCollection",
|
||||
Severity = RuleSeverity.Warning,
|
||||
CheckAction = CheckVirtualizationUsage
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "COL03",
|
||||
Category = BestPracticeCategory.Collections,
|
||||
Title = "使用批量操作",
|
||||
Description = "集合的批量添加/删除应使用AddRange/RemoveRange方法",
|
||||
Severity = RuleSeverity.Information,
|
||||
CheckAction = CheckBatchOperations
|
||||
},
|
||||
|
||||
// 绑定相关规则
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "BIND01",
|
||||
Category = BestPracticeCategory.Binding,
|
||||
Title = "优化绑定表达式",
|
||||
Description = "复杂的绑定表达式应注册到BindingExpressionOptimizer",
|
||||
Severity = RuleSeverity.Warning,
|
||||
CheckAction = CheckBindingOptimization
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "BIND02",
|
||||
Category = BestPracticeCategory.Binding,
|
||||
Title = "避免双向绑定过度使用",
|
||||
Description = "只在必要时使用双向绑定,优先使用单向绑定",
|
||||
Severity = RuleSeverity.Information,
|
||||
CheckAction = CheckTwoWayBindingUsage
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "BIND03",
|
||||
Category = BestPracticeCategory.Binding,
|
||||
Title = "使用绑定缓存",
|
||||
Description = "重复计算的绑定值应启用缓存机制",
|
||||
Severity = RuleSeverity.Information,
|
||||
CheckAction = CheckBindingCaching
|
||||
},
|
||||
|
||||
// 同步相关规则
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "SYNC01",
|
||||
Category = BestPracticeCategory.Synchronization,
|
||||
Title = "注册跨ViewModel同步",
|
||||
Description = "需要数据同步的ViewModel应注册到CrossViewModelSynchronizer",
|
||||
Severity = RuleSeverity.Warning,
|
||||
CheckAction = CheckCrossViewModelSync
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "SYNC02",
|
||||
Category = BestPracticeCategory.Synchronization,
|
||||
Title = "避免循环依赖",
|
||||
Description = "检查并避免ViewModel之间的循环依赖关系",
|
||||
Severity = RuleSeverity.Error,
|
||||
CheckAction = CheckCircularDependencies
|
||||
},
|
||||
|
||||
// 内存相关规则
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "MEM01",
|
||||
Category = BestPracticeCategory.Memory,
|
||||
Title = "及时清理事件订阅",
|
||||
Description = "ViewModel释放时应正确清理事件订阅以避免内存泄漏",
|
||||
Severity = RuleSeverity.Error,
|
||||
CheckAction = CheckEventSubscriptionCleanup
|
||||
},
|
||||
|
||||
new BestPracticeRule
|
||||
{
|
||||
Id = "MEM02",
|
||||
Category = BestPracticeCategory.Memory,
|
||||
Title = "控制缓存大小",
|
||||
Description = "各种缓存的大小应根据应用需求进行合理配置",
|
||||
Severity = RuleSeverity.Information,
|
||||
CheckAction = CheckCacheConfiguration
|
||||
}
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region 规则检查实现
|
||||
|
||||
/// <summary>
|
||||
/// 检查ViewModel是否继承ViewModelBase
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckViewModelBaseInheritance(object context)
|
||||
{
|
||||
if (context is ViewModelBase)
|
||||
{
|
||||
return null; // 合规
|
||||
}
|
||||
|
||||
if (context is INotifyPropertyChanged viewModel)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "VM01",
|
||||
Message = $"ViewModel {viewModel.GetType().Name} 应继承 ViewModelBase 基类",
|
||||
Recommendation = "修改类定义:public class MyViewModel : ViewModelBase",
|
||||
Context = context
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否使用智能属性设置
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckSmartPropertyUsage(object context)
|
||||
{
|
||||
// 这里可以通过反射或代码分析来检查
|
||||
// 为了简化,返回建议性信息
|
||||
if (context is ViewModelBase viewModel)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "VM02",
|
||||
Message = "建议使用 SetPropertySmart 方法以获得更好的性能",
|
||||
Recommendation = "使用 SetPropertySmart(ref _field, value, PropertyUpdateMode.Delayed)",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查性能监控是否启用
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckPerformanceMonitoring(object context)
|
||||
{
|
||||
var monitor = DataBindingPerformanceMonitor.Instance;
|
||||
if (!monitor.IsMonitoringEnabled)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "VM03",
|
||||
Message = "性能监控未启用,建议在开发阶段启用以识别性能问题",
|
||||
Recommendation = "调用 DataBindingPerformanceMonitor.Instance.IsMonitoringEnabled = true",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查频繁属性更新
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckFrequentPropertyUpdates(object context)
|
||||
{
|
||||
// 通过性能监控数据检查
|
||||
var monitor = DataBindingPerformanceMonitor.Instance;
|
||||
var overview = monitor.GetPerformanceOverview();
|
||||
|
||||
if (overview.TotalSlowUpdates > overview.TotalPropertyUpdates * 0.1) // 超过10%的慢更新
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "VM04",
|
||||
Message = $"检测到较多的慢属性更新 ({overview.TotalSlowUpdates} / {overview.TotalPropertyUpdates})",
|
||||
Recommendation = "考虑使用延迟更新或批量更新机制",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Warning
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查线程安全集合使用
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckThreadSafeCollections(object context)
|
||||
{
|
||||
if (context is System.Collections.ObjectModel.ObservableCollection<object> collection &&
|
||||
!(collection is ThreadSafeObservableCollection<object>))
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "COL01",
|
||||
Message = "在多线程环境中应使用 ThreadSafeObservableCollection",
|
||||
Recommendation = "替换为: new ThreadSafeObservableCollection<T>()",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Error
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查虚拟化集合使用
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckVirtualizationUsage(object context)
|
||||
{
|
||||
if (context is System.Collections.ICollection collection && collection.Count > 1000)
|
||||
{
|
||||
if (!(collection is VirtualizedObservableCollection<object>))
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "COL02",
|
||||
Message = $"大集合 ({collection.Count} 项) 建议使用虚拟化",
|
||||
Recommendation = "使用 VirtualizedObservableCollection<T> 以提高性能",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Warning
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查批量操作使用
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckBatchOperations(object context)
|
||||
{
|
||||
// 这里可以通过方法调用分析来检查
|
||||
// 为简化,返回建议信息
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "COL03",
|
||||
Message = "建议使用批量操作方法提高集合操作性能",
|
||||
Recommendation = "使用 AddRange() / RemoveRange() 而不是循环调用 Add() / Remove()",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查绑定优化
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckBindingOptimization(object context)
|
||||
{
|
||||
var optimizer = BindingExpressionOptimizer.Instance;
|
||||
var stats = optimizer.GetStatistics();
|
||||
|
||||
if (stats.RegisteredBindingsCount == 0)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "BIND01",
|
||||
Message = "没有注册绑定优化,复杂绑定可能存在性能问题",
|
||||
Recommendation = "使用 BindingExpressionOptimizer.OptimizeBinding() 注册复杂绑定",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查双向绑定使用
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckTwoWayBindingUsage(object context)
|
||||
{
|
||||
// 静态分析检查,这里返回建议
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "BIND02",
|
||||
Message = "检查双向绑定的必要性",
|
||||
Recommendation = "只在必要时使用 Mode=TwoWay,优先使用 Mode=OneWay",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查绑定缓存
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckBindingCaching(object context)
|
||||
{
|
||||
var optimizer = BindingExpressionOptimizer.Instance;
|
||||
if (!optimizer.EnableBindingCaching)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "BIND03",
|
||||
Message = "绑定缓存未启用",
|
||||
Recommendation = "启用绑定缓存: BindingExpressionOptimizer.Instance.EnableBindingCaching = true",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查跨ViewModel同步
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckCrossViewModelSync(object context)
|
||||
{
|
||||
var synchronizer = CrossViewModelSynchronizer.Instance;
|
||||
var stats = synchronizer.GetStatistics();
|
||||
|
||||
if (stats.RegisteredViewModelsCount == 0)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "SYNC01",
|
||||
Message = "没有注册ViewModel同步,可能存在数据一致性问题",
|
||||
Recommendation = "使用 CrossViewModelSynchronizer.RegisterViewModel() 注册需要同步的ViewModel",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查循环依赖
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckCircularDependencies(object context)
|
||||
{
|
||||
if (context is ViewModelBase viewModel)
|
||||
{
|
||||
var synchronizer = CrossViewModelSynchronizer.Instance;
|
||||
|
||||
// 检查是否存在循环依赖(简化检查)
|
||||
try
|
||||
{
|
||||
// 这里可以实现更复杂的循环依赖检测逻辑
|
||||
var dependencies = synchronizer.GetPropertyDependencies(viewModel, "*");
|
||||
if (dependencies.Any())
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "SYNC02",
|
||||
Message = "检测到可能的循环依赖",
|
||||
Recommendation = "检查ViewModel之间的依赖关系,避免循环引用",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Warning
|
||||
};
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 检查失败,忽略
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查事件订阅清理
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckEventSubscriptionCleanup(object context)
|
||||
{
|
||||
if (context is IDisposable)
|
||||
{
|
||||
return null; // 实现了IDisposable,假设会正确清理
|
||||
}
|
||||
|
||||
if (context is ViewModelBase)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "MEM01",
|
||||
Message = "ViewModel应实现IDisposable以正确清理资源",
|
||||
Recommendation = "实现IDisposable接口并在Dispose中清理事件订阅",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Warning
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查缓存配置
|
||||
/// </summary>
|
||||
private static BestPracticeViolation CheckCacheConfiguration(object context)
|
||||
{
|
||||
var monitor = DataBindingPerformanceMonitor.Instance;
|
||||
var bindingOptimizer = BindingExpressionOptimizer.Instance;
|
||||
|
||||
if (bindingOptimizer.MaxCacheSize < 100)
|
||||
{
|
||||
return new BestPracticeViolation
|
||||
{
|
||||
RuleId = "MEM02",
|
||||
Message = "绑定缓存大小可能过小",
|
||||
Recommendation = "考虑增加缓存大小以提高性能",
|
||||
Context = context,
|
||||
Severity = RuleSeverity.Information
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 验证和报告
|
||||
|
||||
/// <summary>
|
||||
/// 验证对象的数据绑定最佳实践合规性
|
||||
/// </summary>
|
||||
/// <param name="context">要验证的对象</param>
|
||||
/// <param name="categories">要检查的规则类别</param>
|
||||
/// <returns>违规列表</returns>
|
||||
public static List<BestPracticeViolation> ValidateObject(object context, params BestPracticeCategory[] categories)
|
||||
{
|
||||
var violations = new List<BestPracticeViolation>();
|
||||
|
||||
if (context == null)
|
||||
return violations;
|
||||
|
||||
var categoriesToCheck = categories?.Length > 0 ? categories : (BestPracticeCategory[])Enum.GetValues(typeof(BestPracticeCategory));
|
||||
|
||||
foreach (var rule in Rules)
|
||||
{
|
||||
if (System.Linq.Enumerable.Contains(categoriesToCheck, rule.Category))
|
||||
{
|
||||
try
|
||||
{
|
||||
var violation = rule.CheckAction?.Invoke(context);
|
||||
if (violation != null)
|
||||
{
|
||||
violations.Add(violation);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"规则检查失败 {rule.Id}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证ViewModel的数据绑定最佳实践
|
||||
/// </summary>
|
||||
/// <param name="viewModel">要验证的ViewModel</param>
|
||||
/// <returns>验证报告</returns>
|
||||
public static BestPracticeReport ValidateViewModel(ViewModelBase viewModel)
|
||||
{
|
||||
var violations = ValidateObject(viewModel);
|
||||
|
||||
return new BestPracticeReport
|
||||
{
|
||||
TargetObject = viewModel,
|
||||
TargetType = viewModel.GetType(),
|
||||
ValidationTime = DateTime.UtcNow,
|
||||
Violations = violations,
|
||||
OverallScore = CalculateScore(violations)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证整个应用的数据绑定最佳实践
|
||||
/// </summary>
|
||||
/// <param name="viewModels">要验证的ViewModel集合</param>
|
||||
/// <returns>应用验证报告</returns>
|
||||
public static ApplicationBestPracticeReport ValidateApplication(IEnumerable<ViewModelBase> viewModels)
|
||||
{
|
||||
var reports = new List<BestPracticeReport>();
|
||||
var allViolations = new List<BestPracticeViolation>();
|
||||
|
||||
foreach (var viewModel in viewModels)
|
||||
{
|
||||
var report = ValidateViewModel(viewModel);
|
||||
reports.Add(report);
|
||||
allViolations.AddRange(report.Violations);
|
||||
}
|
||||
|
||||
// 添加系统级检查
|
||||
var systemViolations = ValidateSystemConfiguration();
|
||||
allViolations.AddRange(systemViolations);
|
||||
|
||||
return new ApplicationBestPracticeReport
|
||||
{
|
||||
ValidationTime = DateTime.UtcNow,
|
||||
ViewModelReports = reports,
|
||||
SystemViolations = systemViolations,
|
||||
AllViolations = allViolations,
|
||||
OverallScore = CalculateScore(allViolations),
|
||||
Summary = GenerateViolationSummary(allViolations)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证系统配置
|
||||
/// </summary>
|
||||
/// <returns>系统级违规列表</returns>
|
||||
private static List<BestPracticeViolation> ValidateSystemConfiguration()
|
||||
{
|
||||
var violations = new List<BestPracticeViolation>();
|
||||
|
||||
// 检查性能监控
|
||||
violations.AddRange(ValidateObject("system_performance"));
|
||||
|
||||
// 检查绑定优化器
|
||||
violations.AddRange(ValidateObject("system_binding"));
|
||||
|
||||
// 检查同步器
|
||||
violations.AddRange(ValidateObject("system_sync"));
|
||||
|
||||
return violations.Where(v => v != null).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算合规性评分
|
||||
/// </summary>
|
||||
/// <param name="violations">违规列表</param>
|
||||
/// <returns>评分 (0-100)</returns>
|
||||
private static int CalculateScore(List<BestPracticeViolation> violations)
|
||||
{
|
||||
if (violations.Count == 0)
|
||||
return 100;
|
||||
|
||||
var errorCount = violations.Count(v => v.Severity == RuleSeverity.Error);
|
||||
var warningCount = violations.Count(v => v.Severity == RuleSeverity.Warning);
|
||||
var infoCount = violations.Count(v => v.Severity == RuleSeverity.Information);
|
||||
|
||||
// 错误-30分,警告-10分,信息-2分
|
||||
var deduction = errorCount * 30 + warningCount * 10 + infoCount * 2;
|
||||
|
||||
return Math.Max(0, 100 - deduction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成违规汇总
|
||||
/// </summary>
|
||||
/// <param name="violations">违规列表</param>
|
||||
/// <returns>汇总信息</returns>
|
||||
private static ViolationSummary GenerateViolationSummary(List<BestPracticeViolation> violations)
|
||||
{
|
||||
var summary = new ViolationSummary();
|
||||
|
||||
foreach (BestPracticeCategory category in Enum.GetValues(typeof(BestPracticeCategory)))
|
||||
{
|
||||
var categoryViolations = violations.Where(v =>
|
||||
Rules.FirstOrDefault(r => r.Id == v.RuleId)?.Category == (BestPracticeCategory)category).ToList();
|
||||
|
||||
summary.ByCategory[category] = new CategorySummary
|
||||
{
|
||||
TotalCount = categoryViolations.Count,
|
||||
ErrorCount = categoryViolations.Count(v => v.Severity == RuleSeverity.Error),
|
||||
WarningCount = categoryViolations.Count(v => v.Severity == RuleSeverity.Warning),
|
||||
InfoCount = categoryViolations.Count(v => v.Severity == RuleSeverity.Information)
|
||||
};
|
||||
}
|
||||
|
||||
summary.TotalViolations = violations.Count;
|
||||
summary.MostCommonViolations = violations
|
||||
.GroupBy(v => v.RuleId)
|
||||
.OrderByDescending(g => g.Count())
|
||||
.Take(5)
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成最佳实践报告文档
|
||||
/// </summary>
|
||||
/// <param name="report">验证报告</param>
|
||||
/// <returns>报告文档</returns>
|
||||
public static string GenerateReportDocument(ApplicationBestPracticeReport report)
|
||||
{
|
||||
var doc = new StringBuilder();
|
||||
|
||||
doc.AppendLine("# 数据绑定最佳实践验证报告");
|
||||
doc.AppendLine($"生成时间: {report.ValidationTime:yyyy-MM-dd HH:mm:ss}");
|
||||
doc.AppendLine($"总体评分: {report.OverallScore}/100");
|
||||
doc.AppendLine();
|
||||
|
||||
// 执行摘要
|
||||
doc.AppendLine("## 执行摘要");
|
||||
doc.AppendLine($"- 验证的ViewModel数量: {report.ViewModelReports.Count}");
|
||||
doc.AppendLine($"- 发现的问题总数: {report.AllViolations.Count}");
|
||||
doc.AppendLine($"- 错误: {report.Summary.ByCategory.Values.Sum(c => c.ErrorCount)}");
|
||||
doc.AppendLine($"- 警告: {report.Summary.ByCategory.Values.Sum(c => c.WarningCount)}");
|
||||
doc.AppendLine($"- 信息: {report.Summary.ByCategory.Values.Sum(c => c.InfoCount)}");
|
||||
doc.AppendLine();
|
||||
|
||||
// 按类别分组的问题
|
||||
doc.AppendLine("## 问题分类统计");
|
||||
foreach (var kvp in report.Summary.ByCategory)
|
||||
{
|
||||
if (kvp.Value.TotalCount > 0)
|
||||
{
|
||||
doc.AppendLine($"### {kvp.Key}");
|
||||
doc.AppendLine($"- 总计: {kvp.Value.TotalCount}");
|
||||
doc.AppendLine($"- 错误: {kvp.Value.ErrorCount}");
|
||||
doc.AppendLine($"- 警告: {kvp.Value.WarningCount}");
|
||||
doc.AppendLine($"- 信息: {kvp.Value.InfoCount}");
|
||||
doc.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
// 最常见问题
|
||||
doc.AppendLine("## 最常见问题");
|
||||
foreach (var kvp in report.Summary.MostCommonViolations)
|
||||
{
|
||||
var rule = Rules.FirstOrDefault(r => r.Id == kvp.Key);
|
||||
if (rule != null)
|
||||
{
|
||||
doc.AppendLine($"- **{rule.Title}** ({kvp.Key}): {kvp.Value}次");
|
||||
doc.AppendLine($" {rule.Description}");
|
||||
doc.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
// 详细问题列表
|
||||
doc.AppendLine("## 详细问题列表");
|
||||
var violationsByViewModel = report.ViewModelReports
|
||||
.Where(r => r.Violations.Count > 0)
|
||||
.OrderByDescending(r => r.Violations.Count);
|
||||
|
||||
foreach (var vmReport in violationsByViewModel)
|
||||
{
|
||||
doc.AppendLine($"### {vmReport.TargetType.Name}");
|
||||
doc.AppendLine($"评分: {vmReport.OverallScore}/100");
|
||||
doc.AppendLine();
|
||||
|
||||
foreach (var violation in vmReport.Violations.OrderByDescending(v => v.Severity))
|
||||
{
|
||||
var severityIcon = "";
|
||||
switch (violation.Severity)
|
||||
{
|
||||
case RuleSeverity.Error:
|
||||
severityIcon = "❌";
|
||||
break;
|
||||
case RuleSeverity.Warning:
|
||||
severityIcon = "⚠️";
|
||||
break;
|
||||
case RuleSeverity.Information:
|
||||
severityIcon = "ℹ️";
|
||||
break;
|
||||
default:
|
||||
severityIcon = "•";
|
||||
break;
|
||||
}
|
||||
|
||||
doc.AppendLine($"{severityIcon} **{violation.RuleId}**: {violation.Message}");
|
||||
doc.AppendLine($" 💡 建议: {violation.Recommendation}");
|
||||
doc.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
// 优化建议
|
||||
doc.AppendLine("## 优化建议");
|
||||
doc.AppendLine(GenerateOptimizationRecommendations(report));
|
||||
|
||||
return doc.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成优化建议
|
||||
/// </summary>
|
||||
/// <param name="report">验证报告</param>
|
||||
/// <returns>优化建议</returns>
|
||||
private static string GenerateOptimizationRecommendations(ApplicationBestPracticeReport report)
|
||||
{
|
||||
var recommendations = new StringBuilder();
|
||||
|
||||
// 基于违规情况生成针对性建议
|
||||
var errorCount = report.AllViolations.Count(v => v.Severity == RuleSeverity.Error);
|
||||
var warningCount = report.AllViolations.Count(v => v.Severity == RuleSeverity.Warning);
|
||||
|
||||
if (errorCount > 0)
|
||||
{
|
||||
recommendations.AppendLine("### 高优先级 (必须修复)");
|
||||
recommendations.AppendLine("1. 修复所有错误级别的问题,这些问题可能导致运行时错误或严重的性能问题");
|
||||
recommendations.AppendLine("2. 确保所有ViewModel继承ViewModelBase基类");
|
||||
recommendations.AppendLine("3. 使用线程安全的集合类型");
|
||||
recommendations.AppendLine();
|
||||
}
|
||||
|
||||
if (warningCount > 0)
|
||||
{
|
||||
recommendations.AppendLine("### 中等优先级 (建议修复)");
|
||||
recommendations.AppendLine("1. 解决警告级别的问题以提高应用性能");
|
||||
recommendations.AppendLine("2. 对大集合启用虚拟化");
|
||||
recommendations.AppendLine("3. 优化频繁更新的属性");
|
||||
recommendations.AppendLine();
|
||||
}
|
||||
|
||||
recommendations.AppendLine("### 一般建议");
|
||||
recommendations.AppendLine("1. 在开发阶段启用性能监控以及时发现问题");
|
||||
recommendations.AppendLine("2. 定期运行最佳实践验证");
|
||||
recommendations.AppendLine("3. 建立代码审查流程确保新代码符合最佳实践");
|
||||
recommendations.AppendLine("4. 为团队成员提供数据绑定最佳实践培训");
|
||||
|
||||
return recommendations.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 自动修复建议
|
||||
|
||||
/// <summary>
|
||||
/// 获取自动修复建议
|
||||
/// </summary>
|
||||
/// <param name="violation">违规项</param>
|
||||
/// <returns>修复建议</returns>
|
||||
public static AutoFixSuggestion GetAutoFixSuggestion(BestPracticeViolation violation)
|
||||
{
|
||||
switch (violation.RuleId)
|
||||
{
|
||||
case "VM01":
|
||||
return new AutoFixSuggestion
|
||||
{
|
||||
CanAutoFix = false,
|
||||
Description = "需要手动修改类继承关系",
|
||||
Steps = new[]
|
||||
{
|
||||
"1. 添加using NavisworksTransport.UI.WPF.ViewModels;",
|
||||
"2. 修改类定义: public class MyViewModel : ViewModelBase",
|
||||
"3. 移除手动实现的INotifyPropertyChanged相关代码"
|
||||
}
|
||||
};
|
||||
|
||||
case "COL01":
|
||||
return new AutoFixSuggestion
|
||||
{
|
||||
CanAutoFix = true,
|
||||
Description = "可以自动替换为线程安全集合",
|
||||
Steps = new[]
|
||||
{
|
||||
"1. 替换ObservableCollection<T>为ThreadSafeObservableCollection<T>",
|
||||
"2. 添加using NavisworksTransport.UI.WPF.Collections;"
|
||||
}
|
||||
};
|
||||
|
||||
case "COL02":
|
||||
return new AutoFixSuggestion
|
||||
{
|
||||
CanAutoFix = true,
|
||||
Description = "可以自动替换为虚拟化集合",
|
||||
Steps = new[]
|
||||
{
|
||||
"1. 替换为VirtualizedObservableCollection<T>",
|
||||
"2. 实现IVirtualDataProvider<T>接口"
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
return new AutoFixSuggestion
|
||||
{
|
||||
CanAutoFix = false,
|
||||
Description = "需要手动修复",
|
||||
Steps = new[] { violation.Recommendation }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 数据结构定义
|
||||
|
||||
/// <summary>
|
||||
/// 最佳实践规则
|
||||
/// </summary>
|
||||
public class BestPracticeRule
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public BestPracticeCategory Category { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public RuleSeverity Severity { get; set; }
|
||||
public Func<object, BestPracticeViolation> CheckAction { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最佳实践违规
|
||||
/// </summary>
|
||||
public class BestPracticeViolation
|
||||
{
|
||||
public string RuleId { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string Recommendation { get; set; }
|
||||
public RuleSeverity Severity { get; set; }
|
||||
public object Context { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最佳实践报告
|
||||
/// </summary>
|
||||
public class BestPracticeReport
|
||||
{
|
||||
public object TargetObject { get; set; }
|
||||
public Type TargetType { get; set; }
|
||||
public DateTime ValidationTime { get; set; }
|
||||
public List<BestPracticeViolation> Violations { get; set; } = new List<BestPracticeViolation>();
|
||||
public int OverallScore { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用最佳实践报告
|
||||
/// </summary>
|
||||
public class ApplicationBestPracticeReport
|
||||
{
|
||||
public DateTime ValidationTime { get; set; }
|
||||
public List<BestPracticeReport> ViewModelReports { get; set; } = new List<BestPracticeReport>();
|
||||
public List<BestPracticeViolation> SystemViolations { get; set; } = new List<BestPracticeViolation>();
|
||||
public List<BestPracticeViolation> AllViolations { get; set; } = new List<BestPracticeViolation>();
|
||||
public int OverallScore { get; set; }
|
||||
public ViolationSummary Summary { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 违规汇总
|
||||
/// </summary>
|
||||
public class ViolationSummary
|
||||
{
|
||||
public Dictionary<BestPracticeCategory, CategorySummary> ByCategory { get; set; } = new Dictionary<BestPracticeCategory, CategorySummary>();
|
||||
public int TotalViolations { get; set; }
|
||||
public Dictionary<string, int> MostCommonViolations { get; set; } = new Dictionary<string, int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 类别汇总
|
||||
/// </summary>
|
||||
public class CategorySummary
|
||||
{
|
||||
public int TotalCount { get; set; }
|
||||
public int ErrorCount { get; set; }
|
||||
public int WarningCount { get; set; }
|
||||
public int InfoCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动修复建议
|
||||
/// </summary>
|
||||
public class AutoFixSuggestion
|
||||
{
|
||||
public bool CanAutoFix { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string[] Steps { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最佳实践类别
|
||||
/// </summary>
|
||||
public enum BestPracticeCategory
|
||||
{
|
||||
ViewModel,
|
||||
Collections,
|
||||
Binding,
|
||||
Synchronization,
|
||||
Memory,
|
||||
Performance
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 规则严重性
|
||||
/// </summary>
|
||||
public enum RuleSeverity
|
||||
{
|
||||
Information = 0,
|
||||
Warning = 1,
|
||||
Error = 2
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user