第09章 - 异常处理体系
第09章 - 异常处理体系
9.1 异常体系概述
9.1.1 异常层次结构
OGU4Net定义了完整的异常类层次:
System.Exception
└── OguException (OGU基础异常)
├── DataSourceException (数据源异常)
├── FormatParseException (格式解析异常)
├── EngineNotSupportedException (引擎不支持异常)
├── LayerValidationException (图层验证异常)
└── TopologyException (拓扑异常)
9.1.2 异常类型说明
| 异常类型 | 使用场景 | 示例 |
|---|---|---|
| OguException | 基础异常,通用GIS错误 | 一般操作失败 |
| DataSourceException | 数据源访问问题 | 文件不存在、数据库连接失败 |
| FormatParseException | 数据格式解析问题 | WKT解析失败、JSON格式错误 |
| EngineNotSupportedException | 引擎不支持 | 驱动不可用、格式不支持 |
| LayerValidationException | 图层数据验证失败 | 字段重复、属性不匹配 |
| TopologyException | 拓扑操作失败 | 几何无效、拓扑规则违反 |
9.2 OguException基类
9.2.1 类定义
namespace OpenGIS.Utils.Exception;
/// <summary>
/// OGU基础异常类
/// </summary>
public class OguException : System.Exception
{
/// <summary>
/// 错误代码
/// </summary>
public int ErrorCode { get; set; }
/// <summary>
/// 上下文信息
/// </summary>
public Dictionary<string, object> Context { get; set; }
public OguException(string message) : base(message)
{
Context = new Dictionary<string, object>();
}
public OguException(string message, System.Exception innerException)
: base(message, innerException)
{
Context = new Dictionary<string, object>();
}
public OguException(string message, int errorCode) : base(message)
{
ErrorCode = errorCode;
Context = new Dictionary<string, object>();
}
}
9.2.2 使用上下文信息
try
{
// 某些操作
throw new OguException("操作失败", 1001)
{
Context =
{
["FilePath"] = "data.shp",
["Operation"] = "Read",
["Timestamp"] = DateTime.Now
}
};
}
catch (OguException ex)
{
Console.WriteLine($"错误: {ex.Message}");
Console.WriteLine($"错误码: {ex.ErrorCode}");
foreach (var (key, value) in ex.Context)
{
Console.WriteLine($" {key}: {value}");
}
}
9.3 具体异常类型
9.3.1 DataSourceException
数据源相关异常:
namespace OpenGIS.Utils.Exception;
public class DataSourceException : OguException
{
public DataSourceException(string message) : base(message) { }
public DataSourceException(string message, System.Exception innerException)
: base(message, innerException) { }
public DataSourceException(string message, int errorCode)
: base(message, errorCode) { }
}
使用场景:
// 打开数据源失败
if (dataSource == null)
{
throw new DataSourceException($"无法打开数据源: {path}")
{
Context = { ["Path"] = path }
};
}
// 连接数据库失败
try
{
// 连接PostGIS
}
catch (System.Exception ex)
{
throw new DataSourceException("PostGIS连接失败", ex)
{
Context =
{
["Host"] = host,
["Database"] = database
}
};
}
9.3.2 FormatParseException
格式解析异常:
namespace OpenGIS.Utils.Exception;
public class FormatParseException : OguException
{
public FormatParseException(string message) : base(message) { }
public FormatParseException(string message, System.Exception innerException)
: base(message, innerException) { }
public FormatParseException(string message, int errorCode)
: base(message, errorCode) { }
}
使用场景:
// WKT解析失败
try
{
var geom = Geometry.CreateFromWkt(wkt);
if (geom == null)
{
throw new FormatParseException($"无效的WKT: {wkt}");
}
}
catch (System.Exception ex) when (!(ex is FormatParseException))
{
throw new FormatParseException($"WKT解析失败: {wkt}", ex);
}
// JSON解析失败
try
{
var layer = JsonSerializer.Deserialize<OguLayer>(json);
}
catch (JsonException ex)
{
throw new FormatParseException("JSON格式无效", ex);
}
9.3.3 EngineNotSupportedException
引擎不支持异常:
namespace OpenGIS.Utils.Exception;
public class EngineNotSupportedException : OguException
{
public EngineNotSupportedException(string message) : base(message) { }
public EngineNotSupportedException(string message, System.Exception innerException)
: base(message, innerException) { }
public EngineNotSupportedException(string message, int errorCode)
: base(message, errorCode) { }
}
使用场景:
// 驱动不可用
if (!GdalConfiguration.IsDriverAvailable("FileGDB"))
{
throw new EngineNotSupportedException("FileGDB驱动不可用")
{
Context =
{
["Driver"] = "FileGDB",
["AvailableDrivers"] = string.Join(", ",
GdalConfiguration.GetSupportedDrivers().Take(10))
}
};
}
// 不支持的引擎类型
throw new EngineNotSupportedException($"不支持的引擎类型: {engineType}");
9.3.4 LayerValidationException
图层验证异常:
namespace OpenGIS.Utils.Exception;
public class LayerValidationException : OguException
{
public LayerValidationException(string message) : base(message) { }
public LayerValidationException(string message, System.Exception innerException)
: base(message, innerException) { }
public LayerValidationException(string message, int errorCode)
: base(message, errorCode) { }
}
使用场景:
// 图层名称为空
if (string.IsNullOrWhiteSpace(layer.Name))
{
throw new LayerValidationException("图层名称不能为空");
}
// 字段重复
if (Fields.Any(f => f.Name == field.Name))
{
throw new LayerValidationException($"字段'{field.Name}'已存在");
}
// 属性不匹配
foreach (var fieldName in feature.Attributes.Keys)
{
if (!fieldNameSet.Contains(fieldName))
{
throw new LayerValidationException(
$"要素包含未定义的属性'{fieldName}'");
}
}
9.3.5 TopologyException
拓扑异常:
namespace OpenGIS.Utils.Exception;
public class TopologyException : OguException
{
public TopologyException(string message) : base(message) { }
public TopologyException(string message, System.Exception innerException)
: base(message, innerException) { }
public TopologyException(string message, int errorCode)
: base(message, errorCode) { }
}
使用场景:
// 几何无效
var result = GeometryUtil.IsValid(geom);
if (!result.IsValid)
{
throw new TopologyException($"几何无效: {result.ErrorMessage}")
{
Context =
{
["ErrorType"] = result.ErrorType,
["WKT"] = GeometryUtil.Geometry2Wkt(geom)
}
};
}
// 拓扑操作失败
try
{
var intersection = geomA.Intersection(geomB);
}
catch (System.Exception ex)
{
throw new TopologyException("交集运算失败", ex);
}
9.4 异常处理模式
9.4.1 基本异常处理
try
{
var layer = OguLayerUtil.ReadLayer(DataFormatType.SHP, "data.shp");
ProcessLayer(layer);
}
catch (DataSourceException ex)
{
Console.Error.WriteLine($"数据源错误: {ex.Message}");
LogError(ex);
}
catch (FormatParseException ex)
{
Console.Error.WriteLine($"格式解析错误: {ex.Message}");
LogError(ex);
}
catch (OguException ex)
{
Console.Error.WriteLine($"GIS操作错误: {ex.Message}");
LogError(ex);
}
catch (System.Exception ex)
{
Console.Error.WriteLine($"未知错误: {ex.Message}");
LogError(ex);
}
9.4.2 分层异常处理
// 服务层
public OguLayer ReadLayerSafe(string path)
{
try
{
return OguLayerUtil.ReadLayer(DataFormatType.SHP, path);
}
catch (DataSourceException)
{
throw; // 直接传递
}
catch (System.Exception ex)
{
// 包装为更具体的异常
throw new DataSourceException($"读取图层失败: {path}", ex);
}
}
// 控制器层
public IActionResult GetLayer(string path)
{
try
{
var layer = ReadLayerSafe(path);
return Ok(layer);
}
catch (DataSourceException ex)
{
return NotFound(new { error = ex.Message });
}
catch (OguException ex)
{
return BadRequest(new { error = ex.Message });
}
}
9.4.3 使用异常过滤器
// C# 6+ 异常过滤器
try
{
ProcessData(data);
}
catch (OguException ex) when (ex.ErrorCode == 1001)
{
// 处理特定错误码
HandleSpecificError(ex);
}
catch (OguException ex) when (ex.Context.ContainsKey("Recoverable"))
{
// 可恢复的错误
Retry();
}
catch (OguException ex)
{
// 其他OGU异常
throw;
}
9.4.4 异常重试模式
public OguLayer ReadLayerWithRetry(string path, int maxRetries = 3)
{
int attempt = 0;
while (true)
{
try
{
return OguLayerUtil.ReadLayer(DataFormatType.SHP, path);
}
catch (DataSourceException ex) when (attempt < maxRetries)
{
attempt++;
Console.WriteLine($"读取失败,第{attempt}次重试...");
Thread.Sleep(1000 * attempt); // 递增延迟
}
catch (DataSourceException ex)
{
throw new DataSourceException(
$"读取失败,已重试{maxRetries}次: {ex.Message}", ex);
}
}
}
9.5 验证与异常
9.5.1 图层验证
public void ValidateLayer(OguLayer layer)
{
var errors = new List<string>();
// 基本验证
if (string.IsNullOrWhiteSpace(layer.Name))
errors.Add("图层名称不能为空");
if (layer.Fields == null || layer.Fields.Count == 0)
errors.Add("图层必须至少有一个字段");
// 字段验证
var fieldNames = new HashSet<string>();
foreach (var field in layer.Fields)
{
if (string.IsNullOrWhiteSpace(field.Name))
errors.Add("字段名称不能为空");
if (!fieldNames.Add(field.Name))
errors.Add($"字段名'{field.Name}'重复");
}
// 要素验证
foreach (var feature in layer.Features)
{
if (string.IsNullOrWhiteSpace(feature.Wkt))
errors.Add($"要素{feature.Fid}的几何为空");
foreach (var attrName in feature.Attributes.Keys)
{
if (!fieldNames.Contains(attrName))
errors.Add($"要素{feature.Fid}包含未定义的属性'{attrName}'");
}
}
if (errors.Count > 0)
{
throw new LayerValidationException(
$"图层验证失败:\n{string.Join("\n", errors)}")
{
Context = { ["ErrorCount"] = errors.Count }
};
}
}
9.5.2 几何验证
public void ValidateGeometry(string wkt, bool throwOnInvalid = true)
{
try
{
var geom = GeometryUtil.Wkt2Geometry(wkt);
// 空几何检查
if (GeometryUtil.IsEmpty(geom))
{
if (throwOnInvalid)
throw new TopologyException("几何为空");
return;
}
// 有效性检查
var validResult = GeometryUtil.IsValid(geom);
if (!validResult.IsValid)
{
if (throwOnInvalid)
throw new TopologyException($"几何无效: {validResult.ErrorMessage}");
}
// 简单性检查
var simpleResult = GeometryUtil.IsSimple(geom);
if (!simpleResult.IsSimple)
{
Console.WriteLine($"警告: 几何不简单 - {simpleResult.Reason}");
}
}
catch (System.Exception ex) when (!(ex is TopologyException))
{
throw new FormatParseException($"WKT解析失败: {wkt}", ex);
}
}
9.6 日志记录
9.6.1 结合日志框架
using Microsoft.Extensions.Logging;
public class LayerService
{
private readonly ILogger<LayerService> _logger;
public LayerService(ILogger<LayerService> logger)
{
_logger = logger;
}
public OguLayer ReadLayer(string path)
{
_logger.LogInformation("开始读取图层: {Path}", path);
try
{
var layer = OguLayerUtil.ReadLayer(DataFormatType.SHP, path);
_logger.LogInformation("读取成功,要素数: {Count}", layer.GetFeatureCount());
return layer;
}
catch (DataSourceException ex)
{
_logger.LogError(ex, "数据源错误: {Message}", ex.Message);
throw;
}
catch (OguException ex)
{
_logger.LogError(ex, "GIS操作错误: {Message}, 上下文: {@Context}",
ex.Message, ex.Context);
throw;
}
}
}
9.6.2 异常监控
public static class ExceptionMonitor
{
private static int _exceptionCount = 0;
private static readonly ConcurrentDictionary<string, int> _exceptionTypes = new();
public static void Track(OguException ex)
{
Interlocked.Increment(ref _exceptionCount);
var typeName = ex.GetType().Name;
_exceptionTypes.AddOrUpdate(typeName, 1, (_, count) => count + 1);
// 可以发送到监控系统
// SendToMonitoring(ex);
}
public static void PrintStats()
{
Console.WriteLine($"总异常数: {_exceptionCount}");
foreach (var (type, count) in _exceptionTypes)
{
Console.WriteLine($" {type}: {count}");
}
}
}
// 使用
try
{
ProcessData();
}
catch (OguException ex)
{
ExceptionMonitor.Track(ex);
throw;
}
9.7 最佳实践
9.7.1 异常使用原则
// ✓ 使用具体的异常类型
throw new DataSourceException("文件不存在: data.shp");
// ✗ 避免使用通用异常
throw new Exception("文件不存在");
// ✓ 包含有用的上下文信息
throw new OguException("操作失败")
{
Context = { ["Path"] = path, ["Operation"] = "Read" }
};
// ✓ 保留原始异常
catch (Exception ex)
{
throw new DataSourceException("读取失败", ex);
}
// ✗ 吞掉异常
catch (Exception) { } // 不要这样做
9.7.2 异常文档
/// <summary>
/// 读取图层
/// </summary>
/// <param name="path">文件路径</param>
/// <returns>图层对象</returns>
/// <exception cref="ArgumentException">路径为空时抛出</exception>
/// <exception cref="DataSourceException">无法打开数据源时抛出</exception>
/// <exception cref="FormatParseException">数据格式错误时抛出</exception>
public OguLayer ReadLayer(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException("路径不能为空", nameof(path));
// ...
}
9.8 小结
本章介绍了OGU4Net的异常处理体系:
- 异常层次:以OguException为基类的完整异常体系
- 具体异常:DataSource、FormatParse、EngineNotSupported、LayerValidation、Topology
- 处理模式:基本处理、分层处理、过滤器、重试
- 验证机制:图层验证、几何验证
- 日志记录:结合日志框架,异常监控
- 最佳实践:使用具体异常、包含上下文、保留原始异常
良好的异常处理可以提高程序的健壮性,便于问题排查和调试。

浙公网安备 33010602011771号