When an exception occurs, a simple message like “An error occurred” is often insufficient for identifying the cause. Without “contextual information”—such as what values variables held or what the search conditions were—debugging becomes difficult.
While creating a custom exception class with specific properties is one method, the C# Exception class provides a standard Data property (IDictionary). Utilizing this allows for easily attaching arbitrary keys and values to an exception.
This article explains how to retain search keywords and data counts as exception information when a search fails, using an inventory search system as an example.
Overview of the Exception.Data Property
The Data property of the Exception class is a collection implementing the IDictionary interface. By storing arbitrary objects as key-value pairs here, supplementary information beyond the error message can be passed from the point where the exception is thrown to where it is caught.
- Advantage: Dynamic information can be added without defining a dedicated exception class (custom exception).
- Usage: Suitable for recording variable states, timestamps, etc., during log output.
Practical Code Example: Detailed Error Information During Inventory Search
The following code processes a search for a specific product code from a product list. If the corresponding product is not found, an exception is thrown. At this time, “what code was searched” and “what the inventory count was” are added to the Data property.
using System;
using System.Collections; // Required for DictionaryEntry
using System.Collections.Generic;
using System.Linq;
namespace InventorySystem
{
class Program
{
static void Main()
{
// List of inventory data
var inventory = new List<string>
{
"PROD-001",
"PROD-002",
"PROD-005",
"PROD-010"
};
try
{
// Execute search with a non-existent product code
string targetCode = "PROD-999";
var result = FindProduct(inventory, targetCode);
Console.WriteLine($"Product found: {result}");
}
catch (InvalidOperationException ex)
{
// Output error message
Console.WriteLine($"[Error] {ex.Message}");
// Output all additional info contained in the Data property
// Since Data property is IDictionary, iterate using DictionaryEntry.
Console.WriteLine("--- Debug Information ---");
foreach (DictionaryEntry entry in ex.Data)
{
Console.WriteLine($"Key: {entry.Key,-15} | Value: {entry.Value}");
}
}
}
/// <summary>
/// Searches for a product from the list. Throws an exception with detailed info if not found.
/// </summary>
static string FindProduct(ICollection<string> inventory, string searchCode)
{
// Search by exact match, not partial match
var product = inventory.FirstOrDefault(x => x == searchCode);
if (product == null)
{
// Create a general exception
var ex = new InvalidOperationException("The specified product does not exist in the inventory list.");
// ★ Add custom data here
// Keys are generally strings.
ex.Data.Add("SearchCode", searchCode);
ex.Data.Add("InventoryCount", inventory.Count);
ex.Data.Add("Timestamp", DateTime.Now);
// Throw the exception containing the information
throw ex;
}
return product;
}
}
}
Execution Result
[Error] The specified product does not exist in the inventory list.
--- Debug Information ---
Key: SearchCode | Value: PROD-999
Key: InventoryCount | Value: 4
Key: Timestamp | Value: 2025/12/06 10:00:00
Technical Points and Precautions
1. Beware of Key Conflicts
The Data property is managed via key-value pairs. If the same key is already used internally by libraries or elsewhere, it might be overwritten or cause an error upon addition. It is recommended to maintain uniqueness by adding an application-specific prefix to key names (e.g., "MyApp.SearchCode").
2. Consideration for Serialization
If the exception object is serialized across application boundaries (e.g., in Web API responses or between distributed systems), the objects contained in the Data property must also be serializable. When storing instances of custom classes, verify they are serializable. Generally, storing primitive types like string, int, or DateTime is safer.
3. Differentiation from Custom Exceptions
While the Data property is excellent for “supplementary information for logging,” it is unsuitable for program logic control (conditional branching). If control logic based on specific values is required on the catching side (e.g., “retry if this value exists”), creating a “custom exception class” with dedicated properties is more type-safe and appropriate as a design choice, rather than extracting and casting values from the Data property.
Summary
Utilizing the Exception.Data property allows for the retention of contextual information at the time of an error without extending existing exception classes. Since this provides powerful clues for identifying causes from logs, especially during the operation phase, consider attaching values necessary for later investigation when implementing exception handling.
