Code cleanup

This commit is contained in:
2025-07-10 18:58:49 +03:00
parent 1765358071
commit e7e0cbac05
38 changed files with 118 additions and 145 deletions
+1
View File
@@ -1,3 +1,4 @@
using mROA.Abstract;
using mROA.Implementation; using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
+1
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Abstract;
using mROA.Implementation; using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
+1
View File
@@ -1,3 +1,4 @@
using mROA.Abstract;
using mROA.Implementation; using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
+1 -1
View File
@@ -30,7 +30,7 @@ namespace <!L namespace>
<!D> <!D>
}; };
public void Inject(T dependency) public void Inject(object dependency)
{ {
} }
+7 -7
View File
@@ -38,7 +38,7 @@ namespace mROA.Codegen
private TemplateDocument _interfaceTemplateOriginal; private TemplateDocument _interfaceTemplateOriginal;
private TemplateDocument _methodInvokerOriginal; private TemplateDocument _methodInvokerOriginal;
private TemplateDocument _methodRepoTemplate; private TemplateDocument _methodRepoTemplate;
private int _currentInternalCallIndex = 0; private int _currentInternalCallIndex;
private void GenerateCode(SourceProductionContext context, Compilation compilation, private void GenerateCode(SourceProductionContext context, Compilation compilation,
@@ -274,7 +274,7 @@ namespace mROA.Codegen
if (isUntrusted) if (isUntrusted)
{ {
caller = $"CallUntrustedAsync(_callIndices[{_currentInternalCallIndex++}]{parameterLink})"; caller = $"CallUntrustedAsync(CallIndices[{_currentInternalCallIndex++}]{parameterLink})";
} }
else else
{ {
@@ -285,10 +285,10 @@ namespace mROA.Codegen
: string.Empty; : string.Empty;
caller = isVoid caller = isVoid
? $"CallAsync(_callIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})" ? $"CallAsync(CallIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})"
: isAsync : isAsync
? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>(_callIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})" ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>(CallIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})"
: $"GetResultAsync<{ToFullString(method.ReturnType)}>(_callIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})"; : $"GetResultAsync<{ToFullString(method.ReturnType)}>(CallIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})";
if (!isVoid) if (!isVoid)
prefix = "return " + prefix; prefix = "return " + prefix;
@@ -501,7 +501,7 @@ namespace mROA.Codegen
} }
frontend = frontend =
$"get => GetResultAsync<{method.ReturnType.ToUnityString()}>(_callIndices[{_currentInternalCallIndex++}]{parametersArray}).GetAwaiter().GetResult();"; $"get => GetResultAsync<{method.ReturnType.ToUnityString()}>(CallIndices[{_currentInternalCallIndex++}]{parametersArray}).GetAwaiter().GetResult();";
} }
else else
{ {
@@ -547,7 +547,7 @@ namespace mROA.Codegen
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
} }
frontend = $"set => CallAsync(_callIndices[{_currentInternalCallIndex++}], new System.Object[] {{ {parametersArray} }}).Wait();"; frontend = $"set => CallAsync(CallIndices[{_currentInternalCallIndex++}], new System.Object[] {{ {parametersArray} }}).Wait();";
} }
propsCollection.Add((frontend, method)); propsCollection.Add((frontend, method));
+5 -24
View File
@@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using mROA.Implementation; using mROA.Implementation;
@@ -8,30 +7,12 @@ namespace mROA.Abstract
{ {
public interface ICallIndexProvider : IInjectableModule public interface ICallIndexProvider : IInjectableModule
{ {
Dictionary<Type, Func<int, IRepresentationModule, IEndPointContext, int[], RemoteObjectBase>> Activators { get; } Dictionary<Type, Func<int, IRepresentationModule, IEndPointContext, int[], RemoteObjectBase>> Activators
{
get;
}
void SetupOffset(int offset); void SetupOffset(int offset);
int[] GetIndices(Type type); int[] GetIndices(Type type);
} }
// public class GeneratedInvokersCollection : IReadOnlyList<IMethodInvoker>
// {
// private readonly List<IMethodInvoker> _invokers = new()
// {
//
// };
//
// public IEnumerator<IMethodInvoker> GetEnumerator()
// {
// return _invokers.GetEnumerator();
// }
//
// IEnumerator IEnumerable.GetEnumerator()
// {
// return GetEnumerator();
// }
//
// public int Count => _invokers.Count;
//
// public IMethodInvoker this[int index] => _invokers[index];
// }
} }
@@ -11,6 +11,6 @@ namespace mROA.Abstract
T Deserialize<T>(ReadOnlyMemory<byte> rawMemory, IEndPointContext? context); T Deserialize<T>(ReadOnlyMemory<byte> rawMemory, IEndPointContext? context);
object? Deserialize(ReadOnlyMemory<byte> rawMemory, Type type, IEndPointContext? context); object? Deserialize(ReadOnlyMemory<byte> rawMemory, Type type, IEndPointContext? context);
T Cast<T>(object nonCasted, IEndPointContext? context); T Cast<T>(object nonCasted, IEndPointContext? context);
object? Cast(object nonCasted, Type type, IEndPointContext? context); object? Cast(object? nonCasted, Type type, IEndPointContext? context);
} }
} }
+1 -3
View File
@@ -1,6 +1,4 @@
using mROA.Implementation; namespace mROA.Abstract
namespace mROA.Abstract
{ {
public interface IEndPointContext : IInjectableModule public interface IEndPointContext : IInjectableModule
{ {
+2 -1
View File
@@ -5,7 +5,8 @@
public void BindEvents(T source, IEndPointContext context, public void BindEvents(T source, IEndPointContext context,
IRepresentationModuleProducer representationModuleProducer, int index); IRepresentationModuleProducer representationModuleProducer, int index);
void IEventBinder.BindEvents(object source, IEndPointContext context, IRepresentationModuleProducer representationModuleProducer, void IEventBinder.BindEvents(object source, IEndPointContext context,
IRepresentationModuleProducer representationModuleProducer,
int index) int index)
{ {
BindEvents((T)source, context, representationModuleProducer, index); BindEvents((T)source, context, representationModuleProducer, index);
+1
View File
@@ -10,6 +10,7 @@ namespace mROA.Abstract
{ {
int Id { get; } int Id { get; }
IEndPointContext Context { get; } IEndPointContext Context { get; }
Task<(object? Deserialized, EMessageType MessageType)> GetSingle(Predicate<NetworkMessageHeader> rule, Task<(object? Deserialized, EMessageType MessageType)> GetSingle(Predicate<NetworkMessageHeader> rule,
IEndPointContext? context, CancellationToken token = default, IEndPointContext? context, CancellationToken token = default,
params Func<NetworkMessageHeader, Type?>[] converter); params Func<NetworkMessageHeader, Type?>[] converter);
-14
View File
@@ -1,14 +0,0 @@
namespace mROA.Abstract
{
// public interface IContextualSerializationToolKit : IInjectableModule
// {
// byte[] Serialize<T>(T objectToSerialize);
// byte[] Serialize(object objectToSerialize, Type type);
// T? Deserialize<T>(byte[] rawData);
// object? Deserialize(byte[] rawData, Type type);
// T? Deserialize<T>(Span<byte> rawData);
// object? Deserialize(Span<byte> rawData, Type type);
// T? Cast<T>(object? nonCasted);
// object? Cast(object? nonCasted, Type type);
// }
}
+1 -2
View File
@@ -1,6 +1,5 @@
namespace mROA.Implementation namespace mROA.Abstract
{ {
#pragma warning disable CS8618, CS9264
public interface IShared public interface IShared
{ {
} }
@@ -5,6 +5,7 @@ namespace mROA.Implementation.Attributes
public class ApiLevelAttribute : Attribute public class ApiLevelAttribute : Attribute
{ {
public int ApiLevel { get; set; } public int ApiLevel { get; set; }
public ApiLevelAttribute(int level) public ApiLevelAttribute(int level)
{ {
ApiLevel = level; ApiLevel = level;
@@ -12,7 +13,6 @@ namespace mROA.Implementation.Attributes
public ApiLevelAttribute() : this(0) public ApiLevelAttribute() : this(0)
{ {
} }
} }
} }
@@ -5,6 +5,7 @@ namespace mROA.Implementation.Attributes
public class UntrustedAttribute : Attribute public class UntrustedAttribute : Attribute
{ {
public bool UseAwait { get; private set; } public bool UseAwait { get; private set; }
public UntrustedAttribute(bool useAwait = true) public UntrustedAttribute(bool useAwait = true)
{ {
UseAwait = useAwait; UseAwait = useAwait;
@@ -30,7 +30,6 @@ namespace mROA.Implementation.Backend
public ICommandExecution Execute(ICallRequest command, IInstanceRepository instanceRepository, public ICommandExecution Execute(ICallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext endPointContext) IRepresentationModule representationModule, IEndPointContext endPointContext)
{ {
try try
{ {
ThrowIfNotInjected(instanceRepository); ThrowIfNotInjected(instanceRepository);
+2 -1
View File
@@ -23,7 +23,8 @@ namespace mROA.Implementation.Backend
public IChannelInteractionModule GetInteraction(int id) public IChannelInteractionModule GetInteraction(int id)
{ {
return _connections!.GetValueOrDefault(id, null) ?? _connections!.GetValueOrDefault(-id, null) ?? throw new Exception("No connection found"); return _connections!.GetValueOrDefault(id, null) ?? _connections!.GetValueOrDefault(-id, null) ??
throw new Exception("No connection found");
} }
public event ConnectionHandler? OnConnected; public event ConnectionHandler? OnConnected;
@@ -11,15 +11,10 @@ namespace mROA.Implementation.Backend
{ {
public static object[] EventBinders = { }; public static object[] EventBinders = { };
private static int LastDebugId = -1;
private int _debugId = -1;
private IRepresentationModuleProducer? _representationModuleProducer; private IRepresentationModuleProducer? _representationModuleProducer;
// [CanBeNull] private Dictionary<int, object?> _singletons = new();
private Dictionary<int, object?> _singletons; private readonly IStorage<object> _storage;
private IStorage<object> _storage;
public InstanceRepository() public InstanceRepository()
{ {
@@ -82,7 +82,7 @@ namespace mROA.Implementation.Backend
{ {
await interaction.ReceiveChanel.Writer.WriteAsync(message); await interaction.ReceiveChanel.Writer.WriteAsync(message);
}; };
Task.Run(() => streamExtractor.SingleReceive()); _ = Task.Run(() => streamExtractor.SingleReceive());
var connectionRequest = await interaction.ReceiveChanel.Reader.ReadAsync(); var connectionRequest = await interaction.ReceiveChanel.Reader.ReadAsync();
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -17,6 +17,7 @@ namespace mROA.Implementation.Backend
private CancellationTokenSource _tokenSource = new(); private CancellationTokenSource _tokenSource = new();
private IContextualSerializationToolKit _serializationToolkit; private IContextualSerializationToolKit _serializationToolkit;
private IEndPointContext _context; private IEndPointContext _context;
public UdpGateway(IPEndPoint listeningEndpoint) public UdpGateway(IPEndPoint listeningEndpoint)
{ {
_client = new UdpClient(listeningEndpoint); _client = new UdpClient(listeningEndpoint);
@@ -10,15 +10,27 @@ namespace mROA.Implementation.Bootstrap
public void Build() public void Build()
{ {
foreach (var module in Modules) foreach (var module in Modules)
foreach (var injection in Modules) foreach (var injection in Modules)
module.Inject(injection); module.Inject(injection);
} }
public T? GetModule<T>() public T GetModule<T>()
{ {
return Modules.OfType<T>().FirstOrDefault(); return Modules.OfType<T>().FirstOrDefault();
} }
public bool TryGetModule<T>(out IInjectableModule module) where T : IInjectableModule
{
var mod = Modules.OfType<T>().FirstOrDefault();
if (mod is null)
{
module = default;
return false;
}
module = mod;
return true;
}
} }
} }
-1
View File
@@ -23,7 +23,6 @@ namespace mROA.Implementation
public override string ToString() public override string ToString()
{ {
return $"Call request {{ Id : {Id}, CommandId : {CommandId}, ObjectId : {ObjectId} }}"; return $"Call request {{ Id : {Id}, CommandId : {CommandId}, ObjectId : {ObjectId} }}";
} }
} }
@@ -160,12 +160,11 @@ namespace mROA.Implementation
public class StreamExtractor public class StreamExtractor
{ {
private static Stopwatch profiler = new Stopwatch(); private const int BufferSize = ushort.MaxValue;
private readonly Stream _ioStream; private readonly Stream _ioStream;
private readonly IContextualSerializationToolKit _serializationToolkit; private readonly IContextualSerializationToolKit _serializationToolkit;
private const int BufferSize = ushort.MaxValue;
private readonly Memory<byte> _buffer = new byte[BufferSize]; private readonly Memory<byte> _buffer = new byte[BufferSize];
private bool _manualConnectionState = true;
private readonly IEndPointContext _context; private readonly IEndPointContext _context;
private readonly byte[] _lenBuffer; private readonly byte[] _lenBuffer;
@@ -175,7 +174,6 @@ namespace mROA.Implementation
_ioStream = ioStream; _ioStream = ioStream;
_serializationToolkit = serializationToolkit; _serializationToolkit = serializationToolkit;
_context = context; _context = context;
profiler.Start();
_lenBuffer = new byte[2]; _lenBuffer = new byte[2];
} }
@@ -196,7 +194,6 @@ namespace mROA.Implementation
var localSpan = _buffer[..len]; var localSpan = _buffer[..len];
await _ioStream.ReadExactlyAsync(localSpan, cancellationToken: token); await _ioStream.ReadExactlyAsync(localSpan, cancellationToken: token);
profiler.Restart();
var message = _serializationToolkit.Deserialize<NetworkMessageHeader>(localSpan, _context); var message = _serializationToolkit.Deserialize<NetworkMessageHeader>(localSpan, _context);
MessageReceived(message); MessageReceived(message);
} }
@@ -217,7 +214,6 @@ namespace mROA.Implementation
header.CopyTo(_buffer); header.CopyTo(_buffer);
var sendingSpan = _buffer[..(len + 2)]; var sendingSpan = _buffer[..(len + 2)];
await _ioStream.WriteAsync(sendingSpan, token); await _ioStream.WriteAsync(sendingSpan, token);
profiler.Restart();
} }
public async Task SendFromChannel(ChannelReader<NetworkMessageHeader> channel, public async Task SendFromChannel(ChannelReader<NetworkMessageHeader> channel,
@@ -230,8 +226,7 @@ namespace mROA.Implementation
} }
} }
public bool IsConnected => _ioStream is { CanRead: true, CanWrite: true };
public bool IsConnected => _ioStream is { CanRead: true, CanWrite: true } && _manualConnectionState;
} }
} }
} }
@@ -6,9 +6,9 @@ namespace mROA.Implementation
public class CollectableMethodRepository : IMethodRepository public class CollectableMethodRepository : IMethodRepository
{ {
private List<IMethodInvoker> _methods = new(); private List<IMethodInvoker> _methods = new();
public void Inject(object dependency) public void Inject(object dependency)
{ {
} }
public void AppendInvokers(IEnumerable<IMethodInvoker> methodInvokers) public void AppendInvokers(IEnumerable<IMethodInvoker> methodInvokers)
@@ -21,9 +21,6 @@ namespace mROA.Implementation
if (id == -1) if (id == -1)
return MethodInvoker.Dispose; return MethodInvoker.Dispose;
if (_methods.Count <= id)
return null;
return _methods[id]; return _methods[id];
} }
} }
@@ -1,5 +1,6 @@
using System; using System;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation.Bootstrap;
using mROA.Implementation.Frontend; using mROA.Implementation.Frontend;
namespace mROA.Implementation.CommandExecution namespace mROA.Implementation.CommandExecution
+2 -1
View File
@@ -5,7 +5,8 @@ namespace mROA.Implementation
{ {
public class EventBinder<T> : IEventBinder<T> public class EventBinder<T> : IEventBinder<T>
{ {
public Action<T, IEndPointContext, IRepresentationModuleProducer, int> BindAction { get; set; } = (_, _, _, _) => { }; public Action<T, IEndPointContext, IRepresentationModuleProducer, int> BindAction { get; set; } =
(_, _, _, _) => { };
public void BindEvents(T source, IEndPointContext context, public void BindEvents(T source, IEndPointContext context,
IRepresentationModuleProducer representationModuleProducer, int index) IRepresentationModuleProducer representationModuleProducer, int index)
@@ -43,10 +43,6 @@ namespace mROA.Implementation.Frontend
{ {
ThrowIfNotInjected(); ThrowIfNotInjected();
try
{
var streamTokenSource = new CancellationTokenSource(); var streamTokenSource = new CancellationTokenSource();
var query = _representationModule!.GetStream(m => var query = _representationModule!.GetStream(m =>
@@ -79,10 +75,6 @@ namespace mROA.Implementation.Frontend
} }
} }
} }
catch
{
}
}
private void ThrowIfNotInjected() private void ThrowIfNotInjected()
{ {
@@ -13,6 +13,7 @@ namespace mROA.Implementation.Frontend
private IChannelInteractionModule _channelInteractionModule; private IChannelInteractionModule _channelInteractionModule;
private CancellationTokenSource _tokenSource = new(); private CancellationTokenSource _tokenSource = new();
private IEndPointContext _context; private IEndPointContext _context;
public void Dispose() public void Dispose()
{ {
_tokenSource.Cancel(); _tokenSource.Cancel();
@@ -62,7 +63,6 @@ namespace mROA.Implementation.Frontend
var serialized = _serializationToolkit.Serialize(post, _context); var serialized = _serializationToolkit.Serialize(post, _context);
await udpClient.SendAsync(serialized, serialized.Length); await udpClient.SendAsync(serialized, serialized.Length);
} }
} }
+1
View File
@@ -13,6 +13,7 @@ namespace mROA.Implementation
{ {
Id = 0; Id = 0;
} }
public ClientRecovery(int id) public ClientRecovery(int id)
{ {
Id = id; Id = id;
@@ -21,10 +21,12 @@ namespace mROA.Implementation
} }
public static readonly NetworkMessageHeader Null = new(); public static readonly NetworkMessageHeader Null = new();
public NetworkMessageHeader() public NetworkMessageHeader()
{ {
Data = Array.Empty<byte>(); Data = Array.Empty<byte>();
} }
public NetworkMessageHeader(IContextualSerializationToolKit serializationToolkit, public NetworkMessageHeader(IContextualSerializationToolKit serializationToolkit,
INetworkMessage networkMessage, IEndPointContext? context) INetworkMessage networkMessage, IEndPointContext? context)
{ {
@@ -32,6 +34,7 @@ namespace mROA.Implementation
Data = serializationToolkit.Serialize(networkMessage, context); Data = serializationToolkit.Serialize(networkMessage, context);
Id = Guid.NewGuid(); Id = Guid.NewGuid();
} }
public Guid Id { get; set; } public Guid Id { get; set; }
public EMessageType MessageType { get; set; } public EMessageType MessageType { get; set; }
@@ -32,7 +32,8 @@ namespace mROA.Implementation
if (_representationProducer == null) if (_representationProducer == null)
throw new NullReferenceException("representation producer is not initialized"); throw new NullReferenceException("representation producer is not initialized");
if (!_callIndexProvider.Activators.TryGetValue(typeof(T), out var remoteType)) throw new NotSupportedException(); if (!_callIndexProvider.Activators.TryGetValue(typeof(T), out var remoteType))
throw new NotSupportedException();
var representationModule = var representationModule =
_representationProducer.Produce(context.OwnerId); _representationProducer.Produce(context.OwnerId);
var remote = remoteType(id.ContextId, var remote = remoteType(id.ContextId,
@@ -56,7 +57,8 @@ namespace mROA.Implementation
var representationModule = var representationModule =
_representationProducer.Produce(context.OwnerId); _representationProducer.Produce(context.OwnerId);
var instance = _callIndexProvider.Activators[type](-1, representationModule, context, _callIndexProvider.GetIndices(type))!; var instance = _callIndexProvider.Activators[type](-1, representationModule, context,
_callIndexProvider.GetIndices(type))!;
var remoteObjectBase = instance; var remoteObjectBase = instance;
+2 -2
View File
@@ -32,7 +32,7 @@ namespace mROA.Implementation
private readonly ComplexObjectIdentifier _identifier; private readonly ComplexObjectIdentifier _identifier;
private readonly IRepresentationModule _representationModule; private readonly IRepresentationModule _representationModule;
protected readonly int[] _callIndices; protected readonly int[] CallIndices;
protected RemoteObjectBase(int id, IRepresentationModule representationModule, IEndPointContext context, protected RemoteObjectBase(int id, IRepresentationModule representationModule, IEndPointContext context,
int[] indices) int[] indices)
@@ -40,7 +40,7 @@ namespace mROA.Implementation
_identifier = new ComplexObjectIdentifier { ContextId = id, OwnerId = representationModule.Id }; _identifier = new ComplexObjectIdentifier { ContextId = id, OwnerId = representationModule.Id };
_representationModule = representationModule; _representationModule = representationModule;
_context = context; _context = context;
_callIndices = indices; CallIndices = indices;
} }
public int Id => _identifier.ContextId; public int Id => _identifier.ContextId;
+4 -2
View File
@@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Abstract; using mROA.Abstract;
#pragma warning disable CS8602 // Dereference of a possibly null reference. #pragma warning disable CS8602 // Dereference of a possibly null reference.
namespace mROA.Implementation namespace mROA.Implementation
@@ -75,7 +76,7 @@ namespace mROA.Implementation
for (int i = 0; i < converter.Length; i++) for (int i = 0; i < converter.Length; i++)
{ {
var func = converter[i]; var func = converter[i];
if (func(message) is {} t) if (func(message) is { } t)
{ {
var deserialized = _serialization.Deserialize(message.Data, t, context); var deserialized = _serialization.Deserialize(message.Data, t, context);
yield return (deserialized, message.MessageType)!; yield return (deserialized, message.MessageType)!;
@@ -85,7 +86,8 @@ namespace mROA.Implementation
} }
} }
public async Task PostCallMessageAsync<T>(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) where T : notnull public async Task PostCallMessageAsync<T>(Guid id, EMessageType eMessageType, T payload,
IEndPointContext? context) where T : notnull
{ {
if (_interaction == null) if (_interaction == null)
throw new NullReferenceException("Interaction toolkit is not initialized"); throw new NullReferenceException("Interaction toolkit is not initialized");
+3 -1
View File
@@ -60,7 +60,9 @@ namespace mROA.Implementation
} }
} }
[SerializationIgnore] [JsonIgnore] public IEndPointContext EndPointContext { get; set; } [SerializationIgnore]
[JsonIgnore]
public IEndPointContext EndPointContext { get; set; }
public ComplexObjectIdentifier Identifier public ComplexObjectIdentifier Identifier
{ {
+2 -2
View File
@@ -26,8 +26,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.5" /> <PackageReference Include="System.Text.Json" Version="9.0.5"/>
<PackageReference Include="System.Threading.Channels" Version="9.0.5" /> <PackageReference Include="System.Threading.Channels" Version="9.0.5"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>