Migration from 5.x
.NEXT 6.x requires .NET 10 SDK and C# 14 language features. C# 14 introduces extension members feature that changes the .NEXT API shape significantly. Now, the extensions can be expressed as extension members (properties and methods) for the existing classes.
Core library
The following obsolete members are removed:
ConcurrentCache,CacheEvictionPolicy. Replaced withRandomAccessCache<TKey, TValue>fromDotNext.ThreadinglibraryDotNext.Buffers.Text.Hexstatic class. Use Convert class for fast HEX conversionAsyncEnumerablehelpers removed in favor of AsyncEnumerable class
User Data
GetUserData extension method is refactored to UserData extension property:
UserData data = obj.GetUserData(); // 5.x
UserData data = obj.UserData; // 6.x
Array Extensions
IsNullOrEmpty extension method is converted to static extension method to provide a symmetry with string.IsNullOrEmpty static method:
int[] array;
bool isNull = array.IsNullOrEmpty(); // 5.x
bool isNull = Array.IsNullOrEmpty(array); // 6.x
Optional and Result types
Optional.IsOptional and Result.IsResult extension methods are converted to extension properties:
bool isOpt = typeof(Optional<int>).IsOptional(); // 5.x
bool isOpt = typeof(Optional<int>).IsOptional; // 6.x
Random Extensions
NextString is no longer availble in favor of GetString method. Also, extension methods for RandomNumberGenerator instance are converted to static extension methods:
int rnd = RandomNumberGenerator.Create().Next(); // 5.x
int rnd = RandomNumberGenerator.Next(); // 6.x
Delegate Helpers
Converter, Func, Predicate static classes are merge with DelegateHelpers static class. For instance, Func.Constant is a static extension method for Func<T> delegate type:
Func<int> f = Func.Constant<int>(42); // 5.x
Func<int> f = Func<int>.Constant(42); // 6.x
The same true for methods in Predicate static class:
Predicate<string> notNull = Predicate.IsNotNull<string>(); // 5.x
Predicate<string> notNull = Predicate<string>.IsNotNull; // 6.x
DelegateHelpers.CreateDelegate overloads are renamed to FromPointer and marked as static extension methods for appropriate delegate types.
Func<int> d = DelegateHelpers.CreateDelegate<int>(methodPtr); // 5.x
Func<int> d = Func<int>.FromPointer(methodPtr); // 6.x
Span static class
void Span.CopyTo<T>(this Span<T> source, Span<T> destination, out int writtenCount) is now available as the extension operator:
Span<byte> source, destination;
int writtenCount = source >> destination;
Epoch class
Epoch class is now sealed. There is just one Enter method that doesn't provide reclamation. If you need to force a reclamation, you need to call Reclaim method on the scope returned by Enter method:
var epoch = new Epoch();
Epoch.RecycleBin bin;
using (var scope = epoch.Enter())
{
bin = scope.Reclaim(true);
}
bin.Clear();
Also, the scope returned by Enter is now async-friendly.
Intrinsics class
Members of this class are moved to DotNext.Runtime.CompilerServices.AdvancedHelpers static class, and marked as static extension methods for Unsafe and RuntimeHelpers classes.
int alignment = Intrinsics.AlignOf<int>(); // 5.x
int alignment = Unsafe.AlignOf<int>(); // 6.x
int alignment = int.Alignment; // 6.x
IsExactTypeOf is converted into a static extension method within BasicExtensions class.
Reflection
List.Indexer<T> and Dictionary.Indexer<TKey, TValue> inner classes are moved to CollectionType and DictionaryType as static extension methods:
Func<IReadOnlyList<string>, int, string> indexer = List.Indexer<string>.ReadOnly; // 5.x
Func<IReadOnlyList<string>, int, string> indexer = IReadOnlyList<string>.Indexer; // 6.x
StreamSource class
Its factory methods are converted to static extension methods for Stream class:
Stream s = StreamSource.AsStream(new byte[] { 10, 20 }); // 5.x
Stream s = Stream.Create(new byte[] { 10, 20 }); // 6.x
Atomic.Boolean type
The type is no longer needed due to presence of necessary method in Interlocked class. FalseToTrue and TrueToFalse methods are exposed as static extension methods for Interlocked class.
bool value;
Interlocked.FalseToTrue(ref value);
ServiceProviderFactory class
To override the service from the underlying IServiceProvider instance, use Override extension method rather than Create factory method which is removed. FromTuple is converted to static extension method:
IServiceProvider provider = IServiceProvider.Create<(string, int)>(("Service", 42));
LockAcquisition
All the methods are moved into Lock class.
EnumConverter
EnumConverter is replaced with DotNext.Numerics.Enum<T> type.
Range Endpoints
Enclosed() and Disclosed() extension method are replaced with extension properties:
bool r = 42.IsBetween(40.Enclosed, 43.Disclosed);
MemoryAllocator delegate
MemoryAllocator<T> delegate has extension property Default that can be used to obtain the default allocator. AllocateExactly and AllocateAtLeast don't accept nullable allocator anymore. These methods expect non-nullable allocator. It's possible to use DefaultIfNull to fix the nullability issue:
static void M(MemoryAllocator<byte>? allocator)
{
using MemoryOwner<byte> owner = allocator.DefaultIfNull.AllocateExactly(64);
// or
allocator ??= MemoryAllocator<byte>.Default;
using MemoryOwner<byte> owner = allocator.AllocateExactly(64);
}
Threading
The following obsolete members are removed:
LinkedTokenSourceFactorystatic class and its extension methods. UseCancellationTokenMultiplexerinstead.
AsyncLockAcquisition
All the methods are moved into AsyncLock class.
AsyncBridge class
AsyncBridge.WaitAnyAsync is moved to CancellationTokenMultiplexer which is now a single entry point to work with combined cancellation tokens
IndexPool value type
IndexPool is no longer a value type. It's converted to class and the limitation on the maximum number to be returned from the pool is removed. The capacity of IndexPool is customizable. If you're using it to organize custom object pool, consider BoundedObjectPool<T> class instead, which is more convenient. IndexPool remains available for special situations, when you need to know whether the object is really taken from the pool or created on-the-fly.
CancellationTokenMultiplexer class
CancellationTokenMultiplexer class is converted to value type. Also, it has new WaitAnyAsync method that utilizes internal pool of cancellation token sources.
IO
Encoding string interpolation helpers are moved to Core library, DotNext.Text namespace.
DuplexStream is no longer available, but can be construced by using PipeExtensions.AsStream extension method.
SequenceReader type
It's moved from DotNext.IO to DotNext.Buffers namespace. Also, it cannot be construced by using IAsyncBinaryReader.Create factory method. Instead, the constructor of SequenceReader must be used.
TextStreamExtensions class
Most of the factory methods exposed by TextStreamExtensions are converted to static extension methods:
ReadOnlySequence<char> sequence;
TextReader reader = sequence.AsTextReader(); // 5.x
TextReader reader = TextReader.Create(sequence); // 6.x
IAsyncBinaryReader and IAsyncBinaryWriter
IAsyncBinaryReader.Create and IAsyncBinaryWriter.Create methods are transformed into static extension methods. To call Create overload with PipeWriter parameter type, it's required to import DotNext.IO.Pipelines namespace.
Metaprogramming
Const() extension method is renamed to Quoted extension property.
Raft
MemoryBasedStateMachine and DiskBasedStateMachine are removed in favor of high-performance and flexible WriteAheadLog class as announced here. WriteAheadLog is no longer marked as experimental. These WALs are not binary-compatible. Moreover, WriteAheadLog from 5.x is not compatible with its counterpart in 6.x.
Migration from MemoryBasedStateMachine
To do that, you need to use version 5.x and write a migration of the log entries manually from the class that derives from MemoryBasedStateMachine and WriteAheadLog which is marked as experimental. Both classes implement the same IPersistentState interface. There are the steps to do a migration:
- Implement
IStateMachineto provide a storage for the snapshot. Or you can reuseSimpleStateMachineas its base implementation - Iterate over all log entries in
MemoryBasedStateMachineand append them toWriteAheadLoginstance
Now you can migrate WriteAheadLog from 5.x to 6.x by following the steps in from the next chapter.
Migration from experimental WriteAheadLog
WriteAheadLog from 5.x and 6.x are almost identical except the timestamps associated with every log entry. In 6.x, for the performance reasons, the support of timestamps are removed. Therefore, these logs are binary icompatible. However, you can opt-in the compatibility with DotNext.IO.WriteAheadLog.IgnoreTimestamp feature switch. To do that, go to CSPROJ file and add the switch:
<ItemGroup>
<RuntimeHostConfigurationOption
Include="DotNext.IO.WriteAheadLog.IgnoreTimestamp"
Value="false" />
</ItemGroup>
The feature switch forces to interpret the space on the disk related to timestamps correctly. Thus, you can use a log created by WriteAheadLog class in 5.x. For a new log, this option is not recommended. By default, the log doesn't store or parse timestamps.