Skip to content

Commit e582b3a

Browse files
committed
Added new typesafe helper in Result object to get metadata 'TValue? GetMetadataValueOrDefault<TValue>(string key)'
1 parent f5a1736 commit e582b3a

File tree

4 files changed

+92
-2
lines changed

4 files changed

+92
-2
lines changed

docs/wiki/Error-Handling.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,22 @@ public async ValueTask<Result<Order>> HandleAsync(
219219
}
220220
```
221221

222+
Retrieve metadata values with type safety using `GetMetadataValueOrDefault<TValue>`:
223+
224+
```csharp
225+
if (result.IsFailure)
226+
{
227+
// Type-safe retrieval - returns default if key missing or type mismatch
228+
var customerId = result.GetMetadataValueOrDefault<int>("CustomerId");
229+
var itemCount = result.GetMetadataValueOrDefault<int>("ItemCount");
230+
var timestamp = result.GetMetadataValueOrDefault<DateTime>("Timestamp");
231+
232+
_logger.LogError(
233+
"Order creation failed for customer {CustomerId} with {ItemCount} items at {Timestamp}",
234+
customerId, itemCount, timestamp);
235+
}
236+
```
237+
222238
## Logging Errors
223239

224240
Use `AfterHandleAsync` for consistent error logging:

docs/wiki/The-Result-Type.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,16 @@ var result = Result<User>.Success(user)
9494
.WithMetadata("Source", "API")
9595
.WithMetadata("RequestId", Guid.NewGuid());
9696

97-
// Access metadata
98-
var createdAt = (DateTime)result.Metadata["CreatedAt"];
97+
// Access metadata with type-safe helper
98+
var createdAt = result.GetMetadataValueOrDefault<DateTime>("CreatedAt");
99+
var source = result.GetMetadataValueOrDefault<string>("Source");
100+
101+
// Returns default if key doesn't exist or type doesn't match
102+
var missing = result.GetMetadataValueOrDefault<int>("NonExistent"); // Returns 0
103+
var wrongType = result.GetMetadataValueOrDefault<string>("CreatedAt"); // Returns null
104+
105+
// Direct dictionary access is also available
106+
var requestId = (Guid)result.Metadata["RequestId"];
99107
```
100108

101109
## Patterns

src/ResultR.Tests/ResultTests/ResultTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,53 @@ public void Metadata_ReturnsEmptyDictionary_WhenNoMetadataSet()
5757

5858
Assert.Empty(result.Metadata);
5959
}
60+
61+
[Fact]
62+
public void GetMetadataValueOrDefault_ReturnsValue_WhenKeyExistsAndTypeMatches()
63+
{
64+
var result = Result.Success()
65+
.WithMetadata("StringKey", "TestValue")
66+
.WithMetadata("IntKey", 42);
67+
68+
Assert.Equal("TestValue", result.GetMetadataValueOrDefault<string>("StringKey"));
69+
Assert.Equal(42, result.GetMetadataValueOrDefault<int>("IntKey"));
70+
}
71+
72+
[Fact]
73+
public void GetMetadataValueOrDefault_ReturnsDefault_WhenKeyDoesNotExist()
74+
{
75+
var result = Result.Success();
76+
77+
Assert.Null(result.GetMetadataValueOrDefault<string>("NonExistentKey"));
78+
Assert.Equal(0, result.GetMetadataValueOrDefault<int>("NonExistentKey"));
79+
}
80+
81+
[Fact]
82+
public void GetMetadataValueOrDefault_ReturnsDefault_WhenTypeMismatch()
83+
{
84+
var result = Result.Success()
85+
.WithMetadata("IntKey", 42);
86+
87+
Assert.Null(result.GetMetadataValueOrDefault<string>("IntKey"));
88+
}
89+
90+
[Fact]
91+
public void GetMetadataValueOrDefault_ReturnsDefault_WhenNoMetadataSet()
92+
{
93+
var result = Result.Success();
94+
95+
Assert.Null(result.GetMetadataValueOrDefault<object>("AnyKey"));
96+
}
97+
98+
[Fact]
99+
public void GetMetadataValueOrDefault_WorksWithComplexTypes()
100+
{
101+
var complexObject = new List<int> { 1, 2, 3 };
102+
var result = Result.Success()
103+
.WithMetadata("ListKey", complexObject);
104+
105+
var retrieved = result.GetMetadataValueOrDefault<List<int>>("ListKey");
106+
107+
Assert.Same(complexObject, retrieved);
108+
}
60109
}

src/ResultR/Result.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ public Result WithMetadata(string key, object value)
7676
_metadata[key] = value;
7777
return this;
7878
}
79+
80+
/// <summary>
81+
/// Retrieves a metadata value by key, cast to the specified type.
82+
/// Returns the default value for the type if the key does not exist or the value cannot be cast.
83+
/// </summary>
84+
/// <typeparam name="TValue">The type to cast the metadata value to.</typeparam>
85+
/// <param name="key">The metadata key.</param>
86+
/// <returns>The metadata value cast to <typeparamref name="TValue"/>, or <c>default</c> if not found or cast fails.</returns>
87+
public TValue? GetMetadataValueOrDefault<TValue>(string key)
88+
{
89+
if (_metadata is null || !_metadata.TryGetValue(key, out var value))
90+
{
91+
return default;
92+
}
93+
94+
return value is TValue typedValue ? typedValue : default;
95+
}
7996
}
8097

8198
/// <summary>

0 commit comments

Comments
 (0)