Skip to content

Fix NRT errors and enable TreatWarningsAsErrors#159

Merged
niemyjski merged 25 commits intomainfrom
nrt/treat-warnings-as-errors
Apr 12, 2026
Merged

Fix NRT errors and enable TreatWarningsAsErrors#159
niemyjski merged 25 commits intomainfrom
nrt/treat-warnings-as-errors

Conversation

@niemyjski
Copy link
Copy Markdown
Member

Summary

  • Replaced the ineffective <WarningsAsErrors>true\ (which expects warning IDs, not a boolean) with the correct <TreatWarningsAsErrors>true\
  • Fixed all CS86xx nullable reference warnings that were previously hidden
  • Added missing \where T : notnull\ constraints to match ICacheClient interface
  • Added proper null guards in tests and null-conditional access in library code

Changes

  • build/common.props: \WarningsAsErrors\ → \TreatWarningsAsErrors\
  • RedisCacheClient.cs: Added \where T : notnull\ to \GetListAsync, \ListAddAsync, \ListRemoveAsync\ and private helpers
  • RedisMessageBus.cs: Null-safe envelope property access
  • RedisQueue.cs: Null-coalescing for poison entry ID
  • RedisFileStorage.cs: Non-null fallbacks for LogState.Property calls
  • RedisExtensions.cs: [return: MaybeNull]\ on \ToValueOfType()\
  • Tests/Samples: \Assert.NotNull\ guards and null-conditional access

Test plan

  • \dotnet build Foundatio.All.slnx\ — 0 warnings, 0 errors

- Replace ineffective WarningsAsErrors=true with TreatWarningsAsErrors=true
- Add missing where T : notnull constraints on list cache methods
- Fix nullable reference dereferences in queue tests and samples
- Add [return: MaybeNull] to ToValueOfType<T>()
- Guard null IQueueEntry.Value access in tests with Assert.NotNull
- Use null-conditional/coalescing for nullable message envelope properties
- Fix LogState.Property calls to handle nullable arguments
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables nullable reference types and treats warnings as errors across the solution, then updates Foundatio.Redis library code, tests, benchmarks, and samples to satisfy the stricter compiler settings while preserving runtime behavior.

Changes:

  • Enabled <Nullable>enable</Nullable> and updated build settings to properly treat warnings as errors.
  • Updated Foundatio.Redis library code for null-safety (option defaults, deserialization guards, event handlers, generic constraints).
  • Updated tests/benchmarks/samples to align with nullable annotations and updated Foundatio package versions.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
