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>
|
<!--<GitBranch>$([System.IO.File]::ReadAlltext('$(MsBuildThisFileDirectory)\.git\HEAD').Replace('ref: refs/heads/', '').Trim())</GitBranch>
|
||||||
<_ProjectName>$(GitBranch)</_ProjectName>-->
|
<_ProjectName>$(GitBranch)</_ProjectName>-->
|
||||||
|
|
||||||
<TargetFramework Condition="'$(TargetFramework)' == ''">net10.0</TargetFramework>
|
<TargetFramework Condition="'$(TargetFramework)' == ''">net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -1965,7 +1966,12 @@ public static partial class AcBinarySerializer
|
||||||
// Use pre-cached wrapper, fallback to GetWrapper on miss/polymorphism
|
// Use pre-cached wrapper, fallback to GetWrapper on miss/polymorphism
|
||||||
// Set interning eligibility for string collection elements
|
// Set interning eligibility for string collection elements
|
||||||
context.StringInternEligible = prop.IsStringCollectionProperty && prop.UseStringPropertyInterning(context.InternBit);
|
context.StringInternEligible = prop.IsStringCollectionProperty && prop.UseStringPropertyInterning(context.InternBit);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
var value = GetPropertyValueWithDiagnostics(obj, prop);
|
||||||
|
#else
|
||||||
var value = prop.GetValue(obj);
|
var value = prop.GetValue(obj);
|
||||||
|
#endif
|
||||||
|
|
||||||
// SKIP marker only for null (reference types)
|
// SKIP marker only for null (reference types)
|
||||||
// Empty string, empty collections, etc. are valid values and must be written!
|
// 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
|
#endregion
|
||||||
|
|
||||||
#region Specialized Array Writers
|
#region Specialized Array Writers
|
||||||
|
|
|
||||||
|
|
@ -964,11 +964,12 @@ public class AcBinaryHubProtocol : IHubProtocol
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown byte in chunk mode — break out (shouldn't happen).
|
// Unknown byte in chunk mode.
|
||||||
// Note: AsyncPipeReaderInput's WritePos/ReadPos are private, so the previous diagnostic
|
// Real-world case: server-side serialization fails after CHUNK_START was sent, then SignalR
|
||||||
// fields are unavailable here. Enable AsyncPipeReaderInput.DiagnosticLog (DEBUG-only)
|
// emits a normal framed CloseMessage. The first byte here is then the little-endian payload
|
||||||
// for deeper instrumentation when investigating framing-state corruption.
|
// length (e.g. 114), not [201]/[202]. If we keep chunk state, subsequent parses keep failing
|
||||||
_logger?.LogWarning("TryParseChunkData unknown byte {FirstByte} in chunk mode, breaking. " +
|
// 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} " +
|
"binderHash={BinderHash} inputLength={InputLength} " +
|
||||||
"state: streamedArgType={TargetType} deserTaskStatus={TaskStatus} chunkFrameBytesConsumed={ChunkFrameBytesConsumed}",
|
"state: streamedArgType={TargetType} deserTaskStatus={TaskStatus} chunkFrameBytesConsumed={ChunkFrameBytesConsumed}",
|
||||||
firstByte,
|
firstByte,
|
||||||
|
|
@ -977,7 +978,10 @@ public class AcBinaryHubProtocol : IHubProtocol
|
||||||
state.StreamedArgType.Name,
|
state.StreamedArgType.Name,
|
||||||
state.DeserTask?.Status.ToString() ?? "null",
|
state.DeserTask?.Status.ToString() ?? "null",
|
||||||
state.ChunkFrameBytesConsumed);
|
state.ChunkFrameBytesConsumed);
|
||||||
break;
|
|
||||||
|
state.Input.Dispose();
|
||||||
|
_chunkStates.Remove(binder);
|
||||||
|
return TryParseMessage(ref input, binder, out message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue