{"id":39796,"date":"2022-05-10T12:08:35","date_gmt":"2022-05-10T19:08:35","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=39796"},"modified":"2022-05-24T08:43:02","modified_gmt":"2022-05-24T15:43:02","slug":"announcing-dotnet-7-preview-4","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-7-preview-4\/","title":{"rendered":"Announcing .NET 7 Preview 4"},"content":{"rendered":"<p>Today we released .NET 7 Preview 4. The fourth preview of .NET 7 includes enhancements to observability in the .NET implementation of OpenTelemetry, the addition of properties to track microseconds and nanoseconds in date and time structures, new metrics for caching extensions, performance-boosting &#8220;on stack replacement,&#8221; APIs to work with <code>.tar<\/code> archives, and additional features as part of an ongoing effort to improve the performance of and add features to regular expressions in .NET 7. The preview 4 releases for <a href=\"\/dotnet\/asp-net-core-updates-in-dotnet-7-preview-4\/\">ASP.NET Core<\/a> and <a href=\"\/dotnet\/announcing-entity-framework-7-preview-4\">EF7<\/a> are also available.<\/p>\n<p>You can <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/7.0\">download .NET 7 Preview 4<\/a>, for Windows, macOS, and Linux.<\/p>\n<ul>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/7.0\">Installers and binaries<\/a><\/li>\n<li><a href=\"https:\/\/hub.docker.com\/_\/microsoft-dotnet\">Container images<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/master\/release-notes\/7.0\/\">Linux packages<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/tree\/master\/release-notes\/7.0\">Release notes<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/7.0\/known-issues.md\">Known issues<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/issues\">GitHub issue tracker<\/a><\/li>\n<\/ul>\n<p>.NET 7 Preview 4 has been tested with Visual Studio 17.3 Preview 1. We recommend you use the <a href=\"https:\/\/visualstudio.com\/preview\">preview channel builds<\/a> if you want to try .NET 7 with Visual Studio family products. Visual Studio for Mac support for .NET 7 previews isn\u2019t available yet but is coming soon. Now, let&#8217;s get into some of the latest updates in this release.<\/p>\n<h2>.NET Libraries: Nullable annotations for Microsoft.Extensions<\/h2>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43605\">#43605<\/a><\/p>\n<p>We have finished annotating the <code>Microsoft.Extensions.*<\/code> libraries for nullability. In .NET 7 Preview 4, all <code>Microsoft.Extensions.*<\/code> libraries have been fully annotated for nullability.<\/p>\n<p>This work wouldn&#8217;t have been possible without <a href=\"https:\/\/github.com\/maxkoshevoi\">@maxkoshevoi<\/a>&#8216;s multiple-month effort. Starting with the first PR in August 2021, all the way to the final PR in April 2022, this was a lot of work that is greatly appreciated by the .NET community.<\/p>\n<blockquote><p><em>It\u2019s a really great feeling to know that if you don\u2019t like something about libraries you are using (or even the framework itself), you can go and improve it yourself.<\/em> &#8211; Maksym<\/p><\/blockquote>\n<h3>Contributor spotlight: Maksym Koshovyi<\/h3>\n<p>Microsoft&#8217;s Eric Erhardt reached out to Maksym to thank him for his contributions and learn more about his passion for open source software and .NET. We are grateful for his contributions and would like to highlight his story. In Maksym&#8217;s own words:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi.jpg\"><img decoding=\"async\" class=\"alignleft size-medium wp-image-39791\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi-224x300.jpg\" alt=\"Image mkoshovyi jpg\" width=\"224\" height=\"300\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi-224x300.jpg 224w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi-763x1024.jpg 763w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi-768x1030.jpg 768w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi-1145x1536.jpg 1145w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/mkoshovyi.jpg 1525w\" sizes=\"(max-width: 224px) 100vw, 224px\" \/><\/a><\/p>\n<p>My name is Maksym Koshovyi, and I am from Kharkiv, Ukraine.<\/p>\n<p>I&#8217;ve loved programming since school and been doing it for over 8 years (past 4 even got paid to do it \ud83d\ude04). Learned C# early on and never switched. Such a great language, framework and community! Always loved how easy it is to throw together a WinForms app in a couple of hours, or an API, or even a mobile app (without ever switching to Java or Swift). Also always enjoyed the language itself and the tooling around it that makes it easy to fall into the pit of success.<\/p>\n<p>Ironically, my first contribution ever was a <a href=\"https:\/\/github.com\/dotnet\/roslyn-analyzers\/pull\/2717\">Roslyn analyzer<\/a>. I got so used to being warned if I do stupid things, that when it didn&#8217;t happen I&#8217;ve dedicated a day to fixing it. Since then I&#8217;ve made many small contributions here or there. Two of the biggest ones for now are in <code>XamarinCommunityToolkit<\/code> and runtime. It&#8217;s a really great feeling to know that if you don&#8217;t like something about libraries you are using (or even the framework itself), you can go and improve it yourself. Guess it&#8217;s something similar to driving a car you know how to disassemble and fix yourself.<\/p>\n<p>What&#8217;s also great about contributing is that you learn so much! All projects are different, and having more perspectives on how things could be done always helps. I always find myself implementing something I&#8217;ve found on GitHub in my pet projects. Another thing is feeling that your contribution will make someone else&#8217;s life a bit easier. Always feels good thinking about it.<\/p>\n<p>But none of this would be possible without the team that encourages contributions, discusses the issues and helps with PR reviews. Thank you so much for creating such a welcoming and open environment!<\/p>\n<p><em>You&#8217;re very welcome, Maksym! Thank YOU!<\/em> &#8211; .NET Team<\/p>\n<h2>Observability<\/h2>\n<p>The following enhancements were made as part of the ongoing effort to support observability in .NET 7 via <a href=\"https:\/\/opentelemetry.io\/\">OpenTelemetry<\/a>.<\/p>\n<h3>Introducing Activity.Current change event<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/67276\">#67276<\/a><\/p>\n<p>A typical implementation of distributed tracing uses an <code>AsyncLocal&lt;T&gt;<\/code> to track the &#8220;span context&#8221; of managed threads. Changes to the span context are tracked by using the <code>AsyncLocal&lt;T&gt;<\/code> constructor that takes the <code>valueChangedHandler<\/code> parameter. However, with <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.diagnostics.activity\"><code>Activity<\/code><\/a> becoming the standard to represent spans, as used by OpenTelemetry, it is impossible to set the value changed handler since the context is tracked <code>via Activity.Current<\/code>. The new change event can be used instead to receive the desired notifications.<\/p>\n<pre><code class=\"language-C#\">    public partial class Activity : IDisposable\r\n    {\r\n        public static event EventHandler&lt;ActivityChangedEventArgs&gt;? CurrentChanged;\r\n    }<\/code><\/pre>\n<h4>Usage Example<\/h4>\n<pre><code class=\"language-C#\">    Activity.CurrentChanged += CurrentChanged;\r\n\r\n    void CurrentChanged(object? sender, ActivityChangedEventArgs e)\r\n    {\r\n        Console.WriteLine($\"Activity.Current value changed from Activity: {e.Previous.OperationName} to Activity: {e.Current.OperationName}\");\r\n    }\r\n<\/code><\/pre>\n<h3>Expose performant Activity properties enumerator methods<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/67207\">#67207<\/a><\/p>\n<p>The exposed methods can be used in the performance-critical scenarios to enumerate the Activity Tags, Links, and Events properties without any extra allocations and with fast items access.<\/p>\n<pre><code class=\"language-C#\">namespace System.Diagnostics\r\n{\r\n    partial class Activity\r\n    {\r\n        public Enumerator&lt;KeyValuePair&lt;string,object&gt;&gt; EnumerateTagObjects();\r\n        public Enumerator&lt;ActivityLink&gt; EnumerateLinks();\r\n        public Enumerator&lt;ActivityEvent&gt; EnumerateEvents();\r\n\r\n        public struct Enumerator&lt;T&gt;\r\n        {\r\n            public readonly Enumerator&lt;T&gt; GetEnumerator();\r\n            public readonly ref T Current;\r\n            public bool MoveNext();\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h4>Usage Example<\/h4>\n<pre><code class=\"language-C#\">    Activity a = new Activity(\"Root\");\r\n\r\n    a.SetTag(\"key1\", \"value1\");\r\n    a.SetTag(\"key2\", \"value2\");\r\n\r\n    foreach (ref readonly KeyValuePair&lt;string, object?&gt; tag in a.EnumerateTagObjects())\r\n    {\r\n        Console.WriteLine($\"{tag.Key}, {tag.Value}\");\r\n    }<\/code><\/pre>\n<h2>Adding Microseconds and Nanoseconds to TimeStamp, DateTime, DateTimeOffset, and TimeOnly<\/h2>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/23799\">#23799<\/a><\/p>\n<p>Prior to Preview 4, the lowest increment of time available in the various date and time structures was the &#8220;tick&#8221; available in the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.datetime.ticks\">Ticks<\/a> property. In .NET, a single tick is 100ns. Developers traditionally have had to perform computations on the &#8220;tick&#8221; value to determine microsecond and nanosecond values. Preview 4 addresses that by introducing both microseconds and milliseconds to the date and time implementations. Here is the new API surface area:<\/p>\n<pre><code class=\"language-C#\">namespace System {\r\n    public struct DateTime {\r\n        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond);\r\n        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.DateTimeKind kind);\r\n        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar);\r\n        public int Microsecond { get; }\r\n        public int Nanosecond { get; }\r\n        public DateTime AddMicroseconds(double value);\r\n    }\r\n    public struct DateTimeOffset {\r\n        public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset);\r\n        public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset, System.Globalization.Calendar calendar);\r\n        public int Microsecond { get; }\r\n        public int Nanosecond { get; }\r\n        public DateTimeOffset AddMicroseconds(double microseconds);\r\n    }\r\n    public struct TimeSpan {\r\n        public const long TicksPerMicrosecond = 10L;\r\n        public const long NanosecondsPerTick = 100L;\r\n        public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds);\r\n        public int Microseconds { get; }\r\n        public int Nanoseconds { get; }\r\n        public double TotalMicroseconds { get; }\r\n        public double TotalNanoseconds { get; }\r\n        public static TimeSpan FromMicroseconds(double microseconds);\r\n    }\r\n    public struct TimeOnly {\r\n        public TimeOnly(int hour, int minute, int second, int millisecond, int microsecond);\r\n        public int Microsecond { get; }\r\n        public int Nanosecond { get; }\r\n    }\r\n}<\/code><\/pre>\n<p>Thanks to @ChristopherHaws and @deeprobin helping in the design and implementation.<\/p>\n<h2>More improvements and new APIs for System.Text.RegularExpressions<\/h2>\n<p>For preview 4 we are adding the remaining planned APIs in order to add span support into our <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/regular-expression-improvements-in-dotnet-7\/\">Regex library<\/a>. The changes span several issues:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/59629\">Augment Regex extensibility point for better perf and span-based matching<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/65011\">[API Proposal]: Add Regex.Enumerate(ReadOnlySpan) which is allocation free<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/61048\">Overhaul Regex&#8217;s handling of RegexOptions.IgnoreCase<\/a><\/li>\n<\/ul>\n<p>These APIs are amortized allocation-free. The main span-based APIs being added for Preview 4 are:<\/p>\n<ul>\n<li><code>Regex.IsMatch(ReadOnlySpan&lt;char&gt; input)<\/code>: Indicates whether the regular expression finds a match in the input span.<\/li>\n<li><code>Regex.Count(ReadOnlySpan&lt;char&gt; input)<\/code>: Searches an input string for all occurrences of a regular expression and returns the number of matches.<\/li>\n<li><code>Regex.EnumerateMatches(ReadOnlySpan&lt;char&gt; input)<\/code>: Searches an input span for all occurrences of a regular expression and returns a ValueMatchEnumerator to lazily iterate over the matches.<\/li>\n<\/ul>\n<p>We have also done a lot of work that enhances Regex performance in general, with improvements like:<\/p>\n<ul>\n<li>Improving the handling of more common Regex sets<\/li>\n<li>Improving the performance in the logic that finds possible positions where a match could exist.<\/li>\n<li>Use spans in some of our internal types to avoid allocations where possible, which makes the engine go faster.<\/li>\n<li>Improve the logic for when a loop can be made atomic.<\/li>\n<\/ul>\n<p>Finally, we have also made some improvements on the code that is generated by the Regex source generator to make the code more readable and easier to debug, as well as enable projects with multiple source generated regex patterns to share common code between them.<\/p>\n<h2>Added metrics for <code>Microsoft.Extensions.Caching<\/code><\/h2>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/50406\">#50406<\/a><\/p>\n<p>For preview 4 we added metrics support for <code>IMemoryCache<\/code>. The main APIs being added for Preview 4 are:<\/p>\n<ul>\n<li><code>MemoryCacheStatistics<\/code> which holds cache hit\/miss\/estimated size and count for <code>IMemoryCache<\/code><\/li>\n<li><code>GetCurrentStatistics<\/code>: returns an instance of <code>MemoryCacheStatistics<\/code>, or null when <code>TrackStatistics<\/code> flag is not enabled. The library has a built-in implementation available for <code>MemoryCache<\/code>.<\/li>\n<\/ul>\n<p><code>GetCurrentStatistics()<\/code> API allows app developers to use either event counters or metrics APIs to track statistics for one or more memory cache. Note, using these APIs for getting statistics for multiple caches is possible but requires developers to write their own meter.<\/p>\n<p>Issue <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/67770\">#67770<\/a> tracks adding built-in meters into the library to help improve developer experience with monitoring caches.<\/p>\n<h3>How to use the APIs<\/h3>\n<p><code>GetCurrentStatistics()<\/code> API (based on <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/50406\">#50406<\/a>) allows app developers to use either event counters or metrics APIs to track statistics for one or more memory caches with code snippets.<\/p>\n<p>With <code>IMemoryCache.GetCurrentStatistics()<\/code>, the user now has support for the following use cases:<\/p>\n<ul>\n<li>One cache with either event counters or metrics APIs<\/li>\n<li>Multiple caches with metrics API<\/li>\n<\/ul>\n<h3>Using <code>IMemoryCache.GetCurrentStatistics()<\/code> for one memory cache<\/h3>\n<p>Use the <a href=\"https:\/\/apisof.net\/catalog\/3cb04c03-2845-725f-eebb-1eb7ba5d0515\"><code>AddMemoryCache<\/code><\/a> API to instantiate a single memory cache and via DI get it injected to enable them calling <code>GetCurrentStatistics<\/code>.<\/p>\n<h4>Sample usage\/screenshot for event counter:<\/h4>\n<pre><code class=\"language-c#\">\/\/ when using `services.AddMemoryCache(options =&gt; options.TrackStatistics = true);` to instantiate\r\n\r\n    [EventSource(Name = \"Microsoft-Extensions-Caching-Memory\")]\r\n    internal sealed class CachingEventSource : EventSource\r\n    {\r\n        public CachingEventSource(IMemoryCache memoryCache) { _memoryCache = memoryCache; }\r\n        protected override void OnEventCommand(EventCommandEventArgs command)\r\n        {\r\n            if (command.Command == EventCommand.Enable)\r\n            {\r\n                if (_cacheHitsCounter == null)\r\n                {\r\n                    _cacheHitsCounter = new PollingCounter(\"cache-hits\", this, () =&gt;\r\n                        _memoryCache.GetCurrentStatistics().CacheHits)\r\n                    {\r\n                        DisplayName = \"Cache hits\",\r\n                    };\r\n                }\r\n            }\r\n        }\r\n    }<\/code><\/pre>\n<p>Helps them view stats below with <code>dotnet-counters<\/code> tool:<\/p>\n<pre><code>Press p to pause, r to resume, q to quit.\r\n    Status: Running\r\n\r\n[System.Runtime]\r\n    CPU Usage (%)                                      0\r\n    Working Set (MB)                                  28\r\n[Microsoft-Extensions-Caching-MemoryCache]\r\n    cache-hits                                       269<\/code><\/pre>\n<h3>Using <code>IMemoryCache.GetCurrentStatistics()<\/code> for multiple memory caches<\/h3>\n<p>In order to get stats for more than one memory cache in the app, the user may use metrics APIs in their own code, so long as they have a way of distinguishing their caches by name or ID:<\/p>\n<h4>sample usage\/screenshot for multiple caches using metrics APIs<\/h4>\n<pre>static\u00a0Meter\u00a0s_meter\u00a0=\u00a0new\u00a0Meter(\"Microsoft.Extensions.Caching.Memory.MemoryCache\",\u00a0\"1.0.0\");\r\nstatic\u00a0IMemoryCache?\u00a0mc1;\r\nstatic\u00a0IMemoryCache?\u00a0mc2;\r\n\r\nstatic\u00a0void\u00a0Main(string[]\u00a0args)\r\n{\r\n   s_meter.CreateObservableGauge&lt;long&gt;(\"cache-hits\",\u00a0GetCacheHits);\r\n   mc1\u00a0=\u00a0new\u00a0MemoryCache(new\u00a0MemoryCacheOptions()\u00a0{\u00a0TrackStatistics\u00a0=\u00a0true,\u00a0SizeLimit\u00a0=\u00a030\u00a0});\r\n   mc2\u00a0=\u00a0new\u00a0MemoryCache(new\u00a0MemoryCacheOptions()\u00a0{\u00a0TrackStatistics\u00a0=\u00a0true,\u00a0SizeLimit\u00a0=\u00a030\u00a0});\r\n\r\n   \/\/ call to: mc1.TryGetValue(key1,\u00a0out\u00a0object?\u00a0value)\r\n   \/\/ or: mc2.TryGetValue(key2,\u00a0out\u00a0value2)\r\n   \/\/ increments TotalHits\r\n}\r\n\r\n\/\/\u00a0metrics\u00a0callback\u00a0for\u00a0cache\u00a0hits\r\nstatic\u00a0IEnumerable&lt;Measurement&lt;long&gt;&gt;\u00a0GetCacheHits()\r\n{\r\n   return\u00a0new\u00a0Measurement&lt;long&gt;[]\r\n   {\r\n      new\u00a0Measurement&lt;long&gt;(mc1!.GetCurrentStatistics()!.TotalHits,\u00a0new\u00a0KeyValuePair&lt;string,object?&gt;(\"CacheName\",\u00a0\"mc1\")),\r\n      new\u00a0Measurement&lt;long&gt;(mc2!.GetCurrentStatistics()!.TotalHits,\u00a0new\u00a0KeyValuePair&lt;string,object?&gt;(\"CacheName\",\u00a0\"mc2\")),\r\n   };\r\n}<\/pre>\n<p>Sample stats with <code>dotnet-counters<\/code> tool:<\/p>\n<pre><code>Press p to pause, r to resume, q to quit.\r\n    Status: Running\r\n\r\n[System.Runtime]\r\n    CPU Usage (%)                                      0\r\n    Working Set (MB)                                  14\r\n[Microsoft.Extensions.Caching.Memory.MemoryCache]\r\n    cache-hits\r\n        CacheName=mc1                             13,204\r\n        CacheName=mc2                             13,204<\/code><\/pre>\n<p>Each metrics would need to create its own observable gauge (one for hits, then misses, etc.) and each callback function for the gauge iterates through list of caches creating measurements.<\/p>\n<h2>Added new Tar APIs<\/h2>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67883\">Implement Tar APIs &#8211; dotnet\/runtime#67883<\/a><\/p>\n<p>For Preview 4, we added the new <code>System.Formats.Tar<\/code> assembly, which contains cross-platform APIs that allow reading, writing, archiving, and extracting of Tar archives.<\/p>\n<h3>Usage examples<\/h3>\n<p>For the most common usage (extracting and archiving) the following APIs are available:<\/p>\n<pre><code class=\"language-csharp\">\/\/ Generates a tar archive where all the entry names are prefixed by the root directory 'SourceDirectory'\r\nTarFile.CreateFromDirectory(sourceDirectoryName: \"\/home\/dotnet\/SourceDirectory\/\", destinationFileName: \"\/home\/dotnet\/destination.tar\", includeBaseDirectory: true);\r\n\r\n\/\/ Extracts the contents of a tar archive into the specified directory, but avoids overwriting anything found inside\r\nTarFile.ExtractToDirectory(sourceFileName: \"\/home\/dotnet\/destination.tar\", destinationDirectoryName: \"\/home\/dotnet\/DestinationDirectory\/\", overwriteFiles: false);<\/code><\/pre>\n<p>We also offer variants that allow extracting from a stream or archiving into a stream:<\/p>\n<pre><code class=\"language-csharp\">\/\/ Generates a tar archive where all the entry names are prefixed by the root directory 'SourceDirectory'\r\nusing MemoryStream archiveStream = new();\r\nTarFile.CreateFromDirectory(sourceDirectoryName: @\"D:SourceDirectory\", destination: archiveStream, includeBaseDirectory: true);\r\n\r\n\/\/ Extracts the contents of a stream tar archive into the specified directory, and avoids overwriting anything found inside\r\nTarFile.ExtractToDirectory(source: archiveStream, destinationDirectoryName: @\"D:DestinationDirectory\", overwriteFiles: false);<\/code><\/pre>\n<p>Additionally, the entries of an archive can be traversed one-by-one using the reader:<\/p>\n<pre><code class=\"language-csharp\">\/\/ Opens an archive for reading individual entries, and closes the archive stream after the reader disposal\r\nusing FileStream archiveStream = File.OpenRead(\"\/home\/dotnet\/SourceDirectory\/source.tar\");\r\nusing TarReader reader = new(archiveStream, leaveOpen: false);\r\nTarEntry? entry;\r\nwhile ((entry = reader.GetNextEntry()) != null)\r\n{\r\n    \/\/ Extracts an entry to the desired destination, and overwrites if an entry with the same name is found\r\n    string destinationFileName = Path.Join(\"\/home\/dotnet\/DestinationDirectory\", entry.Name);\r\n    entry.ExtractToFile(destinationFileName, overwrite: true);\r\n}<\/code><\/pre>\n<p>And entries can also be written one-by-one into an archive stream using the writer:<\/p>\n<pre><code class=\"language-csharp\">using FileStream archiveStream = File.OpenWrite(@\"D:DestinationDirectorydestination.tar\");\r\n\/\/ Create the archive in the PAX format and write individual entries, closing the stream after the writer disposal\r\nusing TarWriter writer = new(archiveStream, TarFormat.Pax, leaveOpen: false);\r\n\r\n\/\/ Add an entry from an existing file\r\nwriter.WriteEntry(fileName: @\"D:SourceDirectoryfile.txt\", entryName: \"file.txt\");\r\n\r\n\/\/ Or write an entry from scratch\r\nPaxTarEntry entry = new(entryType: TarEntryType.Directory, entryName: \"directory\");\r\nwriter.WriteEntry(entry);<\/code><\/pre>\n<p>We can also pair these new APIs with stream-based compression methods, like <code>System.IO.Compression.GZipStream<\/code>.<\/p>\n<p>Extracting the contents from a compressed tar archive into the filesystem looks like this:<\/p>\n<pre><code class=\"language-cssharp\">using FileStream compressedStream = File.OpenRead(\"\/home\/dotnet\/SourceDirectory\/compressed.tar.gz\");\r\nusing GZipStream decompressor = new(compressedStream, CompressionMode.Decompress);\r\nTarFile.ExtractToDirectory(source: decompressor, destinationDirectoryName: \"\/home\/dotnet\/DestinationDirectory\/\", overwriteFiles: false);<\/code><\/pre>\n<p>Reading individual entries from a compressed tar archive:<\/p>\n<pre><code class=\"language-csharp\">using FileStream compressedStream = File.OpenRead(@\"D:SourceDirectorycompressed.tar.gz\");\r\nusing GZipStream decompressor = new(compressedStream, CompressionMode.Decompress);\r\nusing TarReader reader = new(decompressor, leaveOpen: true);\r\nTarEntry? entry;\r\n\r\nwhile ((entry = GetNextEntry(copyData: true)) != null)\r\n{\r\n    Console.WriteLine($\"Entry type: {entry.EntryType}, entry name: {entry.Name}\");\r\n}<\/code><\/pre>\n<p>Writing the contents of a directory into a compressed tar archive:<\/p>\n<pre><code class=\"language-csharp\">using MemoryStream archiveStream = new();\r\nTarFile.CreateFromDirectory(sourceDirectoryName: \"\/home\/dotnet\/SourceDirectory\/\", destination: archiveStream, includeBaseDirectory: true);\r\nusing FileStream compressedStream = File.Create(\"\/home\/dotnet\/DestinationDirectory\/compressed.tar.gz\");\r\nusing GZipStream compressor = new(compressedStream, CompressionMode.Compress);\r\narchiveStream.Seek(0, SeekOrigin.Begin);\r\narchiveStream.CopyTo(compressor);<\/code><\/pre>\n<p>Writing individual entries into a compressed tar archive:<\/p>\n<pre><code class=\"language-csharp\">using MemoryStream archiveStream = new();\r\nusing (TarWriter writer = new TarWriter(archiveStream, TarFormat.Pax, leaveOpen: true))\r\n{\r\n    \/\/ Add an entry from an existing file\r\n    writer.WriteEntry(fileName: @\"D:SourceDirectoryfile.txt\", entryName: \"file.txt\");\r\n\r\n    \/\/ Or write an entry from scratch\r\n    PaxTarEntry entry = new(entryType: TarEntryType.Directory, entryName: \"directory\");\r\n    writer.WriteEntry(entry);\r\n}\r\n\r\nusing FileStream compressedStream = File.Create(@\"D:DestinationDirectorycompressed.tar.gz\");\r\nusing GZipStream compressor = new(compressedStream, CompressionMode.Compress);\r\narchiveStream.Seek(0, SeekOrigin.Begin);\r\narchiveStream.CopyTo(compressor);<\/code><\/pre>\n<h3>Notes<\/h3>\n<ul>\n<li>We are not yet including any async APIs. They will be included in a future preview release.<\/li>\n<li>In the Ustar, PAX and GNU formats, we do not yet support the <code>UName<\/code>, <code>GName<\/code>, <code>DevMajor<\/code> and <code>DevMinor<\/code> fields.<\/li>\n<li>In the GNU format, we won&#8217;t support the rare entry types for writing, like sparse files, multi-volume, renamed or symlinked, tape volume.<\/li>\n<\/ul>\n<h2>What&#8217;s new in CodeGen<\/h2>\n<p>Many thanks to the JIT community contributors who submitted these important PRs:<\/p>\n<p>@sandreenko: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/66983\">66983<\/a>; @SingleAccretion: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/66558\">66558<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/66251\">66251<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/65803\">65803<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/64581\">64581<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67213\">67213<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67208\">67208<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67206\">67206<\/a>,<a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67486\">67486<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67400\">67400<\/a>; @SkiFoD: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/65072\">65072<\/a>.<\/p>\n<p>Other updates to CodeGen include dynamic PGO: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/65922\">65922<\/a>, Arm64: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/66621\">66621<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/66407\">66407<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/66902\">66902<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67490\">67490<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67384\">67384<\/a>; loop optimizations: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/68105\">68105<\/a>; and general optimizations: <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67205\">67205<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/67395\">67395<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/68055\">68055<\/a>.<\/p>\n<h2>On Stack Replacement (aka OSR)<\/h2>\n<p>On Stack Replacement allows the runtime to change the code executed by currently running methods in the middle of method execution, while those methods are active &#8220;on stack.&#8221; It serves as a complement to tiered compilation.<\/p>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/65675\">#65675<\/a> enabled OSR by default on x64 and Arm64, and enabled quick jitting for methods with loops on those same platforms.<\/p>\n<p>OSR allows long-running methods to switch to more optimized versions mid-execution, so the runtime can jit all methods quickly at first and then transition to more optimized versions when those methods are called frequently (via tiered compilation) or have long-running loops (via OSR).<\/p>\n<h3>Performance Impact<\/h3>\n<p>OSR improves startup time. Almost all methods are now initially jitted by the quick jit. We have seen 25% improvement in startup time in jitting-heavy applications like Avalonia \u201cIL\u201d spy, and the various TechEmpower benchmarks we track show 10-30% improvements in time to first request (see chart below: OSR was enabled by default on March 30).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/166267802-37af5fca-527e-452a-8388-000f7222dd79.png\" alt=\"Performance improvements\" \/><\/p>\n<p>OSR can also improve performance of applications, and in particular, applications using Dynamic PGO, as methods with loops are now better optimized. For example, the <code>Array2<\/code> microbenchmark showed dramatic improvement when OSR was enabled.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/05\/166268309-420349d9-ed6a-4b5f-b4ae-fa2c664e4b50.png\" alt=\"OSR improvements in microbenchmark\" \/><\/p>\n<h3>Further technical details<\/h3>\n<p>See the <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/main\/docs\/design\/features\/OnStackReplacement.md\">OSR Design Document<\/a> for details on how OSR works.<\/p>\n<p>See <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/33658\">OSR Next Steps<\/a> for details on the work that went into enabling OSR, and possible future enhancements.<\/p>\n<h2>Central Package Management<\/h2>\n<p>Dependency management is a core feature of NuGet. Managing dependencies for a single project can be easy. Managing dependencies for multi-project solutions can prove to be difficult as they start to scale in size and complexity. In situations where you manage common dependencies for many different projects, you can leverage NuGet\u2019s new <a href=\"https:\/\/docs.microsoft.com\/nuget\/consume-packages\/Central-Package-Management\">central package management features<\/a> to do all of this from the ease of a single location.<\/p>\n<h2>Targeting .NET 7<\/h2>\n<p>To target .NET 7, you need to use a .NET 7 Target Framework Moniker (TFM) in your project file. For example:<\/p>\n<pre><code class=\"language-xml\">&lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;<\/code><\/pre>\n<p>The full set of .NET 7 TFMs, including operating-specific ones follows.<\/p>\n<ul>\n<li><code>net7.0<\/code><\/li>\n<li><code>net7.0-windows<\/code><\/li>\n<\/ul>\n<p>We expect that upgrading from .NET 6 to .NET 7 should be straightforward. Please report any breaking changes that you discover in the process of testing existing apps with .NET 7.<\/p>\n<h2>Support<\/h2>\n<p>.NET 7 is a <strong>Current<\/strong> release, meaning it will receive free support and patches for 18 months from the release date. It&#8217;s important to note that the quality of all releases is the same. The only difference is the length of support. For more about .NET support policies, see the <a href=\"https:\/\/dotnet.microsoft.com\/platform\/support\/policy\/dotnet-core\">.NET and .NET Core official support policy<\/a>.<\/p>\n<h2>Breaking changes<\/h2>\n<p>You can find the most recent list of breaking changes in .NET 7 by reading the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/7.0\">Breaking changes in .NET 7<\/a> document. It lists breaking changes by area and release with links to detailed explanations.<\/p>\n<p>To see what breaking changes are proposed but still under review, follow the <a href=\"https:\/\/github.com\/dotnet\/core\/issues\/7131\">Proposed .NET Breaking Changes GitHub issue<\/a>.<\/p>\n<h2>Roadmaps<\/h2>\n<p>Releases of .NET include products, libraries, runtime, and tooling, and represent a collaboration across multiple teams inside and outside Microsoft. You can learn more about these areas by reading the product roadmaps:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/39504\">ASP.NET Core 7 and Blazor Roadmap<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/ef\/core\/what-is-new\/ef-core-7.0\/plan\">EF 7 Roadmap<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/machinelearning\/blob\/main\/ROADMAP.md\">ML.NET<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/maui\/wiki\/Roadmap\">.NET MAUI<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/winforms\/blob\/main\/docs\/roadmap.md\">WinForms<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/wpf\/blob\/main\/roadmap.md\">WPF<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/NuGet\/Home\/issues\/11571\">NuGet<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/roslyn\/blob\/main\/docs\/Language%20Feature%20Status.md\">Roslyn<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/roadmap.md\">Runtime<\/a><\/li>\n<\/ul>\n<h2>Closing<\/h2>\n<p>We appreciate and <a href=\"https:\/\/dotnet.microsoft.com\/thanks\">thank you<\/a> for all your support and contributions to .NET. Please <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/7.0\">give .NET 7 Preview 4 a try<\/a> and tell us what you think!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 7 Preview 4 is now available with enhancements to the OpenTelemetry implementation, date and time, cache metrics, on stack replacement (OSR) and regular expressions.<\/p>\n","protected":false},"author":368,"featured_media":39797,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[4,30,7265,51],"class_list":["post-39796","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-net","tag-announcement","tag-announcements","tag-community"],"acf":[],"blog_post_summary":"<p>.NET 7 Preview 4 is now available with enhancements to the OpenTelemetry implementation, date and time, cache metrics, on stack replacement (OSR) and regular expressions.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/39796","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/368"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=39796"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/39796\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/39797"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=39796"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=39796"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=39796"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}