{"id":34055,"date":"2021-08-23T09:06:26","date_gmt":"2021-08-23T16:06:26","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=34055"},"modified":"2021-08-23T20:39:50","modified_gmt":"2021-08-24T03:39:50","slug":"new-dotnet-6-apis-driven-by-the-developer-community","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/new-dotnet-6-apis-driven-by-the-developer-community\/","title":{"rendered":"New .NET 6 APIs driven by the developer community"},"content":{"rendered":"<p>.NET 6 is on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6-preview-7\/\">the way<\/a>, and I wanted to share some of my favorite new APIs in .NET and ASP.NET Core that you are going to love. Why are you going to love them? Well because they were directly driven by our fantastic .NET developer community! Let&#8217;s get started!<\/p>\n<h2>Reading &amp; Writing Files<\/h2>\n<p>In .NET 6, there&#8217;s a new low-level API to enable reading\/writing of files without using a FileStream. It also supports scatter\/gather IO (multiple buffers) and overlapping reads and writes at a given file offset.<\/p>\n<pre><code class=\"language-csharp\">using Microsoft.Win32.SafeHandles;\r\nusing SafeFileHandle handle = File.OpenHandle(\"ConsoleApp128.exe\");\r\nlong length = RandomAccess.GetLength(handle);\r\n\r\nConsole.WriteLine(length);<\/code><\/pre>\n<h2>Process Path &amp; Id<\/h2>\n<p>There are a couple of new ways to access a process path and process id without allocating a new process object:<\/p>\n<pre><code class=\"language-csharp\">int pid = Environment.ProcessId;\r\nstring path = Environment.ProcessPath;\r\n\r\nConsole.WriteLine(pid);\r\nConsole.WriteLine(path);<\/code><\/pre>\n<h2>CSPNG (Cryptographically Secure Pseudorandom Number Generator)<\/h2>\n<p>Generating random numbers from a CSPNG (Cryptographically Secure Pseudorandom Number Generator) is a easier than ever:<\/p>\n<pre><code class=\"language-csharp\">\/\/ Give me 200 random bytes\r\nbyte[] bytes = RandomNumberGenerator.GetBytes(200);<\/code><\/pre>\n<h2>Parallel.ForEachAsync<\/h2>\n<p>We finally added Parallel.ForEachAsync, a way to schedule asynchronous work that allows you to control the degree of parallelism:<\/p>\n<pre><code class=\"language-csharp\">var urlsToDownload = new [] \r\n{\r\n    \"https:\/\/dotnet.microsoft.com\",\r\n    \"https:\/\/www.microsoft.com\",\r\n    \"https:\/\/twitter.com\/davidfowl\"\r\n};\r\n\r\nvar client = new HttpClient();\r\n\r\nawait Parallel.ForEachAsync(urlsToDownload, async (url, token) =&gt;\r\n{\r\n    var targetPath = Path.Combine(Path.GetTempPath(), \"http_cache\", url);\r\n\r\n    HttpResponseMessage response = await client.GetAsync(url);\r\n\r\n    if (response.IsSuccessStatusCode)\r\n    {\r\n        using FileStream target = File.OpenWrite(targetPath);\r\n\r\n        await response.Content.CopyToAsync(target);\r\n    }\r\n});<\/code><\/pre>\n<h2>Configuration Helpers<\/h2>\n<p>We added a helper to make it easier to throw if a required section of configuration is missing:<\/p>\n<pre><code class=\"language-csharp\">var configuration = new ConfigurationManager();\r\nvar options = new MyOptions();\r\n\r\n\/\/ This will throw if the section isn't configured\r\nconfiguration.GetRequiredSection(\"MyOptions\").Bind(options);\r\n\r\nclass MyOptions\r\n{\r\n    public string? SettingValue { get; set;}\r\n}<\/code><\/pre>\n<h2>LINQ<\/h2>\n<p>There&#8217;s a ton of new LINQ methods as well. It got lots of love in this release. Here&#8217;s a new helper to chunk any IEnumerable into batches:<\/p>\n<pre><code class=\"language-csharp\">int chunkNumber = 1;\r\nforeach (int[] chunk in Enumerable.Range(0, 9).Chunk(3))\r\n{\r\n    Console.WriteLine($\"Chunk {chunkNumber++}\");\r\n    foreach (var item in chunk)\r\n    {\r\n        Console.WriteLine(item);\r\n    }\r\n}<\/code><\/pre>\n<h2>Even More LINQ!<\/h2>\n<p>More LINQ! There are now MaxBy and MinBy methods:<\/p>\n<pre><code class=\"language-csharp\">var people = GetPeople();\r\n\r\nvar oldest = people.MaxBy(p =&gt; p.Age);\r\nvar youngest = people.MinBy(p =&gt; p.Age);\r\n\r\nConsole.WriteLine($\"The oldest person is {oldest.Age}\");\r\nConsole.WriteLine($\"The youngest person is {youngest.Age}\");\r\n\r\npublic record Person(string Name, int Age);<\/code><\/pre>\n<h2>Power of 2<\/h2>\n<p>Don&#8217;t keep bit math in your head? Me neither. Here are some new helpers for working with powers of 2:<\/p>\n<pre><code class=\"language-csharp\">using System.Numerics;\r\n\r\nuint bufferSize = 235;\r\nif (!BitOperations.IsPow2(bufferSize))\r\n{\r\n    bufferSize = BitOperations.RoundUpToPowerOf2(bufferSize);\r\n}\r\n\r\nConsole.WriteLine(bufferSize);<\/code><\/pre>\n<h2>WaitAsync Improvements<\/h2>\n<p>There&#8217;s now a much easier (and properly implemented) way to wait for task to complete asynchronously. The following code will yield the await if it hasn&#8217;t completed in 10 seconds. The operation might still be running! This is for un-cancellable operations!<\/p>\n<pre><code class=\"language-csharp\">Task operationTask = SomeLongRunningOperationAsync();\r\n\r\nawait operationTask.WaitAsync(TimeSpan.FromSeconds(10));<\/code><\/pre>\n<h2>ThrowIfNull<\/h2>\n<p>No more having to check for <code>null<\/code> in every method before you throw an exception. It is now just one line of code.<\/p>\n<pre><code class=\"language-csharp\">void DoSomethingUseful(object obj)\r\n{\r\n    ArgumentNullException.ThrowIfNull(obj);\r\n}<\/code><\/pre>\n<h2>Working with NativeMemory<\/h2>\n<p>If you want to use C APIs to allocate memory because you&#8217;re a l33t hacker or need to allocate native memory, then look no further. Don&#8217;t forget to free!<\/p>\n<pre><code class=\"language-csharp\">using System.Runtime.InteropServices;\r\n\r\nunsafe\r\n{\r\n    byte* buffer = (byte*)NativeMemory.Alloc(100);\r\n\r\n    NativeMemory.Free(buffer);\r\n}<\/code><\/pre>\n<h2>Posix Signal Handling<\/h2>\n<p>Native support for Posix signal handling is here and we also emulate a couple of signals on Windows.<\/p>\n<pre><code class=\"language-csharp\">using System.Runtime.InteropServices;\r\n\r\nvar tcs = new TaskCompletionSource();\r\n\r\nPosixSignalRegistration.Create(PosixSignal.SIGTERM, context =&gt;\r\n{\r\n    Console.WriteLine($\"{context.Signal} fired\");\r\n    tcs.TrySetResult();\r\n});\r\n\r\nawait tcs.Task;<\/code><\/pre>\n<h2>New Metrics API<\/h2>\n<p>We added an entirely new metrics API based on @opentelemetry in .NET 6. It supports dimensions, is <em>super<\/em> efficient and will have exporters for popular metric sinks.<\/p>\n<pre><code class=\"language-csharp\">using System.Diagnostics.Metrics;\r\n\r\n\/\/ This is how you produce metrics\r\n\r\nvar meter = new Meter(\"Microsoft.AspNetCore\", \"v1.0\");\r\nCounter&lt;int&gt; counter = meter.CreateCounter&lt;int&gt;(\"Requests\");\r\n\r\nvar app = WebApplication.Create(args);\r\n\r\napp.Use((context, next) =&gt;\r\n{\r\n    counter.Add(1, KeyValuePair.Create&lt;string, object?&gt;(\"path\", context.Request.Path.ToString()));\r\n    return next(context);\r\n});\r\n\r\napp.MapGet(\"\/\", () =&gt; \"Hello World\");<\/code><\/pre>\n<p>You can even listen in and meter:<\/p>\n<pre><code class=\"language-csharp\">var listener = new MeterListener();\r\nlistener.InstrumentPublished = (instrument, meterListener) =&gt;\r\n{\r\n    if(instrument.Name == \"Requests\" &amp;&amp; instrument.Meter.Name == \"Microsoft.AspNetCore\")\r\n    {\r\n        meterListener.EnableMeasurementEvents(instrument, null);\r\n    }\r\n};\r\n\r\nlistener.SetMeasurementEventCallback&lt;int&gt;((instrument, measurement, tags, state) =&gt;\r\n{\r\n    Console.WriteLine($\"Instrument: {instrument.Name} has recorded the measurement: {measurement}\");\r\n});\r\n\r\nlistener.Start();<\/code><\/pre>\n<h2>Modern Timer API<\/h2>\n<p>Last but not least, a modern timer API (I think this is the 5th timer API in .NET now). It&#8217;s fully async and isn&#8217;t plagued by the types of gotchas the other timers are plagued with like object lifetime issues, no asynchronous callbacks etc.<\/p>\n<pre><code class=\"language-csharp\">var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));\r\n\r\nwhile (await timer.WaitForNextTickAsync())\r\n{\r\n    Console.WriteLine(DateTime.UtcNow);\r\n}<\/code><\/pre>\n<h2>Summary<\/h2>\n<p>This is just a sampling of the new APIs coming in .NET 6. For more information look at the <a href=\"https:\/\/github.com\/dotnet\/core\/tree\/main\/release-notes\/6.0\/preview\/api-diff\">.NET 6 release notes API diffs<\/a>. Also, Stephen just wrote a spectacular blog on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-6\/\">performance improvements in .NET6<\/a> so be sure to give that a read. Finally, don&#8217;t forget to <a href=\"https:\/\/dot.net\/download\">download the .NET 6 Preview<\/a> and try out the new APIs today.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 6 is on the way, and I wanted to share some of my favorite new APIs that you are going to love.<\/p>\n","protected":false},"author":1489,"featured_media":34056,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196,197,756],"tags":[7239,7241],"class_list":["post-34055","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","category-aspnet","category-csharp","tag-dotnet-6","tag-apis"],"acf":[],"blog_post_summary":"<p>.NET 6 is on the way, and I wanted to share some of my favorite new APIs that you are going to love.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/34055","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\/1489"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=34055"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/34055\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/34056"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=34055"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=34055"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=34055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}