build/common.props Enables nullable and enforces warnings-as-errors behavior.
src/Foundatio.Redis/Utility/EmbeddedResourceLoader.cs Adds explicit exception when an embedded resource is missing.
src/Foundatio.Redis/Extensions/RedisExtensions.cs Annotates potential null returns and tightens casts for NRT compliance.
src/Foundatio.Redis/Cache/RedisCacheClient.cs Adds where T : notnull for list APIs and adjusts null-handling during (de)serialization.
src/Foundatio.Redis/Cache/RedisCacheClientOptions.cs Marks required options as non-null with null! initialization.
src/Foundatio.Redis/Cache/RedisHybridCacheClient.cs Updates optional parameters to nullable to satisfy NRT.
src/Foundatio.Redis/Queues/RedisQueue.cs Applies null-safety to queue entry flow and payload envelope types.
src/Foundatio.Redis/Queues/RedisQueueOptions.cs Marks required options as non-null with null! initialization.
src/Foundatio.Redis/Storage/RedisFileStorage.cs Null-safety improvements in stream/info retrieval and file listing logic/logging.
src/Foundatio.Redis/Storage/RedisFileStorageOptions.cs Marks required options as non-null with null! initialization.
src/Foundatio.Redis/Messaging/RedisMessageBus.cs Adds null-safe envelope handling and delayed-message type resolution checks.
src/Foundatio.Redis/Messaging/RedisMessageBusOptions.cs Marks required Subscriber as non-null with null! initialization and cleans file header.
src/Foundatio.Redis/Foundatio.Redis.csproj Bumps Foundatio dependency version.
tests/Foundatio.Redis.Tests/SharedConnection.cs Updates muxer fields and API to be nullable-aware.
tests/Foundatio.Redis.Tests/Utility/RedisPatternTests.cs Adjusts nullability in tests for RedisPattern.Escape.
tests/Foundatio.Redis.Tests/Storage/RedisFileStorageTests.cs Uses null-forgiving on muxer retrieval in test setup/creation.
tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs Updates queue factory signature and adds null guards/assertions in tests.
tests/Foundatio.Redis.Tests/Messaging/RedisMessageBusTests.cs Updates nullable annotations and muxer usage.
tests/Foundatio.Redis.Tests/Locks/RedisLockTests.cs Uses null-forgiving muxer retrieval for initialization.
tests/Foundatio.Redis.Tests/Jobs/RedisJobQueueTests.cs Uses null-forgiving muxer retrieval for initialization.
tests/Foundatio.Redis.Tests/Caching/RedisCacheClientTests.cs Uses null-forgiving muxer retrieval; adjusts nullable casts in list removal.
tests/Foundatio.Redis.Tests/Caching/RedisHybridCacheClientTests.cs Uses null-forgiving muxer retrieval for distributed cache client.
tests/Foundatio.Redis.Tests/Caching/ScopedRedisCacheClientTests.cs Uses null-forgiving muxer retrieval; updates nullable test inputs.
tests/Foundatio.Redis.Tests/Caching/ScopedRedisHybridCacheClientTests.cs Uses null-forgiving muxer retrieval; updates nullable test inputs.
tests/Foundatio.Redis.Tests/Foundatio.Redis.Tests.csproj Bumps Foundatio.TestHarness dependency version.
tests/Foundatio.Benchmarks/Queues/QueueBenchmarks.cs Adds null-conditional completion in dequeue benchmark loop.
tests/Foundatio.Benchmarks/Queues/JobQueueBenchmarks.cs Makes optional logger factory parameters nullable.
samples/Foundatio.SampleJobClient/Program.cs Initializes static fields and message models for NRT compliance.
samples/Foundatio.SampleJobClient/Foundatio.SampleJobClient.csproj Bumps Foundatio.Xunit.v3 dependency version.
samples/Foundatio.SampleJob/SampleServiceProvider.cs Adds null-forgiving on logger factory usage in DI setup.
samples/Foundatio.SampleJob/Program.cs Initializes static logger and message model for NRT compliance.
samples/Foundatio.SampleJob/PingQueueJob.cs Makes lock acquisition nullable-aware and updates PingRequest model for NRT compliance.
Comments suppressed due to low confidence (1)

src/Foundatio.Redis/Messaging/RedisMessageBus.cs:99

  • envelope.Properties and envelope.Data are assumed non-null after deserialization, but a deserializer can legitimately produce null for reference-type properties (e.g., older payloads or explicit nulls). Guard against envelope.Data == null and iterate properties only when envelope.Properties != null (or normalize them to empty) to avoid runtime NREs when receiving malformed/legacy messages.
            var envelope = _serializer.Deserialize<RedisMessageEnvelope>(((byte[]?)channelMessage.Message)!);
            if (envelope is null)
            {
                _logger.LogWarning("OnMessage({Channel}) Deserialized envelope was null", channelMessage.Channel);
                return;
            }

            message = new Message(envelope.Data, DeserializeMessageBody)
            {
                Type = envelope.Type ?? string.Empty,
                ClrType = envelope.Type is not null ? GetMappedMessageType(envelope.Type) : null,
                CorrelationId = envelope.CorrelationId,
                UniqueId = envelope.UniqueId
            };

            foreach (var property in envelope.Properties)
                message.Properties.Add(property.Key, property.Value);
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Foundatio.Redis.Tests/Utility/RedisPatternTests.cs
Comment thread tests/Foundatio.Redis.Tests/SharedConnection.cs
Comment thread tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs Outdated
Comment thread tests/Foundatio.Benchmarks/Queues/QueueBenchmarks.cs Outdated
…rectness

- SharedConnection.GetMuxer: throw InvalidOperationException instead of
  returning null when RedisConnectionString is missing, giving a clear
  diagnostic message at the point of failure.
- Remove all null-forgiving operators (!) on GetMuxer calls across every
  test file since the method now guarantees a non-null return.
- QueueBenchmarks.DequeueQueue: replace silent entry?.CompleteAsync()
  with an explicit null throw so benchmark results are never skewed by
  silently dropped dequeues.
- RedisMessageBus.OnMessage: guard envelope.Properties (null check before
  iterating) and envelope.Data (coalesce to empty array) after
  deserialization. Mark both properties as nullable on RedisMessageEnvelope.
Copy link
Copy Markdown
Member Author

