Switch to net9.0; improve AcBinary diagnostics & chunk fallback
- Change target framework to net9.0 in AyCode.Core.targets. - Add DEBUG-only property access diagnostics to AcBinarySerializer for better error reporting. - Update AcBinaryHubProtocol to dispose chunk state and retry normal parse on unknown bytes, improving resilience after serialization failures. - Update comments to clarify new logic and rationale.
This commit is contained in:
parent
c2a22e5215
commit
e2b96b4148
|
|
@ -7,7 +7,7 @@
|
|||
<!--<GitBranch>$([System.IO.File]::ReadAlltext('$(MsBuildThisFileDirectory)\.git\HEAD').Replace('ref: refs/heads/', '').Trim())</GitBranch>
|
||||
<_ProjectName>$(GitBranch)</_ProjectName>-->
|
||||
|
||||
<TargetFramework Condition="'$(TargetFramework)' == ''">net10.0</TargetFramework>
|
||||
<TargetFramework Condition="'$(TargetFramework)' == ''">net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Concurrent;
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
|
@ -1965,7 +1966,12 @@ public static partial class AcBinarySerializer
|
|||
// Use pre-cached wrapper, fallback to GetWrapper on miss/polymorphism
|
||||
// Set interning eligibility for string collection elements
|
||||
context.StringInternEligible = prop.IsStringCollectionProperty && prop.UseStringPropertyInterning(context.InternBit);
|
||||
|
||||
#if DEBUG
|
||||
var value = GetPropertyValueWithDiagnostics(obj, prop);
|
||||
#else
|
||||
var value = prop.GetValue(obj);
|
||||
#endif
|
||||
|
||||
// SKIP marker only for null (reference types)
|
||||
// Empty string, empty collections, etc. are valid values and must be written!
|
||||
|
|
@ -2004,6 +2010,26 @@ public static partial class AcBinarySerializer
|
|||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static object? GetPropertyValueWithDiagnostics(object source, BinaryPropertyAccessor property)
|
||||
{
|
||||
try
|
||||
{
|
||||
return property.GetValue(source);
|
||||
}
|
||||
catch (Exception ex) when (ex is NullReferenceException || ex is TargetInvocationException)
|
||||
{
|
||||
var sourceType = source.GetType().FullName ?? source.GetType().Name;
|
||||
var propertyName = property.Name;
|
||||
var declaredType = property.PropertyType?.FullName ?? "<unknown>";
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"AcBinary runtime property write failed at {sourceType}.{propertyName} (declaredType={declaredType}, accessor={property.AccessorType}). " +
|
||||
"This usually indicates a null-sensitive property getter or malformed source data.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Specialized Array Writers
|
||||
|
|
|
|||
|
|
@ -964,11 +964,12 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
return true;
|
||||
}
|
||||
|
||||
// Unknown byte in chunk mode — break out (shouldn't happen).
|
||||
// Note: AsyncPipeReaderInput's WritePos/ReadPos are private, so the previous diagnostic
|
||||
// fields are unavailable here. Enable AsyncPipeReaderInput.DiagnosticLog (DEBUG-only)
|
||||
// for deeper instrumentation when investigating framing-state corruption.
|
||||
_logger?.LogWarning("TryParseChunkData unknown byte {FirstByte} in chunk mode, breaking. " +
|
||||
// Unknown byte in chunk mode.
|
||||
// Real-world case: server-side serialization fails after CHUNK_START was sent, then SignalR
|
||||
// emits a normal framed CloseMessage. The first byte here is then the little-endian payload
|
||||
// length (e.g. 114), not [201]/[202]. If we keep chunk state, subsequent parses keep failing
|
||||
// with the same warning. Instead, tear down chunk mode and re-parse as normal framed message.
|
||||
_logger?.LogWarning("TryParseChunkData unknown byte {FirstByte} in chunk mode, falling back to normal parse. " +
|
||||
"binderHash={BinderHash} inputLength={InputLength} " +
|
||||
"state: streamedArgType={TargetType} deserTaskStatus={TaskStatus} chunkFrameBytesConsumed={ChunkFrameBytesConsumed}",
|
||||
firstByte,
|
||||
|
|
@ -977,7 +978,10 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
state.StreamedArgType.Name,
|
||||
state.DeserTask?.Status.ToString() ?? "null",
|
||||
state.ChunkFrameBytesConsumed);
|
||||
break;
|
||||
|
||||
state.Input.Dispose();
|
||||
_chunkStates.Remove(binder);
|
||||
return TryParseMessage(ref input, binder, out message);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Reference in New Issue