U2U all postshttps://blogs.u2u.be/peter/post.aspx?id=a8bfe634-8f3a-45fa-a600-29293353548ehttps://blogs.u2u.be/peter/post/faking-dbcontext-in-tests-with-nsubstituteFaking DbContext in Tests with NSubstitute<p>You want to test something that uses an Entity Framework <code>DbContext</code> as a dependency.</p> <p>Can I replace this easily with a fake <code>DbContext</code>?</p> <blockquote> <p>Using MOQ? Read <a href="https://blogs.u2u.be/peter/post/faking-dbcontext-in-tests">this</a></p> </blockquote> <p>Let us say you have this simple DbContext:</p> <pre><code class="language-cs">public class SomedbContext : DbContext { public virtual DbSet&lt;Blog&gt; Blogs =&gt; Set&lt;Blog&gt;(); public virtual DbSet&lt;Post&gt; Posts =&gt; Set&lt;Post&gt;(); } </code></pre> <blockquote> <p>Do note I am using NSubstitute to build fake objects, and that I have made these properties <code>virtual</code> to allow NSubstitute to override their implementation.</p> </blockquote> <p>Now I need some fake data, and turn it into <code>IQueryable&lt;T&gt;</code>:</p> <pre><code class="language-cs">IQueryable&lt;Blog&gt; blogs = FakeData.FakeBlogs.AsQueryable(); IQueryable&lt;Post&gt; posts = FakeData.FakePosts.AsQueryable(); </code></pre> <p>This allows me to build a fake <code>DbContext</code> as follows:</p> <pre><code class="language-cs">SomedbContext db = new SomedbContext() .WithTable(db =&gt; db.Blogs, blogs) .WithTable(db =&gt; db.Posts, posts) .Build(); </code></pre> <p>Now I can pass this fake <code>DbContext</code> to the intended class as a dependency, and it will use my fake data during the test, and no real database is needed.</p> <blockquote> <p>You might also consider simply using the InMemory database provided by EF.</p> </blockquote> <p>So how does this work?</p> <p>Let us examine following piece of code:</p> <pre><code class="language-cs">SomedbContext db = new SomedbContext() .WithTable(db =&gt; db.Blogs, blogs) .WithTable(db =&gt; db.Posts, posts) .Build(); </code></pre> <p>The first <code>WithTable</code> method is an extension method which returns a fluid builder instance:</p> <pre><code class="language-cs">public static class DbContextExtensions { public static FakeDbContextBuilder&lt;TDB&gt; WithTable&lt;TDB, E&gt;( this TDB dbContext, Expression&lt;Func&lt;TDB, DbSet&lt;E&gt;&gt;&gt; exp, IQueryable&lt;E&gt; data) where TDB : DbContext where E : class { FakeDbContextBuilder&lt;TDB&gt; builder = new(); return builder.WithTable(exp, data); } </code></pre> <p>Using the power of generics, I can apply this method to any <code>DbContext</code>, passing the <code>DbSet&lt;E&gt;</code> I want to replace, and the data that it should be replaced with.</p> <pre><code class="language-cs">new SomedbContext().WithTable(db =&gt; db.Blogs, blogs) </code></pre> <p>There are three arguments involved:</p> <ol> <li>The <code>WithTable</code> method is an extension method, so a <code>SomedbContext</code> instance is passed as the first argument.</li> <li>The second argument in an <code>Expression&lt;Func&lt;SomedbContext, DbSet&lt;Blog&gt;&gt;&gt;</code>, and <code>db =&gt; db.Blogs</code> will be passed allows us to replace <code>Blogs</code> with the fake data.</li> <li>The third argument is the fake data passed as an <code>IQueryable&lt;Blog&gt;</code>.</li> </ol> <p>All of this gets passed to the <code>FakeDbContextBuilder&lt;TDB&gt;</code> fluent builder's <code>WithTable</code> extension method. Let us have a look at this:</p> <pre><code class="language-cs">public static FakeDbContextBuilder&lt;TDB&gt; WithTable&lt;TDB, E&gt;( this FakeDbContextBuilder&lt;TDB&gt; builder, Expression&lt;Func&lt;TDB, DbSet&lt;E&gt;&gt;&gt; exp, IQueryable&lt;E&gt; data) where TDB : DbContext where E : class { Mock&lt;DbSet&lt;E&gt;&gt; mockSet = new(); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.Provider) .Returns(data.Provider); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.Expression) .Returns(data.Expression); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.ElementType) .Returns(data.ElementType); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.GetEnumerator()) .Returns(data.GetEnumerator); builder.Replace(exp, mockSet.Object); return builder; } </code></pre> <p>This method has the same arguments as the first <code>WithTable</code> method.</p> <p>What this method does it creates a fake <code>DbSet&lt;E&gt;</code> using MOQ, and then proceeds to relay all of its properties to the <code>IQueryable&lt;T&gt;</code> fake data.</p> <p>Once this is done, we tell the fluent builder to replace a property in the <code>DbContext</code>, for example replace <code>db =&gt; db.Blogs</code> with the fake <code>DbSet&lt;E&gt;</code>.</p> <p>Now it is time to examine the <code>FakeDbContextBuilder&lt;TDB&gt;</code> class:</p> <pre><code class="language-cs">public class FakeDbContextBuilder&lt;TDB&gt; where TDB : DbContext { private Mock&lt;TDB&gt; moq = new(); public TDB Build() =&gt; moq.Object; public void Replace&lt;E&gt;(Expression&lt;Func&lt;TDB, DbSet&lt;E&gt;&gt;&gt; exp, DbSet&lt;E&gt; e) where E : class =&gt; exp.Compile()(this.fake).Returns(e); } </code></pre> <p>This class keeps track of a <code>Mock&lt;SomedbContext&gt;</code>, and replaces each <code>DbSet&lt;E&gt;</code> property if needed. This is done in the <code>Replace</code> method.</p> <p>At the end of the call chain, you should call <code>Build</code>, which returns the fake <code>DbContext</code>.</p> <p>You don't even have to replace each <code>DbSet</code>, pick you ones you need.</p> <p>In this blog post I wanted to demonstrate how easy it is to replace any <code>DbContext</code> with fake data, and show you an implementation that make the life of the user (you) easy and hides away the details on how to do this...</p> Fri, 13 Oct 2023 12:18:00 Zhttps://blogs.u2u.be/peter/post/faking-dbcontext-in-tests-with-nsubstitute#commentpeter@u2u.behttps://blogs.u2u.be/peter/pingback.axdhttps://blogs.u2u.be/peter/post.aspx?id=a8bfe634-8f3a-45fa-a600-29293353548e0https://blogs.u2u.be/peter/trackback.axd?id=a8bfe634-8f3a-45fa-a600-29293353548ehttps://blogs.u2u.be/peter/post/faking-dbcontext-in-tests-with-nsubstitute#commenthttps://blogs.u2u.be/peter/syndication.axd?post=a8bfe634-8f3a-45fa-a600-29293353548ehttps://blogs.u2u.be/u2u/post.aspx?id=194cf174-f138-4124-9bed-4b2bbdf7f8a5https://blogs.u2u.be/u2u/post/u2u-online-v2-1-is-hereU2U Online V2.1 is here!<p>This blog post explains the latest features present in the most recent version of our online platform, online.u2u.be, which we, as trainers, fondly refer to as 'U2U Online'. In case you are unfamiliar with U2U Online, it is likely that you have not attended any of our training courses, as that is where the true magic happens.</p> <p>U2U Online serves as our in-house online learning platform, granting our students access to all the necessary materials linked to their ongoing trainings. This includes theoretical resources, practical exercises, videos, references, general information, and even a group chat to communicate with each other. By exhibiting all these resources in a centralized location, we strive to ensure an optimal user-friendly experience for our students.</p> <p>To provide context, it is crucial to understand the rationale behind the online learning platform. Have you ever encountered the Learning Pyramid Model? This model explains various modes of learning and assesses their efficacy in retaining knowledge. Notably, the model differentiates between passive and active learning methods, with the latter exhibiting a higher rate of knowledge retention compared to the former.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture1.png" alt="/Ruben/u2uonline/Picture1.png" /></p> <p>Figure 1: Learning Pyramid : https://www.arlo.co/blog/overview-of-the-learning-pyramid-for-training-providers</p> <p>While it is imperative to incorporate active learning methods, it is equally important not to disregard the passive learning techniques. A well-rounded training program entails a balance between the two. At U2U, we have carefully combined passive and active learning methods to optimize the learning retention for our students.</p> <p>Merely providing demonstrations may facilitate a 30 percent recall of the theory, but this alone does not suffice. Therefore, we complement our approach with active learning methods. For instance, we initiate classroom discussions by encouraging participants to share their existing knowledge on the subject matter or by posing thought-provoking questions such as 'What do you think will happen and can you explain why?'.</p> <p>Furthermore, students are provided ample time to engage in detailed exercises that allow them to put the theory into practice, resulting in a remarkable 75 percent retention rate. As such, it is crucial for us to prioritize a seamless user experience including both theoretical and practical aspects. To this end, we have strived to optimize the user experience by developing and improving our online learning platform, U2U Online.</p> <p>It has been a few weeks since the implementation of U2U Online V2.1. As is customary with any software program or product, we conducted thorough testing to rectify any remaining minor bugs before unrolling it to the wider audience. Those initial challenges are now behind us, and on behalf of the entire U2U team, Now that those initial challenges are behind us, the entire U2U team is delighted and proud of the result. That's why I'm writing this blog post to walk you through the major differences between V2.0 and V2.1, along with the exciting new features.</p> <h3>Separation of Theory and Labs</h3> <p>Each of our courses consist of modules including both theoretical and practical components. The theory portion involves PowerPoint slides, while the lab segment involves hands-on exercises corresponding to the recently acquired knowledge. In the previous version of U2U Online, theory and labs were combined into a single Word document with numerous pages, requiring considerable scrolling to locate the exercises.</p> <p>In the newest iteration of U2U Online, we have divided each module into distinct theory and lab sections. Bid farewell to the arduous task of searching for exercise starting points within lengthy documents! Within each module, theory and lab materials are elegantly displayed side by side, facilitating seamless transition between theory and practical applications. Thus, we have successfully resolved Issue 1.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture2.png" alt="/Ruben/u2uonline/Picture2.png" /></p> <p>Figure 2: Side by Side rendered Theory and Labs</p> <h3>Enhanced Rendering of Theory Presentations</h3> <p>In U2U Online V2.0, the theory portion of each module comprised screenshots of PowerPoint slides, arranged in a web layout with two slides per page. This approach presented several challenges, such as the inability to click on hyperlinks, copy-paste text, or navigate through subsections smoothly.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture3.png" alt="/Ruben/u2uonline/Picture3.png" /></p> <p>Figure 3: Theory rendering in V2.0 (old)</p> <p>To rectify these limitations, we now render each slide individually in the new version of U2U Online. Moreover, we have incorporated a subsection navigator on the left-hand side for effortless navigation to specific subsections as needed. With this direct slide rendering approach, interactive features such as hyperlink functionality and text copying are fully enabled.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture4.png" alt="/Ruben/u2uonline/Picture4.png" /></p> <p>Figure 4: Theory rendering in V2.1 (new) Enhanced Rendering of Labs</p> <h3>Enhanced Rendering of Labs</h3> <p>Similar to the theory portion, labs were previously rendered within a single Word document comprising multiple pages. Each lab typically encompassed multiple exercises belonging to different subsections, without the ability to navigate between them easily. While it was possible to interact with the text, such as copying specific code snippets, the discrepancy between Word and the rendering engine introduced frustrations in selecting specific text regions.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture5.png" alt="/Ruben/u2uonline/Picture5.png" /></p> <p>Figure 5: Lab rendering in V2.0 (old)</p> <p>To address these issues, we now directly render labs within the U2U Online platform, eliminating the need for Word as an intermediate source. We employ Markdown to visualize text, images, and code snippets in a clear format. This approach empowers end-users to interact effortlessly with the exercise materials. Text automatically scales in relation to the available screen space, and each lab subsection is presented as a separate scrollable page, ensuring a coherent visualization of all associated steps. Furthermore, the section navigator allows swift navigation between sections when necessary.</p> <p>Additionally, lab files associated with a given exercise, such as starter files, resources, and solutions, previously required users to navigate back to the menu, download the files, and then manually find their place in the exercise again. To mitigate this inconvenience, we have incorporated a solution in the new version. At the top of the Section Navigator, users can now directly download lab files associated with the current exercise, eliminating the need to leave the page or lose track of progress. All necessary resources are conveniently accessible from within the current page.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture6.png" alt="/Ruben/u2uonline/Picture6.png" /></p> <p>Figure 6: Rendering Labs in V2.1 (new)</p> <h3>Streamlined Evaluations</h3> <p>At the conclusion of each training course, we kindly request that students complete an evaluation form. This valuable feedback allows us to continually enhance our course materials and ensure alignment with student expectations. As a token of appreciation for completing the evaluation, students are granted access to a digital certificate for their course participation.</p> <p>In U2U Online V2.0, students had to click a link within the course to be redirected to an external webpage for evaluation submission. After completing the evaluation, they needed to manually return to U2U Online to download their certificate, potentially sharing it on social media or saving it as a PDF. However, in U2U Online V2.1, we have integrated the entire evaluation process within the platform itself. There is no longer a need for redirection or returning to U2U Online after submitting the evaluation. The entire process is now centralized.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture7.png" alt="/Ruben/u2uonline/Picture7.png" /></p> <p>Figure 7: Evaluations included in V2.1 (new)</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture8.png" alt="/Ruben/u2uonline/Picture8.png" /></p> <p>Figure 8: Integrated sharing of the obtained certificate in V2.1 (new)</p> <p>These four improvements constitute the most significant enhancements in U2U Online V2.1. If you haven't explored these features yet, I highly recommend taking a look and experiencing them firsthand. Of course, there is always room for further improvements, and we are actively working on designing V2.2 with additional features that will prove advantageous. Stay tuned for updates on these new features and their anticipated release date.</p> Tue, 13 Jun 2023 10:29:00 Zhttps://blogs.u2u.be/u2u/post/u2u-online-v2-1-is-here#commentruben@u2u.behttps://blogs.u2u.be/u2u/pingback.axdhttps://blogs.u2u.be/u2u/post.aspx?id=194cf174-f138-4124-9bed-4b2bbdf7f8a50https://blogs.u2u.be/u2u/trackback.axd?id=194cf174-f138-4124-9bed-4b2bbdf7f8a5https://blogs.u2u.be/u2u/post/u2u-online-v2-1-is-here#commenthttps://blogs.u2u.be/u2u/syndication.axd?post=194cf174-f138-4124-9bed-4b2bbdf7f8a5https://blogs.u2u.be/peter/post.aspx?id=4edb836e-b0e2-4f50-b78b-0f47541e9432https://blogs.u2u.be/peter/post/using-system-text-json-source-generator-to-improve-performance.NET Core.NET DevelopmentUsing System.Text.Json Source Generator to Improve Performance<h2>Overview</h2> <p>This blog post shows how you can improve JSON serialization performance with a simple step, thanks to the Roslyn Source Generator feature.</p> <h2>Source Generators</h2> <p>Source generators are Roslyn plugins that can write tedious code for you, either C# or VB. Here I will use C#.</p> <p>When it comes down to JSON serialization, the <code>JsonSerializer.Serialize</code> method will use reflection to figure out the meta-data it needs to serialize and deserialize a certain type. You can speed up this process by supplying the meta-data yourself, but this results in a lot of boring code, and who wants to write this kind of repetetive code?</p> <h2>Generating Serialization Meta-data using a Source Generator</h2> <p>You can automate this meta-data code using a source generator.</p> <p>Start by creating a class deriving from <code>JsonSerializerContext</code>, and add the <code>JsonSerializable</code> attribute:</p> <pre><code class="language-CS">[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Person))] internal partial class PersonJsonSerializerContext : JsonSerializerContext { } </code></pre> <p>Hit compile, and this will now generate the code! That's it!</p> <h2>Examining the generated code</h2> <p>If you like you can examine the generated code. Start by adding the <code>&lt;EmitCompilerGeneratedFiles&gt;</code> option to your project.</p> <pre><code class="language-XML">&lt;PropertyGroup&gt; &lt;EmitCompilerGeneratedFiles&gt;true&lt;/EmitCompilerGeneratedFiles&gt; &lt;/PropertyGroup&gt; </code></pre> <p>You can now find the generated code in</p> <pre><code>obj \Debug \net*.0 \generated \System.Text.Json.SourceGeneration \System.Text.Json.SourceGeneration.JsonSourceGenerator </code></pre> <h2>Using the generated meta-data</h2> <p>You can now use the generated meta-data by passing it to the serializer:</p> <pre><code class="language-CS">Person person = new() { ... }; string Json = JsonSerializer.Serialize(person, PersonJsonSerializerContext.Default.Person); Person p = JsonSerializer.Deserialize(Json, PersonJsonSerializerContext.Default.Person); </code></pre> <h2>What about changes in the code?</h2> <p>When you make changes to your <code>Person</code> class, the source generator will regenerate all the code.</p> Wed, 26 Apr 2023 12:01:00 Zhttps://blogs.u2u.be/peter/post/using-system-text-json-source-generator-to-improve-performance#commentpeter@u2u.behttps://blogs.u2u.be/peter/pingback.axdhttps://blogs.u2u.be/peter/post.aspx?id=4edb836e-b0e2-4f50-b78b-0f47541e94320https://blogs.u2u.be/peter/trackback.axd?id=4edb836e-b0e2-4f50-b78b-0f47541e9432https://blogs.u2u.be/peter/post/using-system-text-json-source-generator-to-improve-performance#commenthttps://blogs.u2u.be/peter/syndication.axd?post=4edb836e-b0e2-4f50-b78b-0f47541e9432https://blogs.u2u.be/u2u/post.aspx?id=82341edd-d70f-4e23-8787-78b0347281b3https://blogs.u2u.be/u2u/post/using-the-power-platform-cli-with-power-pagesPower PlatformPower PagesUsing the Power Platform CLI with Power Pages<p>In this blog post you will see how to use the Power Platform CLI with Power Pages. This feature enables you to download the content of a Power Pages site and edit it in Visual Studio Code. Once finished, you can upload all your changes back to the Power Pages site. Another use case for this feature is to move a Power Pages site between production, acceptance and production environments.</p> <p>As a first step you will need to install and download <a href="https://code.visualstudio.com">Visual Studio Code</a>. Once installed you can follow the steps below to install the Power Platform CLI.</p> <ol> <li>Start Visual Studio Code.</li> <li>Select the <strong>Extensions</strong> icon from the left-side window.</li> </ol> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2009.39.23.png" alt="enter image description here" title="enter image title here" /></p> <ol start="3"> <li>In the search text box, type <strong>Power Platform Tools</strong>.</li> </ol> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2009.47.00.png" alt="enter image description here" title="enter image title here" /></p> <ol start="4"> <li>Click the on the blue <strong>Install</strong> button.</li> <li>Restart Visual Studio Code.</li> </ol> <p>Once Visual Studio Code is restarted, go to <strong>View</strong> -&gt; <strong>Terminal</strong> to open a new Terminal window. In this new terminal window type <strong>pac</strong> and press enter.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2010.27.37.png" alt="enter image description here" title="enter image title here" /></p> <p>This will list all the commands that can be executed using the Power Platform CLI.</p> <p>As a first step, we must authenticate with the Dataverse environment that hosts the Power Pages site for which we want to download the content. For this we can run the <em>pac auth create --environment [Dataverse Environment URL]</em> command.</p> <pre><code>pac auth create --environment https://u2udev.crm4.dynamics.com </code></pre> <p>Once you successfully login, the following output will be displayed.</p> <pre><code>'trainer@u2ucourse.com' authenticated successfully. Validating connection... Default organization: U2UDev Authentication profile created * UNIVERSAL https://u2udev.crm4.dynamics.com/ : trainer@u2ucourse.com Public </code></pre> <p>If you now want to list all Power Pages sites in the selected environment, you can use the <em>pac paportal list</em> command. As you can see in the output below, there are 2 Power Pages sites present in the selected environment.</p> <pre><code>PS /Users/jurgenpostelmans&gt; pac paportal list Connected to... U2UDev Connected as trainer@u2ucourse.com Completed listing Power Pages websites Index Website Id Friendly Name [1] fd99279d-cace-ed11-b597-0022489ee5d6 U2U Blog Post - u2ublogpost [2] ad7c0439-f381-ed11-81ad-0022489fd57f U2U Portal - u2uportal </code></pre> <p>To download the content of a Power Pages site, you can use the <em>pac paportal download --path [Local Path] --webSiteId [WebSiteId-GUID]</em> command. The <em>path</em> parameter indicates where the Power Pages website content will be downloaded. The <em>WebSiteId-GUID</em> is the ID of the Power Pages site and can be found in the output of the <em>pac paportal list</em> command above.</p> <pre><code>PS /Users/jurgenpostelmans/blogpost&gt; pac paportal download --path ./ --webSiteId fd99279d-cace-ed11-b597-0022489ee5d6 Information: Be careful when you're updating public sites. The changes you make are visible to anyone immediately. To check site visibility go to https://make.powerpages.microsoft.com/. To get additional information, please visit https://go.microsoft.com/fwlink/?linkid=2204350 Started downloading website fd99279d-cace-ed11-b597-0022489ee5d6 Connected to... U2UDev Connected as trainer@u2ucourse.com Downloading: Website [adx_website]... Downloaded: Website [adx_website] Downloading: Website Language [adx_websitelanguage]... ... Updating manifest... Manifest updated successfully. Power Pages website download succeeded in 13.465261 secs. </code></pre> <p>Once the download is finished, a new folder is created that is made up of the display name and name of your Power Pages site.</p> <pre><code>PS /Users/jurgenpostelmans/blogpost&gt; ls -l total 0 drwxr-xr-x@ 25 jurgenpostelmans staff 800 Mar 30 10:58 u2u-blog-post---u2ublogpost PS /Users/jurgenpostelmans/blogpost&gt; </code></pre> <p>Open this folder with Visual Studio Code.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2011.17.32.png" alt="enter image description here" title="enter image title here" /></p> <p>If we now want to edit the content of the Home page, open the file that is named <em>Home.en-US.webpage.copy.html</em>.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2011.21.02.png" alt="enter image description here" title="enter image title here" /></p> <p>Let's add some custom text and a small piece of Liquid code inside the innermost div tag.</p> <pre><code>&lt;div class='row sectionBlockLayout text-left' style='display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;'&gt; &lt;div class='container' style='padding: 0px; display: flex; flex-wrap: wrap;'&gt; &lt;div class='col-md-12 columnBlockLayout' style='flex-grow: 1; display: flex; flex-direction: column; min-width: 300px;'&gt; This page was modified in Visual Studio Code&lt;br&gt; {{ now }} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; </code></pre> <p>To upload the changes, the <em>pac paportal upload --path [Local Path]</em> command can be used.</p> <pre><code>PS /Users/jurgenpostelmans/blogpost/u2u-blog-post---u2ublogpost&gt; pac paportal upload --path ./ Information: Be careful when you're updating public sites. The changes you make are visible to anyone immediately. To check site visibility go to https://make.powerpages.microsoft.com/. To get additional information, please visit https://go.microsoft.com/fwlink/?linkid=2204350 Started uploading website data Connected to... U2UDev Connected as trainer@u2ucourse.com Loading Power Pages website manifest... Manifest loaded successfully. Updated record(s) for table: adx_webpage Power Pages website upload succeeded in 5.77 secs. </code></pre> <p>Notice how it only uploads data for the adx_webpage entity because the command only uploads new/modified/deleted content. When the content of a Power Pages site is download, a manifest.yaml file is created inside the .portalconfig folder.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2011.33.19.png" alt="enter image description here" title="enter image title here" /></p> <p>As you can see in the screenshot, a checksum is calculated and stored for each record that was downloaded. If the content of a record gets modified, the current checksum will not match with the checksum in the manifest. This way the pac paportal upload command can upload only the changes.</p> Thu, 30 Mar 2023 11:17:00 Zhttps://blogs.u2u.be/u2u/post/using-the-power-platform-cli-with-power-pages#commentjurgen@u2u.behttps://blogs.u2u.be/u2u/pingback.axdhttps://blogs.u2u.be/u2u/post.aspx?id=82341edd-d70f-4e23-8787-78b0347281b30https://blogs.u2u.be/u2u/trackback.axd?id=82341edd-d70f-4e23-8787-78b0347281b3https://blogs.u2u.be/u2u/post/using-the-power-platform-cli-with-power-pages#commenthttps://blogs.u2u.be/u2u/syndication.axd?post=82341edd-d70f-4e23-8787-78b0347281b3https://blogs.u2u.be/michael/post.aspx?id=b9adcc69-ef51-4113-82ed-161192630202https://blogs.u2u.be/michael/post/2023/03/28/more-happiness-for-the-lazy-developer-ms-graph-toolkit-with-htmlMore happiness for the lazy developer: MS Graph toolkit with HTML<p>Last blogpost showed how to use the MS Graph Toolkit for getting info from the graph, and show them in a SPFX webpart, without the need for writing all the data-fetching and UI code.</p> <p>Using the toolkit directly in HTML is also super-easy. The setup is a bit different, because now we need to register the app in AAD. Let's try to achieve the same result as what we had with SPFX. Let's get to AAD and register the app. I register the app as an SPA, with following setup:</p> <p><img src="https://blogs.u2u.be/michael/image.axd?picture=/2023/03/snap1.png" alt="/2023/03/snap1.png" title="Registering the app in AAD" /></p> <p>I also need Read-write permissions for Tasks:</p> <p><img src="https://blogs.u2u.be/michael/image.axd?picture=/2023/03/snap2.png" alt="/2023/03/snap2.png" title="needed Graph Permissions" /></p> <p>And now for the HTML. You can start by refering to the necessary javascript:</p> <pre><code class="language-HTML">&lt;script src=&quot;https://unpkg.com/@microsoft/mgt@2/dist/bundle/mgt-loader.js&quot;&gt;&lt;/script&gt; </code></pre> <p>The component used is again mgt-todo, but we need two additional things: a component for loging in (equally simple to add), and a provider for the authentication using MSAL.</p> <pre><code class="language-HTML">&lt;mgt-msal2-provider client-id=&quot;73fcc049-180b-4bca-ba91-bdf9be07cde9&quot; authority=&quot;https://login.microsoftonline.com/d12f9872-a3ea-4c33-a06f-3c7a33df595c&quot;&gt; &lt;/mgt-msal2-provider&gt; &lt;mgt-login&gt;&lt;/mgt-login&gt; &lt;mgt-todo&gt;&lt;/mgt-todo&gt; </code></pre> <p>The client-id used for the provider is, obviously, the id you got when registering the app in AAD. The authority refers to your Tenant. The last on can be skipped if you register the app as multitenant.</p> <p>Again, that's it. It's working in like 5' !! Let's go home early today...</p> <p><img src="https://blogs.u2u.be/michael/image.axd?picture=/2023/03/snap3.png" alt="/2023/03/snap3.png" title="The page in action" /></p> Tue, 28 Mar 2023 17:15:00 Zhttps://blogs.u2u.be/michael/post/2023/03/28/more-happiness-for-the-lazy-developer-ms-graph-toolkit-with-html#commentmichael@u2u.behttps://blogs.u2u.be/michael/pingback.axdhttps://blogs.u2u.be/michael/post.aspx?id=b9adcc69-ef51-4113-82ed-1611926302020https://blogs.u2u.be/michael/trackback.axd?id=b9adcc69-ef51-4113-82ed-161192630202https://blogs.u2u.be/michael/post/2023/03/28/more-happiness-for-the-lazy-developer-ms-graph-toolkit-with-html#commenthttps://blogs.u2u.be/michael/syndication.axd?post=b9adcc69-ef51-4113-82ed-161192630202https://blogs.u2u.be/michael/post.aspx?id=04a5e562-c3d7-42a1-9ade-402d0840f361https://blogs.u2u.be/michael/post/2023/03/09/bliss-for-the-lazy-developer-the-ms-graph-toolkitBliss for the lazy developer : the MS Graph Toolkit<p>&quot;A good developer is a lazy developer&quot;. That's something I've been told in the past. You shouldn't take it too literaly though. It means that you shouldn't write it yourself, when something can be done with standard functionalities, libraries or tools. Microsoft recently dropped the MS Graph toolkit, which makes it easier to create solutions targetting Microsoft Graph. Today I'll have a look at how this can help us in a SharePoint framework solution.</p> <p>Let's imagine an SPFX solution in which we need to show the Todo items for the logged on person. In Graph I need to address following URL for getting the tasks in the default ToDo list:</p> <p>https://graph.microsoft.com/v1.0/me/todo/lists/AAMkADI5ZmU4ZmY4LWEzZGQtNDZlMS04YTM2LTI4MTc0NGI5YmIyNwAuAAAAAACcpXDx9lqqRbqZiOBY7OjwAQChhNHLRVScQJBh2LuUX06kAAAAAAESAAA=/tasks</p> <p>Of course your solution need the necessary permissions, so you need to ask for this in the <em>package-solution.json</em>.</p> <pre><code class="language-json"> &quot;webApiPermissionRequests&quot;: [ { &quot;resource&quot;: &quot;Microsoft Graph&quot;, &quot;scope&quot;: &quot;Tasks.ReadWrite&quot; } </code></pre> <p>And with a bit of code we can create a serviceclient class for getting the information.</p> <pre><code class="language-Typescript">export class TodoService { constructor(public ctx:WebPartContext) {} private async getGraphClient():Promise&lt;MSGraphClientV3&gt;{ const gClient = await this.ctx.msGraphClientFactory.getClient(&quot;3&quot;); return gClient; } async getTodos():Promise&lt;MSGraph.TodoTask[]&gt; { let gClient = await this.getGraphClient(); let todos:any = await gClient .api('/me/todo/lists/AAMkADI5ZmU4ZmY4LWEzZGQtNDZlMS04YTM2LTI4MTc0NGI5YmIyNwAuAAAAAACcpXDx9lqqRbqZiOBY7OjwAQChhNHLRVScQJBh2LuUX06kAAAAAAESAAA=/tasks') .get(); return todos.value; } } </code></pre> <p>And with some minimal effort we can show this.</p> <pre><code class="language-Typescript">export default class ToDos extends React.Component&lt;{}, IToDosState&gt; { constructor() { this.state = {data:[]}; } componentDidMount(): void { this.props.service.getTodos().then(r=&gt; { this.setState({data:r}); }).catch(err=&gt;console.log(err)); } public render(): React.ReactElement&lt;IToDosProps&gt; { const data = this.state.data.map(el=&gt;&lt;li key={el.id}&gt;{el.title}&lt;/li&gt;); return ( &lt;&gt; &lt;div&gt;Todos&lt;/div&gt; &lt;ul&gt;{data}&lt;/ul&gt; &lt;/&gt; ); } } </code></pre> <p>Okay, the UI is really minimalistic, I know. And the code is not too complex, but still, it can get easier !!! Let's use the MS Graph Toolkit. First add it to your project with</p> <pre><code>npm i @microsoft/mgt-spfx </code></pre> <p>Let's create a new webpart now. There's a few things that we must do. First we need to initialize some providers. In the Graph toolkit, Providers will take care of authentication.</p> <pre><code class="language-Typescript">import { Providers, SharePointProvider } from '@microsoft/mgt-spfx'; export default class ToDo2WebPart extends BaseClientSideWebPart&lt;IToDo2WebPartProps&gt; { //... protected onInit(): Promise&lt;void&gt; { if (!Providers.globalProvider) { Providers.globalProvider = new SharePointProvider(this.context); } return Promise.resolve(); } } </code></pre> <p>And then we can show the todo's like this:</p> <pre><code class="language-Typescript"> public render(): void { this.domElement.innerHTML = `&lt;div&gt; &lt;mgt-todo&gt;&lt;/mgt-todo&gt; &lt;/div&gt; ` } </code></pre> <p>And that's it. How easy is that. It even looks good. Like I said, bliss for the lazy developer.</p> Thu, 09 Mar 2023 22:11:00 Zhttps://blogs.u2u.be/michael/post/2023/03/09/bliss-for-the-lazy-developer-the-ms-graph-toolkit#commentmichael@u2u.behttps://blogs.u2u.be/michael/pingback.axdhttps://blogs.u2u.be/michael/post.aspx?id=04a5e562-c3d7-42a1-9ade-402d0840f3610https://blogs.u2u.be/michael/trackback.axd?id=04a5e562-c3d7-42a1-9ade-402d0840f361https://blogs.u2u.be/michael/post/2023/03/09/bliss-for-the-lazy-developer-the-ms-graph-toolkit#commenthttps://blogs.u2u.be/michael/syndication.axd?post=04a5e562-c3d7-42a1-9ade-402d0840f361https://blogs.u2u.be/peter/post.aspx?id=fa254730-b819-4f66-b534-3ab8fa6d1ab9https://blogs.u2u.be/peter/post/faking-dbcontext-in-testsFaking DbContext in Tests with MOQ<p>You want to test something that uses an Entity Framework <code>DbContext</code> as a dependency.</p> <p>Can I replace this easily with a fake <code>DbContext</code>?</p> <blockquote> <p>Using NSubstitute? Read <a href="https://blogs.u2u.be/peter/post/faking-dbcontext-in-tests-with-nsubstitute">this</a></p> </blockquote> <p>Let us say you have this simple DbContext:</p> <pre><code class="language-cs">public class SomedbContext : DbContext { public virtual DbSet&lt;Blog&gt; Blogs =&gt; Set&lt;Blog&gt;(); public virtual DbSet&lt;Post&gt; Posts =&gt; Set&lt;Post&gt;(); } </code></pre> <blockquote> <p>Do note I am using MOQ to build fake objects, and that I have made these properties <code>virtual</code> to allow MOQ to override their implementation.</p> </blockquote> <p>Now I need some fake data, and turn it into <code>IQueryable&lt;T&gt;</code>:</p> <pre><code class="language-cs">IQueryable&lt;Blog&gt; blogs = FakeData.FakeBlogs.AsQueryable(); IQueryable&lt;Post&gt; posts = FakeData.FakePosts.AsQueryable(); </code></pre> <p>This allows me to build a fake <code>DbContext</code> as follows:</p> <pre><code class="language-cs">SomedbContext db = new SomedbContext() .WithTable(db =&gt; db.Blogs, blogs) .WithTable(db =&gt; db.Posts, posts) .Build(); </code></pre> <p>Now I can pass this fake <code>DbContext</code> to the intended class as a dependency, and it will use my fake data during the test, and no real database is needed.</p> <blockquote> <p>You might also consider simply using the InMemory database provided by EF.</p> </blockquote> <p>So how does this work?</p> <p>Let us examine following piece of code:</p> <pre><code class="language-cs">SomedbContext db = new SomedbContext() .WithTable(db =&gt; db.Blogs, blogs) .WithTable(db =&gt; db.Posts, posts) .Build(); </code></pre> <p>The first <code>WithTable</code> method is an extension method which returns a fluid builder instance:</p> <pre><code class="language-cs">public static class DbContextExtensions { public static FakeDbContextBuilder&lt;TDB&gt; WithTable&lt;TDB, E&gt;( this TDB dbContext, Expression&lt;Func&lt;TDB, DbSet&lt;E&gt;&gt;&gt; exp, IQueryable&lt;E&gt; data) where TDB : DbContext where E : class { FakeDbContextBuilder&lt;TDB&gt; builder = new(); return builder.WithTable(exp, data); } </code></pre> <p>Using the power of generics, I can apply this method to any <code>DbContext</code>, passing the <code>DbSet&lt;E&gt;</code> I want to replace, and the data that it should be replaced with.</p> <pre><code class="language-cs">new SomedbContext().WithTable(db =&gt; db.Blogs, blogs) </code></pre> <p>There are three arguments involved:</p> <ol> <li>The <code>WithTable</code> method is an extension method, so a <code>SomedbContext</code> instance is passed as the first argument.</li> <li>The second argument in an <code>Expression&lt;Func&lt;SomedbContext, DbSet&lt;Blog&gt;&gt;&gt;</code>, and <code>db =&gt; db.Blogs</code> will be passed allows us to replace <code>Blogs</code> with the fake data.</li> <li>The third argument is the fake data passed as an <code>IQueryable&lt;Blog&gt;</code>.</li> </ol> <p>All of this gets passed to the <code>FakeDbContextBuilder&lt;TDB&gt;</code> fluent builder's <code>WithTable</code> extension method. Let us have a look at this:</p> <pre><code class="language-cs">public static FakeDbContextBuilder&lt;TDB&gt; WithTable&lt;TDB, E&gt;( this FakeDbContextBuilder&lt;TDB&gt; builder, Expression&lt;Func&lt;TDB, DbSet&lt;E&gt;&gt;&gt; exp, IQueryable&lt;E&gt; data) where TDB : DbContext where E : class { Mock&lt;DbSet&lt;E&gt;&gt; mockSet = new(); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.Provider) .Returns(data.Provider); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.Expression) .Returns(data.Expression); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.ElementType) .Returns(data.ElementType); mockSet.As&lt;IQueryable&lt;E&gt;&gt;() .Setup(m =&gt; m.GetEnumerator()) .Returns(data.GetEnumerator); builder.Replace(exp, mockSet.Object); return builder; } </code></pre> <p>This method has the same arguments as the first <code>WithTable</code> method.</p> <p>What this method does it creates a fake <code>DbSet&lt;E&gt;</code> using MOQ, and then proceeds to relay all of its properties to the <code>IQueryable&lt;T&gt;</code> fake data.</p> <p>Once this is done, we tell the fluent builder to replace a property in the <code>DbContext</code>, for example replace <code>db =&gt; db.Blogs</code> with the fake <code>DbSet&lt;E&gt;</code>.</p> <p>Now it is time to examine the <code>FakeDbContextBuilder&lt;TDB&gt;</code> class:</p> <pre><code class="language-cs">public class FakeDbContextBuilder&lt;TDB&gt; where TDB : DbContext { private Mock&lt;TDB&gt; moq = new(); public TDB Build() =&gt; moq.Object; public void Replace&lt;E&gt;(Expression&lt;Func&lt;TDB, DbSet&lt;E&gt;&gt;&gt; exp, DbSet&lt;E&gt; e) where E : class { moq.Setup(exp).Returns(e); } } </code></pre> <p>This class keeps track of a <code>Mock&lt;SomedbContext&gt;</code>, and replaces each <code>DbSet&lt;E&gt;</code> property if needed. This is done in the <code>Replace</code> method.</p> <p>At the end of the call chain, you should call <code>Build</code>, which returns the fake <code>DbContext</code>.</p> <p>You don't even have to replace each <code>DbSet</code>, pick you ones you need.</p> <p>In this blog post I wanted to demonstrate how easy it is to replace any <code>DbContext</code> with fake data, and show you an implementation that make the life of the user (you) easy and hides away the details on how to do this...</p> Tue, 14 Feb 2023 11:52:00 Zhttps://blogs.u2u.be/peter/post/faking-dbcontext-in-tests#commentpeter@u2u.behttps://blogs.u2u.be/peter/pingback.axdhttps://blogs.u2u.be/peter/post.aspx?id=fa254730-b819-4f66-b534-3ab8fa6d1ab90https://blogs.u2u.be/peter/trackback.axd?id=fa254730-b819-4f66-b534-3ab8fa6d1ab9https://blogs.u2u.be/peter/post/faking-dbcontext-in-tests#commenthttps://blogs.u2u.be/peter/syndication.axd?post=fa254730-b819-4f66-b534-3ab8fa6d1ab9https://blogs.u2u.be/peter/post.aspx?id=a2603239-5a64-421d-8671-3ab6ee27d7fbhttps://blogs.u2u.be/peter/post/chatgpts-singleton-implementationChatGPTs Singleton Implementation<p>I asked ChatGPT to write me some code:</p> <p><img src="https://blogs.u2u.be/peter/image.axd?picture=/Peter/ChatGPT1.png" alt="/Peter/ChatGPT1.png" /></p> <p>This was the answer I got:</p> <p><img src="https://blogs.u2u.be/peter/image.axd?picture=/Peter/ChatGPT2.png" alt="/Peter/ChatGPT2.png" /></p> <p>Including this code fragment</p> <pre><code class="language-CS">public sealed class Singleton { private static Singleton instance = null; private static readonly object padlock = new object(); private Singleton() {} public static Singleton Instance { get { lock (padlock) { if (instance == null) { instance = new Singleton(); } return instance; } } } } </code></pre> <p>Nice piece of code, taking care of lazy instantiation, and thread-safe. But I don't think ChatGPT is going to replace a well-trained developer yet. Even in this code there are optimizations, like checking for <code>null</code> before taking the lock:</p> <pre><code class="language-CS"> get { if (instance == null) { lock (padlock) { if (instance == null) { instance = new Singleton(); } return instance; } } } </code></pre> <p>But what I am really missing is the .NET way of implementing singleton: Let the runtime take care of this by this simple piece of code:</p> <pre><code class="language-CS">public sealed class Singleton { private Singleton() { } private static Singleton Instance { get; } = new Singleton(); } </code></pre> <p>Here the static constructor will take care of lazy instantiation in a thread-safe way.</p> Mon, 13 Feb 2023 14:46:00 Zhttps://blogs.u2u.be/peter/post/chatgpts-singleton-implementation#commentpeter@u2u.behttps://blogs.u2u.be/peter/pingback.axdhttps://blogs.u2u.be/peter/post.aspx?id=a2603239-5a64-421d-8671-3ab6ee27d7fb0https://blogs.u2u.be/peter/trackback.axd?id=a2603239-5a64-421d-8671-3ab6ee27d7fbhttps://blogs.u2u.be/peter/post/chatgpts-singleton-implementation#commenthttps://blogs.u2u.be/peter/syndication.axd?post=a2603239-5a64-421d-8671-3ab6ee27d7fbhttps://blogs.u2u.be/peter/post.aspx?id=13543fa9-5377-4d49-a3c8-362ff7a022ebhttps://blogs.u2u.be/peter/post/prefer-valuetask-t-for-interfacesPrefer ValueTask<T> for Interfaces<p><img src="https://blogs.u2u.be/peter/image.axd?picture=/Peter/choice2.jpg" alt="/Peter/choice2.jpg" /></p> <p>Declaring interfaces for your .NET classes gives you the choice: synchronous or asynchronous. Why not both?</p> <h2>Using Task<T> for interfaces</h2> <p>Let's look at this interface:</p> <pre><code class="language-csharp">interface IProductRepo { IEnumerable&lt;Product&gt; GetProducts(); } </code></pre> <p>The <code>GetProducts</code> method returns some collection of products.</p> <p>However, this interface declaration is restrictive, meaning that we should implement this method synchronously. When talking to a database this will block the current thread which we don't want.</p> <p>The alternative? We can use <code>Task&lt;T&gt;</code>:</p> <pre><code class="language-csharp">interface IProductRepo { Task&lt;IEnumerable&lt;Product&gt;&gt; GetProductsAsync(); } </code></pre> <p>Again, this is restrictive because this method should always return a <code>Task&lt;T&gt;</code>, which being a reference type results in the allocation of a new <code>Task</code> on the heap, even when implementing this method synchronously.</p> <p>Is there a way we can declare an interface to support both a synchronous and asynchronous implementation?</p> <h2>Using ValueTask<T> for interfaces</h2> <p>Yes, there is! We can use <code>ValueTask&lt;T&gt;</code>! This type represents the union of <code>T</code> and <code>Task&lt;T&gt;</code> and allows you to implement a method synchronously or asynchronously.</p> <p>The interface should now look like this:</p> <pre><code class="language-csharp">interface IProductRepo { ValueTask&lt;IEnumerable&lt;Product&gt;&gt; GetProductsAsync(); } </code></pre> <p><code>ValueTask&lt;T&gt;</code> is a value type, so when implementing this method synchronously this will result in no heap allocation for <code>Task&lt;T&gt;</code>. This will also allow you to implement the <code>GetProductsAsync</code> method asynchronously which does result in a <code>Task&lt;T&gt;</code> allocation on the heap.</p> <p><code>ValueTask&lt;T&gt;</code> gives you the choice! And the .NET runtime takes care of <a href="https://github.com/dotnet/coreclr/pull/26310">optimization</a>.</p> <p>Do note that the method should be named <code>GetProductsAsync</code> because you should invoke this using the <code>await</code> syntax!</p> Mon, 30 Jan 2023 12:40:00 Zhttps://blogs.u2u.be/peter/post/prefer-valuetask-t-for-interfaces#commentpeter@u2u.behttps://blogs.u2u.be/peter/pingback.axdhttps://blogs.u2u.be/peter/post.aspx?id=13543fa9-5377-4d49-a3c8-362ff7a022eb0https://blogs.u2u.be/peter/trackback.axd?id=13543fa9-5377-4d49-a3c8-362ff7a022ebhttps://blogs.u2u.be/peter/post/prefer-valuetask-t-for-interfaces#commenthttps://blogs.u2u.be/peter/syndication.axd?post=13543fa9-5377-4d49-a3c8-362ff7a022ebhttps://blogs.u2u.be/peter/post.aspx?id=a0eeacba-41b4-48f5-824c-fb836de4367chttps://blogs.u2u.be/peter/post/debugging-jwt-tokens-in-net-6Debugging JWT tokens in .NET 6<p><img src="https://blogs.u2u.be/peter/image.axd?picture=/Peter/debugging.jpeg" alt="Debugging" /></p> <p>Currently I am doing some consultancy and got asked to port a Web API to .NET 6 which uses JWT tokens for authorization.</p> <p>In .NET 6 using JWT tokens is quite easy, especially when in combination with Azure AD.</p> <p>Here is my configuration for dependency injection:</p> <pre><code class="language-cs">var azureAdSection = builder.Configuration.GetSection(&quot;AzureAd&quot;); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(azureAdSection); </code></pre> <p>Still every time my access token from the client got rejected. Why?</p> <p>I started searching the web but could not find anything up to date. Then my eye saw that in the <code>AddAuthentication</code> method you can pass a <code>bool</code>:</p> <pre><code class="language-cs">var azureAdSection = builder.Configuration.GetSection(&quot;AzureAd&quot;); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(azureAdSection, subscribeToJwtBearerMiddlewareDiagnosticsEvents: true); </code></pre> <p>Running this told my my audience was wrong. But what was the expected value?</p> <p>Seems you can also tell logging to reveal the values of the tokens with:</p> <pre><code>var azureAdSection = builder.Configuration.GetSection(&quot;AzureAd&quot;); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(azureAdSection, subscribeToJwtBearerMiddlewareDiagnosticsEvents: true); IdentityModelEventSource.ShowPII = true; </code></pre> <p>Running the WebAPI would log everything in the console, even telling me about the expected value for the audience. Nice!</p> <p>Hope this helps you with debugging JWT tokens in .NET 6!</p> Sun, 04 Sep 2022 18:08:00 Zhttps://blogs.u2u.be/peter/post/debugging-jwt-tokens-in-net-6#commentpeter@u2u.behttps://blogs.u2u.be/peter/pingback.axdhttps://blogs.u2u.be/peter/post.aspx?id=a0eeacba-41b4-48f5-824c-fb836de4367c0https://blogs.u2u.be/peter/trackback.axd?id=a0eeacba-41b4-48f5-824c-fb836de4367chttps://blogs.u2u.be/peter/post/debugging-jwt-tokens-in-net-6#commenthttps://blogs.u2u.be/peter/syndication.axd?post=a0eeacba-41b4-48f5-824c-fb836de4367c