@niemyjski niemyjski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Foundatio.Redis/Cache/RedisCacheClientOptions.cs
Comment thread tests/Foundatio.Redis.Tests/Caching/RedisCacheClientTests.cs Outdated
Comment thread src/Foundatio.Redis/Messaging/RedisMessageBus.cs Outdated
Comment thread samples/Foundatio.SampleJob/PingQueueJob.cs Outdated
Comment thread samples/Foundatio.SampleJob/PingQueueJob.cs
Comment thread src/Foundatio.Redis/Storage/RedisFileStorage.cs Outdated
Comment thread src/Foundatio.Redis/Storage/RedisFileStorage.cs Outdated
Comment thread src/Foundatio.Redis/Storage/RedisFileStorage.cs Outdated
Comment thread tests/Foundatio.Redis.Tests/Caching/RedisCacheClientTests.cs Outdated
Comment thread tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs
Comment thread tests/Foundatio.Redis.Tests/SharedConnection.cs Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Foundatio.Redis.Tests/Caching/RedisCacheClientTests.cs Outdated
Comment thread tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs Outdated
- Revert logging hacks: restore original criteria.Prefix/Pattern patterns

- Revert NormalizePath change: use NormalizePath() in GetFileListAsync

- Fix string. -> String. globally across the repo

- Revert cast hack in RedisCacheClientTests: use items[10].ToString()

- Restore removed assertions in RedisQueueTests

- SharedConnection.GetMuxer: return null instead of throwing, return ConnectionMultiplexer?
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Foundatio.Redis.Tests/SharedConnection.cs
Comment thread samples/Foundatio.SampleJob/SampleServiceProvider.cs Outdated
Comment thread samples/Foundatio.SampleJob/PingQueueJob.cs Outdated
Comment thread samples/Foundatio.SampleJob/PingQueueJob.cs Outdated
Comment thread samples/Foundatio.SampleJob/PingQueueJob.cs Outdated
Comment thread samples/Foundatio.SampleJob/Program.cs
Comment thread src/Foundatio.Redis/Cache/RedisCacheClient.cs Outdated
Comment thread src/Foundatio.Redis/Cache/RedisCacheClient.cs Outdated
Comment on lines 29 to 53
{
T value;
if (redisValue.IsNull)
{
if (!typeof(T).IsValueType || Nullable.GetUnderlyingType(typeof(T)) is not null)
return default!;

throw new InvalidOperationException($"Cannot convert null Redis value to non-nullable type {typeof(T).Name}");
}

var type = typeof(T);

if (type == TypeHelper.BoolType || type == TypeHelper.StringType || type.IsNumeric())
value = (T)Convert.ChangeType(redisValue, type);
else if (type == TypeHelper.NullableBoolType || type.IsNullableNumeric())
value = redisValue.IsNull ? default : (T)Convert.ChangeType(redisValue, Nullable.GetUnderlyingType(type));
else
return serializer.Deserialize<T>((byte[])redisValue);
return (T)Convert.ChangeType(redisValue, type)!;

if (type == TypeHelper.NullableBoolType || type.IsNullableNumeric())
{
var underlyingType = Nullable.GetUnderlyingType(type)
?? throw new InvalidOperationException($"Expected nullable type but {type.Name} has no underlying type");
return (T)Convert.ChangeType(redisValue, underlyingType)!;
}

return value;
byte[] bytes = (byte[]?)redisValue
?? throw new InvalidOperationException($"Expected non-null byte[] from RedisValue for type {typeof(T).Name}");
return serializer.Deserialize<T>(bytes);
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not sure any of these changes are correct for this method implementation, this has been working for a decade like this

timeout = timeout.TotalMilliseconds
}).AnyContext();
return result.ToString();
return (RedisValue)result;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct?

Comment on lines +251 to +252
if (searchPattern is not null)
searchPattern = NormalizePath(searchPattern);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this line need to change?

.Select(entry => _serializer.Deserialize<FileSpec>((byte[])entry.Value))
.Where(fileSpec => patternRegex == null || patternRegex.IsMatch(fileSpec.Path))
.Where(entry => entry.Value.HasValue)
.Select(entry => _serializer.Deserialize<FileSpec>((byte[]?)entry.Value ?? []))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this need ?? []

.Select(entry => _serializer.Deserialize<FileSpec>((byte[])entry.Value))
.Where(fileSpec => criteria.Pattern == null || criteria.Pattern.IsMatch(fileSpec.Path))
.Where(entry => entry.Value.HasValue)
.Select(entry => _serializer.Deserialize<FileSpec>((byte[]?)entry.Value ?? []))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this need ?? []

