{"id":55963,"date":"2009-11-04T13:52:00","date_gmt":"2009-11-04T13:52:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2009\/11\/04\/whats-new-in-beta-2-for-the-task-parallel-library-part-33\/"},"modified":"2009-11-04T13:52:00","modified_gmt":"2009-11-04T13:52:00","slug":"whats-new-in-beta-2-for-the-task-parallel-library-part-33","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-beta-2-for-the-task-parallel-library-part-33\/","title":{"rendered":"What\u2019s new in Beta 2 for the Task Parallel Library? (Part 3\/3)"},"content":{"rendered":"<p><P class=\"MsoNormal\"><FONT face=\"Calibri\"><FONT size=\"3\">Related posts:<\/FONT><\/FONT><\/P>\n<UL>\n<LI>\n<DIV class=\"MsoNormal\"><FONT size=\"3\" face=\"Calibri\"><A title=\"What's new in Beta 2 for the Task Parallel Library? (1\/3)\" href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/10\/19\/9909371.aspx\" target=\"_blank\">What&#8217;s new in Beta 2 for the Task Parallel Library? (1\/3)<\/A><\/FONT><\/DIV><\/LI>\n<LI>\n<DIV class=\"MsoNormal\"><FONT size=\"3\" face=\"Calibri\"><A title=\"What's new in Beta 2 for the Task Parallel Library? (2\/3)\" href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/10\/27\/9913610.aspx\" target=\"_blank\">What&#8217;s new in Beta 2 for the Task Parallel Library? (2\/3)<\/A><\/FONT><\/DIV><\/LI><\/UL>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Last time, we covered Tasks being detached by default and some refactorings in our multiple-Task continuation APIs.<SPAN>&nbsp; <\/SPAN>The final post of this series will discuss <B>Nested Tasks and Unwrap<\/B>, a <B>Parallel namespace change<\/B>, and some <B>changes under the covers<\/B>.<\/FONT><\/FONT><\/P>\n<H2><FONT size=\"4\"><FONT color=\"#4f81bd\"><FONT face=\"Cambria\">Nested Tasks and Unwrap<\/FONT><\/FONT><\/FONT><\/H2>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">We\u2019ve added the Unwrap APIs to address scenarios that deal with nested Tasks.<SPAN>&nbsp; <\/SPAN>Before jumping into Unwrap, let\u2019s first talk about nested Tasks, e.g. a Task&lt;Task&gt; or Task&lt;Task&lt;TResult&gt;&gt;.<SPAN>&nbsp; <\/SPAN>For example:<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><SPAN>var nestedTask = Task.Factory.StartNew(() =&gt;<BR>{<BR><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>return Task.Factory.StartNew(() =&gt;<BR><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>{<BR><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>return 42;<BR><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>});<BR>});<\/SPAN><SPAN class=\"arial9italic\"><B><SPAN><\/SPAN><\/B><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><FONT size=\"3\"><FONT face=\"Calibri\">Nested Tasks commonly lead to unexpected behavior in applications.<SPAN>&nbsp; <\/SPAN>For example, consider an API that provides the following functionality for asynchronously logging into a web service (like one from Facebook), retrieving a list of friends, and sending an email to each friend.<\/FONT><\/FONT><\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>\/\/ Given a user name and password, returns a Task whose<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>\/\/ result is a UserToken object.<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>public Task&lt;UserToken&gt; LogOn(string username, string password);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>\/\/ Given a UserToken, returns a Task whose result<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>\/\/ is a collection of Friend objects.<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>public Task&lt;FriendCollection&gt; GetFriendList(UserToken userToken);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>\/\/ Given a Friend, subject, and body, returns a Task that<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>\/\/ represents an email sending operation.<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>public Task SendEmail(Friend friend, string subject, string body);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><FONT size=\"3\"><FONT face=\"Calibri\">A user would like to be able to write code like the following, utilizing these APIs and Task continuations:<\/FONT><\/FONT><\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>var userToken = LogOn(user, pass);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>var friends = userToken.ContinueWith(_ =&gt; <B>GetFriendList(userToken.Result));<\/B><\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>var emails = friends.ContinueWith(_ =&gt;<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>var sentMails = new List&lt;Task&gt;();<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>foreach(var friend in friends.Result)<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>sentMails.Add(SendEmail(friend, subject, body));<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>}<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>return Task.Factory.ContinueWhenAll(<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>sentMails.ToArray(), tasks =&gt; Task.WaitAll(tasks));<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>});<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>emails<B>.ContinueWith<\/B>(_ =&gt; Console.WriteLine(\u201cAll emails sent\u201d));<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><FONT size=\"3\"><FONT face=\"Calibri\">The bolded code is problematic.<SPAN>&nbsp; <\/SPAN>Because the GetFriendList method returns a Task&lt;FriendCollection&gt;, the \u2018friends\u2019 variable is actually going to be a Task&lt;Task&lt;FriendCollection&gt;&gt;.<SPAN>&nbsp; <\/SPAN>This will cause a compiler error at the foreach loop, because \u2018friends.Result\u2019 will return a Task&lt;FriendCollection&gt; instead of a FriendCollection. <SPAN>&nbsp;<\/SPAN>The compiler error in this case is a good thing, of course, highlighting a programming error.<SPAN>&nbsp; <\/SPAN>However, once a developer realizes the type mismatch, he still has to code additional logic to unwrap the \u2018friends\u2019 Task so that it returns an actual FriendCollection.<SPAN>&nbsp; <\/SPAN>This logic is nontrivial, especially if it is to correctly deal with exceptions, cancellation, etc.<SPAN>&nbsp; <\/SPAN>The last line is also problematic.<SPAN>&nbsp; <\/SPAN>The emails variable here is actually of type Task&lt;Task&gt;.<SPAN>&nbsp; <\/SPAN>The outer Task will complete once the inner Task is returned from its body, even if the inner Task hasn\u2019t completed.<SPAN>&nbsp; <\/SPAN>The net result of this is that \u201cAll emails sent\u201d could be written out prior to all of the email tasks actually completing.<\/FONT><\/FONT><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><FONT size=\"3\"><FONT face=\"Calibri\">Now, you may have noticed that, in Beta 1, we provided special ContinueWith overloads to deal with this type of scenario.<SPAN>&nbsp; <\/SPAN>For example:<\/FONT><\/FONT><\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>public Task&lt;TResult&gt; ContinueWith&lt;TResult&gt;(<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>Func&lt;Task, Task&lt;TResult&gt;&gt; continuationFunction);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><FONT face=\"Calibri\"><FONT size=\"3\">The Func returns a Task&lt;TResult&gt;, so normally, ContinueWith would return a Task&lt;Task&lt;TResult&gt;&gt;.<SPAN>&nbsp; <\/SPAN>But this ContinueWith overload does some magic under the covers to return a Task&lt;TResult&gt;.<SPAN>&nbsp; <\/SPAN>There were a number of reasons why we didn\u2019t like this approach, including:<\/FONT><\/FONT><\/P>\n<P class=\"MsoListParagraphCxSpFirst\"><SPAN><SPAN><FONT size=\"3\">\u00b7<\/FONT><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN><\/SPAN><\/SPAN><FONT face=\"Calibri\"><FONT size=\"3\"><B>Too much magic.<\/B><SPAN>&nbsp; <\/SPAN>It\u2019s hard to explain why one set of ContinueWith overloads is \u201cjust different\u201d.<\/FONT><\/FONT><\/P>\n<P class=\"MsoListParagraphCxSpMiddle\"><SPAN><SPAN><FONT size=\"3\">\u00b7<\/FONT><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN><\/SPAN><\/SPAN><FONT face=\"Calibri\"><FONT size=\"3\"><B>Only works for ContinueWith<\/B>.<SPAN>&nbsp; <\/SPAN>What if user scenarios result in nested Tasks for other Task creation APIs like StartNew, ContinueWhenAll, etc.?<\/FONT><\/FONT><\/P>\n<P class=\"MsoListParagraphCxSpLast\"><SPAN><SPAN><FONT size=\"3\">\u00b7<\/FONT><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN><\/SPAN><\/SPAN><FONT size=\"3\"><FONT face=\"Calibri\"><B>What if nested Tasks are actually desired<\/B>?<B><SPAN>&nbsp; <\/SPAN><\/B>If a user actually wants that Task&lt;Task&lt;TResult&gt;&gt;, he still might unknowingly bind to this magical overload.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Given these reasons, our solution for Beta 2 and beyond is two Unwrap extension methods.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>namespace System.Threading.Tasks<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>public static class TaskExtensions<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>public static Task Unwrap(<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/SPAN>this Task&lt;Task&gt; task);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>public static Task&lt;TResult&gt; Unwrap&lt;TResult&gt;(<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/SPAN>this Task&lt;Task&lt;TResult&gt;&gt; task);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>}<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>}<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">These methods may be used to transform any Task&lt;Task&gt; or Task&lt;Task&lt;TResult&gt;&gt; into a Task or Task&lt;TResult&gt;, respectively.<SPAN>&nbsp; <\/SPAN>The transformation performed produces a Task or Task&lt;TResult&gt; that fully represents the original nested Task, including exceptions, cancellation state, etc.<SPAN>&nbsp; <\/SPAN><\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">With Unwrap, we can fix the above scenario (note the bolded).<\/FONT><\/FONT><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>var userToken = LogOn(user, pass);<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>var friends = userToken.ContinueWith(<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>_ =&gt; GetFriendList(userToken.Result)).<B>Unwrap();<\/B><\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>var emails = friends.ContinueWith(_ =&gt;<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>var sentMails = new List&lt;Task&gt;();<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>foreach(var friend in friends.Result)<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>sentMails.Add(SendEmail(friend, subject, body));<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>}<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>return Task.Factory.ContinueWhenAll(<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>sentMails.ToArray(), tasks =&gt; Task.WaitAll(tasks));<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>}).<B>Unwrap();<\/B><\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>emails.ContinueWith(_ =&gt; Console.WriteLine(\u201cAll emails sent\u201d));<\/SPAN><\/P>\n<P class=\"MsoNoSpacing\"><SPAN>&nbsp;<\/SPAN><\/P>\n<H1><FONT size=\"5\"><FONT color=\"#376092\"><FONT face=\"Cambria\">Parallel Namespace Change<\/FONT><\/FONT><\/FONT><\/H1>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">We\u2019ve moved the Parallel class from the System.Threading namespace to the <B>System.Threading.Tasks<\/B> namespace.<SPAN>&nbsp; <\/SPAN>We found that most applications needed to bring in both namespaces when using TPL, so why not put everything into one namespace?<SPAN>&nbsp; <\/SPAN>Additionally, Parallel is such a common word (and will likely become more so in the future), and System.Threading such a common namespace, we wanted to reduce the chances of conflict with other .NET types as much as possible.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Here\u2019s a useful IDE tip.<SPAN>&nbsp; <\/SPAN>Use \u201cCtrl + .\u201d to automatically bring in the relevant namespace once you\u2019ve typed a class\/type name.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\"><\/FONT><\/FONT>&nbsp;<IMG title=\"Ctrl Period IDE\" alt=\"Ctrl Period IDE\" src=\"https:\/\/blogs.msdn.com\/pfxteam\/attachment\/9917581.ashx\" width=\"335\" height=\"173\"><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\"><SPAN><\/SPAN><\/FONT><\/FONT><\/P>\n<H1><FONT size=\"5\"><FONT color=\"#376092\"><FONT face=\"Cambria\">Under the Covers<\/FONT><\/FONT><\/FONT><\/H1>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Beta 2 contains quite a few bug fixes not done for Beta 1.<SPAN>&nbsp; <\/SPAN>All of them were important, but we\u2019ll focus on only two here.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">First, Parallel.For and ForEach have been tweaked for better load-balancing with other workloads in the current or other AppDomains.<SPAN>&nbsp; <\/SPAN>Essentially, the change was to service the parallel loops with Tasks that periodically retired and re-queued themselves, allowing other contenders to grab Threads and make progress.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Second, waiting for Tasks in parent\/child relationships has become more efficient.<SPAN>&nbsp; <\/SPAN>In Beta 1 and before, parent Tasks waited for their children using explicit Waits, so even if a parent completed first, it would burn a thread until all of its children completed.<SPAN>&nbsp; <\/SPAN>In Beta 2, parent Tasks wait for their children using callbacks; the parent maintains a count for number of children it has, and each child decrements the count as it completes.<SPAN>&nbsp; <\/SPAN>This waiting logic significantly improves scalability.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">That\u2019s it folks!<SPAN>&nbsp; <\/SPAN>We hope you\u2019ve enjoyed this series.<SPAN>&nbsp; <\/SPAN><\/FONT><\/FONT><A href=\"http:\/\/www.microsoft.com\/visualstudio\/en-us\/products\/2010\/default.mspx\"><FONT size=\"3\" face=\"Calibri\">Download Beta 2<\/FONT><\/A><FONT size=\"3\"><FONT face=\"Calibri\"> and try it out!<\/FONT><\/FONT><\/P><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/21\/2019\/02\/ctrl-period.png\">ctrl-period.png<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Related posts: What&#8217;s new in Beta 2 for the Task Parallel Library? (1\/3) What&#8217;s new in Beta 2 for the Task Parallel Library? (2\/3) Last time, we covered Tasks being detached by default and some refactorings in our multiple-Task continuation APIs.&nbsp; The final post of this series will discuss Nested Tasks and Unwrap, a Parallel [&hellip;]<\/p>\n","protected":false},"author":485,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7907,7909,7912,7323],"class_list":["post-55963","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-parallel-extensions","tag-task-parallel-library","tag-visual-studio-2010"],"acf":[],"blog_post_summary":"<p>Related posts: What&#8217;s new in Beta 2 for the Task Parallel Library? (1\/3) What&#8217;s new in Beta 2 for the Task Parallel Library? (2\/3) Last time, we covered Tasks being detached by default and some refactorings in our multiple-Task continuation APIs.&nbsp; The final post of this series will discuss Nested Tasks and Unwrap, a Parallel [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55963","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\/485"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=55963"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55963\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=55963"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55963"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55963"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}