return new RedisCacheClient(o => o.ConnectionMultiplexer(SharedConnection.GetMuxer(Log, Protocol)).LoggerFactory(Log).ShouldThrowOnSerializationError(shouldThrowOnSerializationError));
var muxer = SharedConnection.GetMuxer(Log, Protocol);
if (muxer is null)
return null!;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if muxer is null we should throw no? or should this method return type be nullable? Also always whitespace after return, take a deeper look at this.

Comment on lines +644 to +645
return;
var db = muxer.GetDatabase();
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log and return and always new line after return or }

var muxer = SharedConnection.GetMuxer(Log, Protocol);
if (muxer is null)
return ValueTask.CompletedTask;
return new ValueTask(muxer.FlushAllAsync());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after this.

return new ScopedCacheClient(new RedisCacheClient(o => o.ConnectionMultiplexer(SharedConnection.GetMuxer(Log, Protocol)).LoggerFactory(Log).ShouldThrowOnSerializationError(shouldThrowOnSerializationError)), "scoped");
var muxer = SharedConnection.GetMuxer(Log, Protocol);
if (muxer is null)
return null!;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after

{
var muxer = SharedConnection.GetMuxer(Log, Protocol);
if (muxer is null)
return null!;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same feedback as before should this method return ICacheClient? also always newline after return

@niemyjski
Copy link
Copy Markdown
Member Author

Addressing all remaining feedback

RedisCacheClient.cs .ToString() (lines 323, 905):
Fixed. Replaced (string)hashSlotKeys[i]!\ and (string)redisKeys[keyIndex]!\ with .ToString()\ which returns a non-nullable string directly from RedisKey/RedisValue, avoiding casts and null-forgiving operators.

RedisExtensions.cs ToValueOfType (line 36):
Reverted to minimal NRT changes. Only additions: [return: MaybeNull]\ attribute, \default!\ for nullable path, !\ on two conversions. Logic and branching are identical to the decade-old implementation on main.

RedisExtensions.cs ! on line 31:
The !\ on (byte[])redisValue!\ is needed because the implicit cast from \RedisValue\ to \�yte[]\ returns \�yte[]?. The \Deserialize\ method takes non-nullable \�yte[].

RedisQueue.cs options throw (line 46):
\�ase(options)\ already validates \options\ — the explicit \ArgumentNullException.ThrowIfNull(options.ConnectionMultiplexer)\ handles the specific case. Adding \ThrowIfNull(options)\ before is unreachable since base(options) runs first.

RedisQueue.cs workId/waitId conversions:
Replaced all \workId!, \waitId!, and \id!\ on RedisValue structs with explicit .ToString()\ calls for clarity.

PingQueueJob.cs (lines 31, 43):
Fixed. Removed null guards, using \Value!\ since base class QueueJobBase guarantees non-null Value before these methods are called.

RedisQueueTests.cs Assert.NotNull(entry.Value) (line 792):
These are needed because \IQueueEntry.Value\ is now \T?\ with NRT. The assertion validates non-null at runtime and tells the compiler it's safe to dereference.

RedisPatternTests.cs (line 28):
Already using built-in \string?\ type — no change needed.

RedisFileStorage.cs line 252:
Changed because \NormalizePath\ now takes non-nullable \string. Since \searchPattern\ is nullable, we check for null before calling. Path normalization logic unchanged.

RedisCacheClientTests.cs GetCacheClient return type:
Changed to \ICacheClient?\ with
eturn null\ instead of
eturn null!\ — properly reflects nullable return when muxer is not configured.

SharedConnection.cs GetMuxer:
Returns \ConnectionMultiplexer?\ — callers handle null with early returns. Name unchanged.

@niemyjski niemyjski requested a review from ejsmith April 12, 2026 16:08
Comment on lines +62 to 63
return ValueTask.CompletedTask;
return new ValueTask(muxer.FlushAllAsync());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline after return

Comment on lines +24 to 25
return null!;
return new RedisQueue<SampleQueueWorkItem>(o => o
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after eturn and also should this be returning IQueue? nullable.

Comment on lines +120 to 121
return ValueTask.CompletedTask;
return new ValueTask(muxer.FlushAllAsync());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after return

@@ -26,8 +26,8 @@ protected RedisLockTests(ITestOutputHelper output, RedisProtocol? protocol) : ba
{
_protocol = protocol;
var muxer = SharedConnection.GetMuxer(Log, _protocol);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this throw if null? also newline after validation

Comment on lines 32 to 33
return null!;
return new RedisMessageBus(o =>
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be returning IMessageBus? and also newline after return

Comment on lines +200 to 201
return;
var messageBus1 = new RedisMessageBus(new RedisMessageBusOptions { Subscriber = muxer.GetSubscriber(), Topic = _topic, LoggerFactory = Log });
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline after

Comment on lines +249 to 250
return ValueTask.CompletedTask;
return new ValueTask(muxer.FlushAllAsync());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline after

}

protected override IQueue<SimpleWorkItem> GetQueue(int retries = 1, TimeSpan? workItemTimeout = null, TimeSpan? retryDelay = null, int[] retryMultipliers = null, int deadLetterMaxItems = 100, bool runQueueMaintenance = true, TimeProvider timeProvider = null, ISerializer serializer = null)
protected override IQueue<SimpleWorkItem> GetQueue(int retries = 1, TimeSpan? workItemTimeout = null, TimeSpan? retryDelay = null, int[]? retryMultipliers = null, int deadLetterMaxItems = 100, bool runQueueMaintenance = true, TimeProvider? timeProvider = null, ISerializer? serializer = null)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

override IQueue?

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +28 to +34
var muxer = SharedConnection.GetMuxer(Log, _protocol);
if (muxer is null)
{
_cache = null!;
_messageBus = null!;
return;
}
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When SharedConnection.GetMuxer(...) returns null (missing RedisConnectionString), this constructor sets _cache/_messageBus to null! and continues. Subsequent methods (GetLockProvider, GetThrottlingLockProvider, and Dispose) unconditionally dereference these fields, leading to NullReferenceException rather than a clear skip/fail-fast. Consider throwing an xUnit SkipException (or similar) in this branch, or otherwise ensuring the class won’t execute tests/Dispose with null dependencies.

Copilot uses AI. Check for mistakes.
@@ -373,7 +375,7 @@ protected override async Task<IQueueEntry<T>> DequeueImplAsync(CancellationToken
catch (Exception ex)
{
_logger.LogWarning(ex, "Error deserializing queue entry payload: {WorkId}, abandoning for retry", value);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning log message uses the {WorkId} placeholder but passes value (a RedisValue) instead of the workId string that was just computed. This makes logs harder to correlate and can produce unexpected output for non-string RedisValue representations; pass workId to match the placeholder and subsequent poison-entry handling.

Suggested change
_logger.LogWarning(ex, "Error deserializing queue entry payload: {WorkId}, abandoning for retry", value);
_logger.LogWarning(ex, "Error deserializing queue entry payload: {WorkId}, abandoning for retry", workId);

Copilot uses AI. Check for mistakes.
Comment on lines 14 to 32
@@ -18,13 +26,18 @@ public static T ToValueOfType<T>(this RedisValue redisValue, ISerializer seriali
if (type == TypeHelper.BoolType || type == TypeHelper.StringType || type.IsNumeric())
value = (T)Convert.ChangeType(redisValue, type);
else if (type == TypeHelper.NullableBoolType || type.IsNullableNumeric())
value = redisValue.IsNull ? default : (T)Convert.ChangeType(redisValue, Nullable.GetUnderlyingType(type));
value = redisValue.IsNull ? default! : (T)Convert.ChangeType(redisValue, Nullable.GetUnderlyingType(type)!);
else
return serializer.Deserialize<T>((byte[])redisValue);
return serializer.Deserialize<T>((byte[])redisValue!);

Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML docs claim ToValueOfType<T> “handles null Redis values”, but the implementation doesn’t check redisValue.IsNull (or the "@@null" sentinel) and will throw if called with a null Redis value for primitive targets. Either update the docs to reflect that callers must filter HasValue/sentinel values before calling, or add explicit handling in the method so the documented behavior matches implementation.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +32
_cache = null!;
_messageBus = null!;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should throw here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Constructor now throws InvalidOperationException via null-coalescing throw when GetMuxer returns null, instead of silently assigning null!. Also ran dotnet format which fixed indentation in RedisQueueTests.cs.

@niemyjski niemyjski merged commit 3f37c36 into main Apr 12, 2026
4 checks passed
@niemyjski niemyjski deleted the nrt/treat-warnings-as-errors branch April 12, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants