<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Marius Wodtke</title>
        <link>https://www.marius-wodtke.de/</link>
        <description>Recent content on Marius Wodtke</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Mon, 20 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.marius-wodtke.de/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Revisited: Managed Identities for Plugin Packages</title>
        <link>https://www.marius-wodtke.de/post/managed-identity/revisited/</link>
        <pubDate>Sun, 21 Jun 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/managed-identity/revisited/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/cover.jpg" alt="Featured image of post Revisited: Managed Identities for Plugin Packages" /&gt;&lt;p&gt;We already had a first shot at Managed Identities for Plugins 2 years ago, but since then, the feature has left the preview stage and is now generally available. This also brought a new version of the subject identifier to be used for new managed identity records in Dynamics. And I already teased in the previous article that we will need a proper ALM to use this feature in a production environment. So today we kick off a gain with a &amp;ldquo;Getting Started&amp;rdquo; to then expand with automation for developers and CI/CD pipelines in the next articles.&lt;/p&gt;
&lt;p&gt;There is also &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-platform/admin/set-up-managed-identity&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;an official Microsoft documentation&lt;/a&gt;, but I will diverge from that a bit because I&amp;rsquo;m already thinking about my next steps with multiple developers and pipelines.&lt;/p&gt;
&lt;h2 id=&#34;the-code&#34;&gt;The Code&lt;/h2&gt;
&lt;p&gt;Because this is a new project, we want to use plugin packages of course!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pac plugin init -o ManagedIdentitySample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the code I really only ran a sample that lets me debug this well, so the Plugin1.cs got some additions, the 3 highlighted lines are what is important for your own adventure. The rest of my code will make sure that the returned JWT will be persisted to logs and the description field of the Account I am triggering on to easily copy it out and for example analyze it further with a tool like &lt;a class=&#34;link&#34; href=&#34;https://jwt.ms&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;jwt.ms&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;protected&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;override&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; ExecuteDataversePlugin(ILocalPluginContext localPluginContext)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (localPluginContext == &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArgumentNullException(nameof(localPluginContext));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; context = localPluginContext.PluginExecutionContext;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; managedIdentity = localPluginContext.ServiceProvider.Get&amp;lt;IManagedIdentityService&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Acquiring Token&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; scope = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://vault.azure.net/.default&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; token = managedIdentity.AcquireToken(scope);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#if&lt;/span&gt; DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Token: {token}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; updateAccount = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Entity(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;account&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Id = context.PrimaryEntityId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;        [&amp;#34;description&amp;#34;]&lt;/span&gt; = token
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.PluginUserService.Update(updateAccount);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We retrieve the &lt;code&gt;IManagedIdentityService&lt;/code&gt;, then build a scope and then request a token for this scope. If everything is correct, &lt;code&gt;AcquireToken&lt;/code&gt; will return a JWT, a JSON Web Token, that can be used for authentication/authorization. Of course this only works for services that accept this kind of auth, for example all Azure services. Azure Key Vault is chosen here on purpose because it can securely store other credentials like username and password for services that do not accept JWT. For these, the Managed Identity in combination with the Key Vault still provide a bootstrap for secure communication.&lt;/p&gt;
&lt;h2 id=&#34;generating-the-certificate&#34;&gt;Generating the Certificate&lt;/h2&gt;
&lt;p&gt;For Managed Identity to work, our plugin package needs to be signed. And it&amp;rsquo;s not enough to use the Signing Key we are used to from Plugin Assemblies deployed to Dataverse, no the NuGet package needs to be signed after packaging. For that matter we will need a Certificate with a private key.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve put a script below that will automate this mostly, please notice that you should&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;replace the variables at the top of the file with your own ones&lt;/li&gt;
&lt;li&gt;choose a good CommonName as we will need this later for retrieval, it should be unique on everyones computer, so simply your companies name is not enough. But it should also be the same for everyone and the build server, so maybe something like the Assembly name which may already have your Company in there like a &amp;ldquo;Microsoft.CRM.Plugins.ERPSynchronization&amp;rdquo; might work well&lt;/li&gt;
&lt;li&gt;receive a warning when the script tries to trust the generated certificate, this is optional, but if you don&amp;rsquo;t you will receive annoying warnings every time you save about the certificate not being trusted.&lt;/li&gt;
&lt;li&gt;save the &amp;ldquo;App Registration Values&amp;rdquo; that are printed in the end, we will need it later.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: The official docs use the Extension &amp;lsquo;2.5.29.37={text}1.3.6.1.5.5.7.3.4&amp;rsquo;, which in my testing was never deemed valid for signing a package by the NuGet command line tool, instead &amp;lsquo;2.5.29.37={text}1.3.6.1.5.5.7.3.3&amp;rsquo; (.3 in the end) is used as the valid extension for code signing.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;If you are unsure about the environmentId, you can find it in PPAC on your environment. tenantId is found in the Session Details under the setting cog icon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/EnvironmentId.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/TenantId.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$email = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MariusWodtke@MariusWodtkeMVP.onmicrosoft.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$commonName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MariusWodtkeMVP&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantId = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;738016bf-2f59-46ca-9660-357d1de3b865&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$environmentId = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;7efcc31a-c410-e20e-9b95-dd448cedf653&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Generate Cert&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$params = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Type = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Custom&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Subject = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;E=&lt;/span&gt;$email&lt;span style=&#34;color:#e6db74&#34;&gt;,CN=&lt;/span&gt;$commonName&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TextExtension = @(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2.5.29.37={text}1.3.6.1.5.5.7.3.3&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.5.29.17={text}email=&lt;/span&gt;$email&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    KeyAlgorithm = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;RSA&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    KeyLength = &lt;span style=&#34;color:#ae81ff&#34;&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SmimeCapabilities = $true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CertStoreLocation = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Cert:\CurrentUser\My&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$cert = New-SelfSignedCertificate @params
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;New Certificate generated&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# (Optional) Add to trusted certs to suppress warnings while signing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# This will open a dialog if you are sure you want to trust it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Root&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CurrentUser&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store.Open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ReadWrite&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store.Add($cert)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store.Close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Certificate installed to Trusted Root store&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Extract hash of cert for the Subject we need for the Federated Credential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$cerFilePath = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PSScriptRoot&lt;span style=&#34;color:#e6db74&#34;&gt;\&lt;/span&gt;$commonName&lt;span style=&#34;color:#e6db74&#34;&gt;.cer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Export-Certificate -Cert $cert -FilePath $cerFilePath
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Certificate exported to: &lt;/span&gt;$cerFilePath&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$certUtilOutput = CertUtil -hashfile $cerFilePath SHA256
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$hash = ($certUtilOutput | Select-Object -Index &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).Trim()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;SHA256 Hash: &lt;/span&gt;$hash&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Encode the TenantId for the Subject we need for the Federated Credential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantGuid = [&lt;span style=&#34;color:#66d9ef&#34;&gt;System.Guid&lt;/span&gt;]::Parse($tenantId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantHex = $tenantGuid.ToByteArray()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantBase64 = [&lt;span style=&#34;color:#66d9ef&#34;&gt;System.Convert&lt;/span&gt;]::ToBase64String($tenantHex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$encodedTenantId = $tenantBase64.Replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;).Replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;_&amp;#39;&lt;/span&gt;).TrimEnd(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Encoded Tenant ID: &lt;/span&gt;$encodedTenantId&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Print out the final subject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$subject = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/eid1/c/pub/t/&lt;/span&gt;$encodedTenantId&lt;span style=&#34;color:#e6db74&#34;&gt;/a/qzXoWDkuqUa3l6zM5mM0Rw/n/plugin/e/&lt;/span&gt;$environmentId&lt;span style=&#34;color:#e6db74&#34;&gt;/h/&lt;/span&gt;$hash&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$issuer = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://login.microsoftonline.com/&lt;/span&gt;$tenantId&lt;span style=&#34;color:#e6db74&#34;&gt;/v2.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;App Registration Values:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;`Issuer:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host $issuer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Subject Value:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host $subject
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Name:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host $email
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is a sample output&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PS C:\Repos\Kunter-Bunt\...\ManagedIdentitySample&amp;gt; .\GenerateCert.ps1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;New Certificate generated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Certificate installed to Trusted Root store
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Directory&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; C:\Repos\Kunter-Bunt\...\ManagedIdentitySample
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Mode                 LastWriteTime         Length Name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;----                 -------------         ------ ----
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-a----         &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;2026&lt;/span&gt;     &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;54&lt;/span&gt;           &lt;span style=&#34;color:#ae81ff&#34;&gt;1110&lt;/span&gt; MariusWodtkeMVP.cer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Certificate exported to&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; C:\Repos\Kunter-Bunt\...\ManagedIdentitySample\MariusWodtkeMVP.cer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SHA256 Hash&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; b7d2145e1e8f76faa41561e47b70eb3bc7ca5b2f337ded7480c21a343c8ac6c3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Encoded Tenant ID&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; vxaAc1kvykaWYDV9HeO4ZQ
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;App Registration Values&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Issuer&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;//login.microsoftonline.com/738016bf-2f59-46ca-&lt;span style=&#34;color:#ae81ff&#34;&gt;9660&lt;/span&gt;-357d1de3b865/v2.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Subject Value&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/eid1/c/pub/t/vxaAc1kvykaWYDV9HeO4ZQ/a/qzXoWDkuqUa3l6zM5mM0Rw/n/plugin/e/7efcc31a-c410-e20e-9b95-dd448cedf653/h/b7d2145e1e8f76faa41561e47b70eb3bc7ca5b2f337ded7480c21a343c8ac6c3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MariusWodtke@MariusWodtkeMVP.onmicrosoft.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;signing-the-package&#34;&gt;Signing the Package&lt;/h2&gt;
&lt;p&gt;Now we start do deviate from the official documentation. They use an exported .pfx file with a password to sign their packages, but I want to automate signing as a post build step and don&amp;rsquo;t want to a password in the project file nor do I want to rely on unprotected .pfx files. Therefore we will search the Certificate by CN (CommonName) from the Certificate Store of Windows. Because it&amp;rsquo;s already loaded to the store, no password is required. And here you see the reason why I wanted to have a unique but common among developer and build machines name for CN as &lt;code&gt;-CertificateSubjectName &#39;WhateverYourCNIs&#39;&lt;/code&gt; will be hardcoded.&lt;/p&gt;
&lt;p&gt;For now I will use the nuget sign command manually, but the plan is to deliver an automation in another article.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PS C:\Repos\Kunter-Bunt\...\ManagedIdentitySample\bin\Debug&amp;gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt; nuget sign .\ManagedIdentitySample.1.0.0.nupkg -CertificateStoreLocation CurrentUser -CertificateStoreName My -CertificateSubjectName &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MariusWodtkeMVP&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WARNING&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; NU3002&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; The &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-Timestamper&amp;#39;&lt;/span&gt; option was not provided. The signed package will not be timestamped. To learn more about this option, please visit https&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;//docs.nuget.org/docs/reference/command-line-reference
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Signing package(s) with certificate&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Subject Name&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; E=MariusWodtke@MariusWodtkeMVP.onmicrosoft.com, CN=MariusWodtkeMVP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  SHA1 hash&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; 313D5BD2A5E36DB2B7211DC1B9D99C9836825636
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  SHA256 hash&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; B7D2145E1E8F76FAA41561E47B70EB3BC7CA5B2F337DED7480C21A343C8AC6C3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Issued by&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; E=MariusWodtke@MariusWodtkeMVP.onmicrosoft.com, CN=MariusWodtkeMVP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Valid from&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;2026&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;44&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt; to &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;2027&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;04&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Package(s) signed successfully.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can register the package in Dataverse and for testing I also registered an asynchronous step on updating telephone1 of Account.&lt;/p&gt;
&lt;h2 id=&#34;creating-the-app-registration&#34;&gt;Creating the App Registration&lt;/h2&gt;
&lt;p&gt;Now we have a signed package in Dataverse and code inside that tries to authenticate with a Managed Identity, but what identity? We will create an AppRegistration here, no special setting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/AppReg.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then we create a &lt;em&gt;Federated Credential&lt;/em&gt; under Certificates &amp;amp; Secrets.
Here you will need the issuer and subject that you saved after executing the script to generate the secret. Subject Value is the &lt;em&gt;Value&lt;/em&gt; needed here.&lt;/p&gt;
&lt;p&gt;For name I still have the certificates thumbprint in the screenshot, however, developer name (I used email in the script) is a better choice here. It&amp;rsquo;s easier to associate entries with people when needing to invalidate a leaving member or needing to update your certificate after it reached its validity date.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/Credential.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;creating-the-managed-identity&#34;&gt;Creating the Managed Identity&lt;/h2&gt;
&lt;p&gt;And finally we need to tell Dynamics what App Registration matches our signed Plugin Package. Fortunately David Rivard wrote an XrmToolbox tool for this matter. Really simple, select your Assembly, &lt;em&gt;Link to a New Identity&lt;/em&gt; and use the TenantId and ApplicationId (marked that one for you in the screenshot of the AppRegistration overview).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/XrmToolbox.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The Result&lt;/h2&gt;
&lt;p&gt;Now we have everything in place and the &amp;ldquo;If everything is correct&amp;rdquo; should be fulfilled now, so if I update my Accounts Main Phone, I can see the token in the Description field. Still, that is not a use case and you don&amp;rsquo;t want to hand out the tokens to anyone except the receiving application but it clearly shows the feature working.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/Result.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Managed Identities provide the bootstrap for external authentication that we were waiting for a long time. These are the first steps towards Plugin Packages that will leverage this feature to enable communication without exposing credentials somewhere.&lt;/p&gt;
&lt;p&gt;For this we generate a Certificate with the provided script. This Certificate is used for signing our Plugin Package before registering in Dataverse. Then we create an App Registration &amp;amp; Federated Credential with the values returned from the script. Finally, we associate the Assembly with the App Registration using a Managed Identity record in Dataverse. And now we can use the &lt;code&gt;AcquireToken&lt;/code&gt; method of the &lt;code&gt;IManagedIdentityService&lt;/code&gt; to obtain a JWT for a given service.&lt;/p&gt;
&lt;p&gt;The solution presented here is functional, however, there are ALM parts still missing, check &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/managed-identity/&#34; &gt;the series&lt;/a&gt; with upcoming articles on how to automate signing for development workflows and how a pipeline could work for deployment to get a production ready feature.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[Video] Server Logic with Custom APIs in Practice</title>
        <link>https://www.marius-wodtke.de/post/power-pages/serverlogic/practice/</link>
        <pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/serverlogic/practice/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/serverlogic/cover.jpg" alt="Featured image of post [Video] Server Logic with Custom APIs in Practice" /&gt;&lt;p&gt;This time we put Server Logic into practice to solve a practical problem: My custom Training Event entity offers both public and private events and allowing a Portal User to read all of them could leak information on the private events. Server Logic and Custom APIs will help us to retrieve the right amount of Training Events without assigning excessive privileges to our users.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/9c9SXWrxneE&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/9c9SXWrxneE&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/serverlogic/practice/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Having the right view on data</title>
        <link>https://www.marius-wodtke.de/post/other/the-right-view/</link>
        <pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/the-right-view/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/cover.jpg" alt="Featured image of post Having the right view on data" /&gt;&lt;p&gt;No, this is not going to be a political discussion about data interpretation, it&amp;rsquo;s about which view does what. You will certainly be familiar with the so called &lt;em&gt;Public Views&lt;/em&gt;, these are the ones the Model Driven App displays when you select a table in the navigation, but what&amp;rsquo;s a &lt;em&gt;Quick Find View&lt;/em&gt; and a &lt;em&gt;Advanced Find View&lt;/em&gt;? Let&amp;rsquo;s find out!&lt;/p&gt;
&lt;h2 id=&#34;public-view&#34;&gt;Public View&lt;/h2&gt;
&lt;p&gt;This is the only view type that you can create for an existing table! You will not be able to choose a type when creating a new view, it will always be a &lt;em&gt;Public View&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But that means, in contrast to the other view types, that you will have to choose a default view for a table. This view will be used when the table is selected in the navigation of an app (at least if the view is part of the app) and as well in customization when adding a subgrid to a form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/SetDefault.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Two public views are created for you when create a table X, &amp;ldquo;Active Xs&amp;rdquo; and &amp;ldquo;Inactive Xs&amp;rdquo;. This may not make sense for your table, you can change, rename, add and remove public views at will, only the default view cannot be removed, which also ensures there is always at least one public view present.&lt;/p&gt;
&lt;p&gt;No matter if you chose to add a new view or change an existing one, the editor offers options, to add/remove columns and change their width. You can also manipulate sorting and filters.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/Edit1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/Edit2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If you are changing filters, make sure to reflect that on the name, ending up with &amp;ldquo;Active Xs&amp;rdquo; that actually shows all Xs will confuse everyone!&lt;/p&gt;
&lt;p&gt;And finally, don&amp;rsquo;t forget to &lt;em&gt;Save and Publish&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;lookup-view&#34;&gt;Lookup View&lt;/h2&gt;
&lt;p&gt;Now we have customizing out of the way, we can focus on the special views. Since you can&amp;rsquo;t create new ones, you will edit the existing view if required.&lt;/p&gt;
&lt;p&gt;This view used by default for Lookups. This can be changed on forms, but there are parts where you do not have such a selection! Examples will be the &lt;em&gt;Lookup Objects&lt;/em&gt; dialog that is opened when associating records N:N or the &lt;em&gt;Assign&lt;/em&gt; dialog where the default &lt;em&gt;Lookup View&lt;/em&gt; is fixed.&lt;/p&gt;
&lt;p&gt;The first two columns will be shown to the user in the dialog, by default, those are the primary field (usually Name) and Created On. In my experience Created On is rarely helpful for the user, but fortunately we can change that. You can also go beyond those two columns, they will not directly be shown but can be unfolded per record with the chevron icon, so make sure to put the two most distinguishing columns up first.&lt;br&gt;
All columns shown can also be searched, but they are search with a &amp;ldquo;begins with&amp;rdquo;, which should also be considered. For example with alphanumeric key with a prefix, e.g &amp;ldquo;TST-4234&amp;rdquo;, here you won&amp;rsquo;t find the record by typing &amp;ldquo;4234&amp;rdquo;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/Lookup1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/Lookup2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;2 more notes regarding the column order: If say the third column is matched by a search it will move up to the spot of the second column, overwriting the set column sorting. This makes it more transparent to the user why this record is shown for their search.&lt;br&gt;
And if the second column is empty, the third column will also move up. This also makes sense, but can be a bit confusing if the second column is empty for some records creating a mix of columns being shown in the results.&lt;/p&gt;
&lt;p&gt;If you choose not to use the primary field (usually Name) as first column or at all, be aware that it will still be taken as the value shown in the Lookup after selecting a record.&lt;/p&gt;
&lt;h2 id=&#34;quick-find-view&#34;&gt;Quick Find View&lt;/h2&gt;
&lt;p&gt;The quick find as such has actually vanished on my apps. This view was shown when you used the search bar on the top right of a view. Instead the Copilot search bar is available to &amp;ldquo;Ask about data in this table&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;But that does not make this view useless! While the columns configured on the left pane are not relevant anymore as the Copilot search will keep the columns of the selected view (so for example &amp;ldquo;Active Xs&amp;rdquo;), the &lt;em&gt;Find By&lt;/em&gt; columns in the right pane are still important. This section is only found on the &lt;em&gt;Quick Find View&lt;/em&gt; and determines what fields the user can search for. They are also relevant for the &lt;em&gt;Dataverse Search&lt;/em&gt;, but that&amp;rsquo;s something for a separate article.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/QuickFind1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now that we have configured the Owning Business Unit (my BU is called &amp;ldquo;mariuswodtke-mvp&amp;rdquo;), we can search for it. Even though the column is not present in the view! But sadly, this is not a &amp;ldquo;contains&amp;rdquo; search, but a &amp;ldquo;begins with&amp;rdquo;, making the second query not find the record although it would fit with a contains search.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/QuickFind2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Copilot search bar replaced the old quick find.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/QuickFind3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Even though not present, records of the given Owning Business Unit “mariuswodtke-mvp” can be search.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/QuickFind4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;But they are searched with begins with, and “mariuswodtke-mvp” does not begin with “wodtke-”.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For me right now, the &amp;ldquo;Advanced Filters&amp;rdquo; do not display the added filter, making me unable to produce a screenshot, but here is the filter from the downloaded FetchXml:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-HTML&#34; data-lang=&#34;HTML&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;filter&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;or&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isquickfindfields&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;condition&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;attribute&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;like&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mariuswodtke%&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;condition&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;attribute&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;owningbusinessunitname&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;like&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mariuswodtke%&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;filter&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;filter&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;and&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;condition&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;attribute&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;statecode&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eq&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;filter&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;associated-view&#34;&gt;Associated View&lt;/h2&gt;
&lt;p&gt;This view is shown by default whenever you select a Relationship in the &lt;em&gt;Related&lt;/em&gt; navigation of a form. The user can switch to other views in the app, but of course they would prefer you to configure meaningful columns for this view so that they may save the extra clicks. And think about the filter as well, the default one for active records is not always the right way to go.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/Associated1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;advanced-find-view&#34;&gt;Advanced Find View&lt;/h2&gt;
&lt;p&gt;Now this one is really obsolete by now, I wouldn&amp;rsquo;t worry about. Advanced Find is still a thing, you can use to for example explore tables that are in your app, but not in the navigation, but this will show the default &lt;em&gt;Public View&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/AdvancedFind1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/AdvancedFind2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/AdvancedFind3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The filters of the view are automatically opened.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Advanced Find View&lt;/em&gt; is still accessible with detours:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/AdvancedFind4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Go to the Power Platform Environment Settings app, use the filter icon and then Switch to Classic.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/AdvancedFind6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Select the table in Look for, now Use Saved View with the name “[new]” is preselected for you. This is the Advanced Find view, the custom filter I applied in customization (on the right) is also applied here.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/the-right-view/AdvancedFind5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;For every situation the right view. &lt;em&gt;Public Views&lt;/em&gt; are the only ones creatable by you and are usually the ones you can select manually in several customization spaces like Form- and App-Designer. But sometimes those options are not given, then the other views become important.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Lookup View&lt;/em&gt; is used by default for any lookups to the given table, in the Form Designer you can usually adapt this, but some dialogs and for example Business Process Flows do not have an option to change away from that view.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Quick Find View&lt;/em&gt; has special options for &lt;em&gt;Find by&lt;/em&gt;, these are used by the search bar and Dataverse search. Just keep in mind that these work with a &amp;ldquo;begins with&amp;rdquo; filter.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Associated View&lt;/em&gt; is used for the &lt;em&gt;Related&lt;/em&gt; navigation in a form, the user can switch here to other views, but that requires extra clicks every time they open the related subgrid.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Advanced Find View&lt;/em&gt; became irrelevant with the move to the modern UI as it is only used in the classic advanced find accessible trough the Power Platform Environment Settings app.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[Video] Calling Unbound Custom APIs with Power Pages Server Logic</title>
        <link>https://www.marius-wodtke.de/post/power-pages/serverlogic/unbound-api/</link>
        <pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/serverlogic/unbound-api/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/serverlogic/cover.jpg" alt="Featured image of post [Video] Calling Unbound Custom APIs with Power Pages Server Logic" /&gt;&lt;p&gt;Microsoft has just made Server Logic generally available and it now includes a function to call unbound custom APIs.&lt;/p&gt;
&lt;p&gt;With Flows and my solution &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt; this is our third option for custom APIs.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/mSM0Tg7RXWI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/mSM0Tg7RXWI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/serverlogic/unbound-api/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Virtual Tables: Implementing custom RetrieveMultiple</title>
        <link>https://www.marius-wodtke.de/post/virtual-tables/retrievemultiple/</link>
        <pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/virtual-tables/retrievemultiple/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/virtual-tables/retrievemultiple/cover.jpg" alt="Featured image of post Virtual Tables: Implementing custom RetrieveMultiple" /&gt;&lt;p&gt;A while back we took a look at virtual tables with &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/externaltable/&#34; &gt;the wizard for connecting to SQL Databases&lt;/a&gt;. Today we will again connect an SQL Database, but this time &amp;ldquo;on foot&amp;rdquo; with implementing the data layer on our own.&lt;/p&gt;
&lt;p&gt;There is of course maintenance to be considered when implementing ourselves, but it also offers the possibility to do some custom transformations like if your source DB does not provide a guid column or you want to do some row level access (virtual tables are always organization owned).&lt;/p&gt;
&lt;p&gt;For this matter, &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/virtual-entities/sample-ve-provider-crud-operations&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MS provides quite a good sample&lt;/a&gt;, however, for RetrieveMultiple they simply return everything without applying a single bit of filtering, sorting or selecting columns. This will be our focus for today, the article will mainly be code snippets of the different options from fetch.&lt;/p&gt;
&lt;h2 id=&#34;select&#34;&gt;SELECT&lt;/h2&gt;
&lt;p&gt;To help me with all of these functions, I&amp;rsquo;ve first created a dictionary for mapping between columns of the DB and Dataverse. If you want to be generic, need to manage many tables or expect frequent changes to the schema, this can be constructed from Metadata with the &lt;em&gt;External Name&lt;/em&gt;. But I expect custom requirements here and have constructed a nice primary field from first and lastname that was not present in source.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FieldMapping&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; ColumnName { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Type ColumnType { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; IsComputed { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; } = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; IsIdentity { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; } = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; ComputedColumnAlias { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, FieldMapping&amp;gt; FieldMappings = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, FieldMapping&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_personid&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(Guid), IsIdentity = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_firstname&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Firstname&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_lastname&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Lastname&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(ISNULL(Firstname, &amp;#39;&amp;#39;) + &amp;#39; &amp;#39; + ISNULL(Lastname, &amp;#39;&amp;#39;))&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, ComputedColumnAlias = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt; } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now we simply iterate the columns in the given &lt;code&gt;QueryExpression&lt;/code&gt;, map them from the given Dataverse column to the DB column and join them with a comma. The method is a little longer than this description as we need to consider all columns being retrieved, my computed column and the Dataverse behavior that the ID is returned even if it is not requested.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; EntityCollection RetrieveMultiple(QueryExpression query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; selectClause = BuildSelectClause();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; sqlQuery = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; StringBuilder(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SELECT &amp;#34;&lt;/span&gt; + selectClause + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; FROM dbo.Persons&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; BuildSelectClause(ColumnSet columnSet)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; selectColumns = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; columnsToInclude = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (columnSet.AllColumns)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        columnsToInclude.AddRange(FieldMappings.Keys);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        columnsToInclude.AddRange(columnSet.Columns.ToArray());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; columnToInclude &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; columnsToInclude)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (FieldMappings.TryGetValue(columnToInclude, &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; mapping))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mapping.IsComputed)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                selectColumns.Add(mapping.ColumnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; AS &amp;#34;&lt;/span&gt; + mapping.ComputedColumnAlias);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                selectColumns.Add(mapping.ColumnName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!selectColumns.Contains(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        selectColumns.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, selectColumns);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, how do we get a &lt;code&gt;QueryExpression&lt;/code&gt; as input for the function? Easy, we can just construct it with a RetrieveMultipleRequest. This works no matter if the &lt;code&gt;InputParameters&lt;/code&gt; contain a QueryExpression in the first place or a FetchXml string.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; context = serviceProvider.Get&amp;lt;IPluginExecutionContext&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; request = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RetrieveMultipleRequest { Parameters = context.InputParameters };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; query = (QueryExpression)request.Query;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;where&#34;&gt;WHERE&lt;/h2&gt;
&lt;p&gt;For mapping the WHERE, we need to consider that filters may contain filters. So &lt;code&gt;BuildWhereClause&lt;/code&gt; may call itself recursively and needs to capture with brackets that nested filters may use different operators (&amp;amp;&amp;amp;, ||).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; EntityCollection RetrieveMultiple(QueryExpression query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (query.Criteria != &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; (query.Criteria.Conditions.Count &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; || query.Criteria.Filters.Count &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sqlQuery.Append(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; WHERE &amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; whereClause = BuildWhereClause(query.Criteria, command);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sqlQuery.Append(whereClause);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; BuildWhereClause(FilterExpression filter, SqlCommand command)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; clauses = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; condition &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; filter.Conditions)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; clause = BuildConditionClause(condition, command);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(clause))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            clauses.Add(clause);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; childFilter &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; filter.Filters)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; childClause = BuildWhereClause(childFilter, command);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(childClause))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            clauses.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&amp;#34;&lt;/span&gt; + childClause + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (clauses.Count == &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; logicalOperator = filter.FilterOperator == LogicalOperator.And ? &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; AND &amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; OR &amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(logicalOperator, clauses);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the next section I&amp;rsquo;ll happily admit that I&amp;rsquo;m also lazy and use GitHub Copilot for this stuff. Some notes: The params get a randomized postfix to ensure uniqueness should one column be filtered with multiple values, e.g. mwo_firstname eq &amp;ldquo;Marius&amp;rdquo; or mwo_firstname eq &amp;ldquo;Ambesh&amp;rdquo;. &lt;code&gt;In&lt;/code&gt; operators have more than one value so we have to iterate them. And finally my switch is quite long but still missing some operators, for example the long list of Date operators, I suggest leveraging the AI again here to implement them as this is a typical time thief.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; BuildConditionClause(ConditionExpression condition, SqlCommand command)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; columnName = GetColumnName(condition.AttributeName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; paramName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt; + condition.AttributeName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_&amp;#34;&lt;/span&gt; + Guid.NewGuid().ToString(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;N&amp;#34;&lt;/span&gt;).Substring(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (condition.Operator)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.Equal:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; = &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.NotEqual:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;lt;&amp;gt; &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.Like:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; LIKE &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.Null:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; IS NULL&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.NotNull:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; IS NOT NULL&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.GreaterThan:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;gt; &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.GreaterEqual:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;gt;= &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.LessThan:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;lt; &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.LessEqual:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;lt;= &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.In:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (condition.Values.Count == &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 = 0&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; inParams = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i = &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &amp;lt; condition.Values.Count; i++)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; inParamName = paramName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_&amp;#34;&lt;/span&gt; + i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                command.Parameters.AddWithValue(inParamName, condition.Values[i] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                inParams.Add(inParamName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; IN (&amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, inParams) + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.NotIn:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (condition.Values.Count == &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 = 1&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; notInParams = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i = &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &amp;lt; condition.Values.Count; i++)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; notInParamName = paramName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_&amp;#34;&lt;/span&gt; + i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                command.Parameters.AddWithValue(notInParamName, condition.Values[i] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                notInParams.Add(notInParamName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; NOT IN (&amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, notInParams) + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; = &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; GetColumnName(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; attributeName)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (FieldMappings.TryGetValue(attributeName.ToLower(), &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; FieldMapping mapping))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; mapping.ColumnName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; attributeName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;order-by&#34;&gt;ORDER BY&lt;/h2&gt;
&lt;p&gt;Ordering is much simpler again, we can just concatenate the mapped column names with comma, the only thing to consider is wether the order is ascending or descending.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; EntityCollection RetrieveMultiple(QueryExpression query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (query.Orders != &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; query.Orders.Count &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sqlQuery.Append(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; ORDER BY &amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; orderClauses = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; order &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; query.Orders)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; columnName = GetColumnName(order.AttributeName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; direction = order.OrderType == OrderType.Descending ? &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DESC&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ASC&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            orderClauses.Add(columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; + direction);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sqlQuery.Append(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, orderClauses));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;top&#34;&gt;TOP&lt;/h2&gt;
&lt;p&gt;For top or count as it is often called in Dataverse contexts, we have a small problem: It needs to be inserted after the SELECT and before the names of the columns. You may have a different opinion of where to put the code, the pragmatic approach here was to just use the &lt;code&gt;Insert&lt;/code&gt; method to put it right behind the SELECT.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; EntityCollection RetrieveMultiple(QueryExpression query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (query.TopCount != &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; query.TopCount &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sqlQuery.Insert(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; TOP &amp;#34;&lt;/span&gt; + query.TopCount);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;reading-the-output&#34;&gt;Reading the Output&lt;/h2&gt;
&lt;p&gt;And finally we need to read the output, again, we can use our initial dictionary to map the DB columns back to an entity.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; EntityCollection RetrieveMultiple(QueryExpression query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; entityCollection = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; EntityCollection();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SqlConnection connection = GetConnection();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (SqlCommand command = connection.CreateCommand())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        command.CommandText = sqlQuery.ToString();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        connection.Open();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (SqlDataReader reader = command.ExecuteReader())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (reader.Read())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Entity entity = ReadEntityFromReader(reader, query.EntityName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    entityCollection.Entities.Add(entity);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            connection.Close();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; entityCollection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; Entity ReadEntityFromReader(SqlDataReader reader, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; entityName)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; id = reader.GetGuid(reader.GetOrdinal(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Entity entity = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Entity(entityName, id);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; mapping &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; FieldMappings)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; columnName = mapping.Value.IsComputed ? mapping.Value.ComputedColumnAlias : mapping.Value.ColumnName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!reader.IsDBNull(reader.GetOrdinal(columnName)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mapping.Value.ColumnType == &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(Guid))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                entity[mapping.Key] = reader.GetGuid(reader.GetOrdinal(columnName));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mapping.Value.ColumnType == &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt; = reader.GetString(reader.GetOrdinal(columnName));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(&lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    entity[mapping.Key] = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; entity;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Hopefully this saves you some painful experimentation.
It is also clear now why MS skipped this, as you will see in the full reference code below, implementing all of this already is more than 200 lines of code, which would have blown up their sample. And as discussed, we are still missing condition operators for Date attributes.&lt;/p&gt;
&lt;p&gt;I am totally aware that your requirements for the Data Provider will be different and especially your DB might be different but if you feed this article into something like GitHub Copilot or Claude Code with some existing code for your DB, it certainly will do a decent job of translating the code shown here to your technology/table names/column names/&amp;hellip;&lt;/p&gt;
&lt;p&gt;Consolidated reference code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Xrm.Sdk;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Xrm.Sdk.Query;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; System;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; System.Data.SqlClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; System.Text;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; System.Collections.Generic;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ... Code as found in the MS sample, adapted to my table dbo.Persons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, FieldMapping&amp;gt; FieldMappings = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, FieldMapping&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_personid&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(Guid), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;, IsIdentity = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_firstname&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Firstname&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_lastname&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Lastname&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_email&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Email&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_phone&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Phone&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FieldMapping { ColumnName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(ISNULL(Firstname, &amp;#39;&amp;#39;) + &amp;#39; &amp;#39; + ISNULL(Lastname, &amp;#39;&amp;#39;))&amp;#34;&lt;/span&gt;, ColumnType = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;), IsComputed = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, ComputedColumnAlias = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt; } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FieldMapping&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; ColumnName { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Type ColumnType { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; IsComputed { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; IsIdentity { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; ComputedColumnAlias { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; EntityCollection RetrieveMultiple(QueryExpression query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; entityCollection = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; EntityCollection();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SqlConnection connection = GetConnection();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (SqlCommand command = connection.CreateCommand())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; selectClause = BuildSelectClause(query.ColumnSet);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; sqlQuery = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; StringBuilder(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SELECT &amp;#34;&lt;/span&gt; + selectClause + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; FROM dbo.Persons&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (query.Criteria != &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; (query.Criteria.Conditions.Count &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; || query.Criteria.Filters.Count &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sqlQuery.Append(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; WHERE &amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; whereClause = BuildWhereClause(query.Criteria, command);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sqlQuery.Append(whereClause);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (query.Orders != &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; query.Orders.Count &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sqlQuery.Append(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; ORDER BY &amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; orderClauses = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; order &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; query.Orders)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; columnName = GetColumnName(order.AttributeName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; direction = order.OrderType == OrderType.Descending ? &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DESC&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ASC&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                orderClauses.Add(columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; + direction);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sqlQuery.Append(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, orderClauses));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (query.TopCount != &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; query.TopCount &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sqlQuery.Insert(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; TOP &amp;#34;&lt;/span&gt; + query.TopCount);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        command.CommandText = sqlQuery.ToString();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        connection.Open();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (SqlDataReader reader = command.ExecuteReader())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (reader.Read())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Entity entity = ReadEntityFromReader(reader, query.EntityName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    entityCollection.Entities.Add(entity);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            connection.Close();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; entityCollection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; BuildWhereClause(FilterExpression filter, SqlCommand command)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; clauses = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; condition &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; filter.Conditions)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; clause = BuildConditionClause(condition, command);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(clause))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            clauses.Add(clause);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; childFilter &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; filter.Filters)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; childClause = BuildWhereClause(childFilter, command);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(childClause))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            clauses.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&amp;#34;&lt;/span&gt; + childClause + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (clauses.Count == &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; logicalOperator = filter.FilterOperator == LogicalOperator.And ? &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; AND &amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; OR &amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(logicalOperator, clauses);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; BuildConditionClause(ConditionExpression condition, SqlCommand command)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; columnName = GetColumnName(condition.AttributeName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; paramName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt; + condition.AttributeName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_&amp;#34;&lt;/span&gt; + Guid.NewGuid().ToString(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;N&amp;#34;&lt;/span&gt;).Substring(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (condition.Operator)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.Equal:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; = &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.NotEqual:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;lt;&amp;gt; &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.Like:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; LIKE &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.Null:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; IS NULL&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.NotNull:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; IS NOT NULL&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.GreaterThan:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;gt; &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.GreaterEqual:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;gt;= &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.LessThan:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;lt; &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.LessEqual:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;lt;= &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.In:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (condition.Values.Count == &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 = 0&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; inParams = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i = &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &amp;lt; condition.Values.Count; i++)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; inParamName = paramName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_&amp;#34;&lt;/span&gt; + i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                command.Parameters.AddWithValue(inParamName, condition.Values[i] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                inParams.Add(inParamName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; IN (&amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, inParams) + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; ConditionOperator.NotIn:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (condition.Values.Count == &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 = 1&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; notInParams = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i = &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &amp;lt; condition.Values.Count; i++)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; notInParamName = paramName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_&amp;#34;&lt;/span&gt; + i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                command.Parameters.AddWithValue(notInParamName, condition.Values[i] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                notInParams.Add(notInParamName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; NOT IN (&amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, notInParams) + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command.Parameters.AddWithValue(paramName, condition.Values[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] ?? DBNull.Value);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; columnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; = &amp;#34;&lt;/span&gt; + paramName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; Entity ReadEntityFromReader(SqlDataReader reader, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; entityName)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; id = reader.GetGuid(reader.GetOrdinal(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Entity entity = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Entity(entityName, id);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; mapping &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; FieldMappings)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; columnName = mapping.Value.IsComputed ? mapping.Value.ComputedColumnAlias : mapping.Value.ColumnName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!reader.IsDBNull(reader.GetOrdinal(columnName)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mapping.Value.ColumnType == &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(Guid))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                entity[mapping.Key] = reader.GetGuid(reader.GetOrdinal(columnName));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mapping.Value.ColumnType == &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt; = reader.GetString(reader.GetOrdinal(columnName));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(&lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    entity[mapping.Key] = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; entity;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; BuildSelectClause(ColumnSet columnSet)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; selectColumns = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; columnsToInclude = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (columnSet.AllColumns)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        columnsToInclude.AddRange(FieldMappings.Keys);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        columnsToInclude.AddRange(columnSet.Columns.ToArray());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; columnToInclude &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; columnsToInclude)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (FieldMappings.TryGetValue(columnToInclude, &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; mapping))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mapping.IsComputed)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                selectColumns.Add(mapping.ColumnName + &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; AS &amp;#34;&lt;/span&gt; + mapping.ComputedColumnAlias);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                selectColumns.Add(mapping.ColumnName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!selectColumns.Contains(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        selectColumns.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.Join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;, selectColumns);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; GetColumnName(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; attributeName)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (FieldMappings.TryGetValue(attributeName.ToLower(), &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; FieldMapping mapping))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; mapping.ColumnName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; attributeName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
        </item>
        <item>
        <title>Copilot for Sales: Working with forms</title>
        <link>https://www.marius-wodtke.de/post/copilot/outlook/forms/</link>
        <pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/copilot/outlook/forms/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/cover.jpg" alt="Featured image of post Copilot for Sales: Working with forms" /&gt;&lt;p&gt;In the Copilot for Sales series we already covered &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/copilot/outlook/admin/&#34; &gt;the settings&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/copilot/outlook/sidepane/&#34; &gt;OOTB experience for the Email sidepane&lt;/a&gt;. But we did not cover yet how changing the form settings will affect the user experience. In this article we will enable the Lead table for Outlook and play around with the different switches to determine the effects.&lt;/p&gt;
&lt;h2 id=&#34;enabling-the-table&#34;&gt;Enabling the table&lt;/h2&gt;
&lt;p&gt;When we select the &lt;em&gt;Add a new record type&lt;/em&gt; button, the Lead will be found because it does have a relationshipt to Account, Contact or Opportuntity - in fact to all of them. This is what we get presented when selecting the relationships which will be relevant for the displayed cards in the sidepane.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The next step is actually in &lt;a class=&#34;link&#34; href=&#34;https://make.powerapps.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;make.powerapps.com&lt;/a&gt; where I create a new view for the Lead with all the fields I want to have on the form. I&amp;rsquo;ve named this one &amp;ldquo;Outlook&amp;rdquo; to associate it accordingly but this is preference. Note that this view will also be shown in your Dynamics Model Driven App if you chose to display all views, as soon as you start using Copilot for Sales I think it&amp;rsquo;s time to think about what views you want in your app and select accordingly. In terms of fields you want to divert from other views, on Lead views usually show the combined &amp;ldquo;Name&amp;rdquo; field which is not editable, so it won&amp;rsquo;t be in Outlook either&amp;hellip; And I applied no filter and instead show the status reason field, allowing users to find also qualified or disqualified leads.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The fields in the settings match the ones from the view in Make.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget to hit &lt;em&gt;Publish&lt;/em&gt; now!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add7.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Because both options for creating a new lead were enabled…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Add6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;…both are offered.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If you enabled both create options, the user has to go trough an extra step before they arrive at the form, you might want to enable only one method.&lt;/p&gt;
&lt;h2 id=&#34;require-field&#34;&gt;Require field&lt;/h2&gt;
&lt;p&gt;In this case it makes sense to require the firstname and emailaddress as well, since we can assume that the user can extract this from the current email. Remember that you cannot turn of required fields that are already required by Dynamics/your customization, but we can require additional ones.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Require1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;First Name can be manipulated, but Last Name is locked as it is required by Dynamics.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Require2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;This works as expected. There is apparently no smart prefilling of the Email Address. A few more observations here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Require3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Lookups show the Key Fields (Contact has Job Title and Account Name, “Test” is an Account Name here) and unfolding reveals the emailaddress and a link to Dynamics.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Require4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Status Reason does work, but it allows illegal combinations, which could lead to user errors trying to create a Lead as Qualified or similar. I still want this in the form here, because I think it makes a good Key Field for Leads.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;A nice touch that I didn&amp;rsquo;t notice before: The empty Parent Account for lead field is hidden in the card, which makes it more readable if you happen to have many optional fields.&lt;br&gt;
If I edit the lead again and remove the First Name, we can see it still being required on the edit form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Require5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After saving, the card is displayed, empty fields are hidden.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Require6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;For fields having a value, a label is shown instead of the hint text, this label has the usual required stars (*).&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;disable-editing-for-fields&#34;&gt;Disable editing for fields&lt;/h2&gt;
&lt;p&gt;I already moaned about the status reason field which I want for the Key Fields but editing can lead to problems. Let&amp;rsquo;s disable it!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Disable1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Disable2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Status Reason in grayed out on the edit form.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Disable3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Status Reason is gone from the create form.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a great success!&lt;/p&gt;
&lt;h2 id=&#34;disable-editing-at-all&#34;&gt;Disable editing at all&lt;/h2&gt;
&lt;p&gt;Also pretty straight forward. If you disable editing, the edit button vanishes from the Lead card.
However, important note: Although you cannot edit the required fields anymore after you disable editing, they still apply, so First Name and Email are still required on the creation now. This is probably an oversight/bug, but you can work around it by temporarily enabling editing to manipulate those required fields and then disable it again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Disable4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Disable5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;disable-creation&#34;&gt;Disable creation&lt;/h2&gt;
&lt;p&gt;We have kind of seen this one already, since it is OOTB disabled for Account and Opportunity; and for Contact, creating in Dynamics is disabled, giving us a simple create button instead of a dropdown. So one more thing to test: What happens if we only allow creation in Dynamics?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Create1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Create2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And thats a really nice outcome! The button now is essentially a link to the Dynamics with the parameters &lt;code&gt;&amp;amp;forceUCI=1&amp;amp;pagetype=entityrecord&amp;amp;etn=lead&lt;/code&gt;. If you have problems with the Outlook forms, like my concerns with the Status Reason field (which we solved differently here!), you can still supply the users with a quick link in Outlook so they don&amp;rsquo;t have to navigate through the MDA, this might be a good time saver for them.&lt;/p&gt;
&lt;h2 id=&#34;key-fields&#34;&gt;Key Fields&lt;/h2&gt;
&lt;p&gt;Until now I have searched this lead multiple times, but since I haven&amp;rsquo;t configured any Key Fields (there is a maximum of 2) only the primary field Name was shown. I have selected Status Reason and Topic here and then first tested with the Lead we&amp;rsquo;ve previously used. Since I then noticed both fields are in the same row I changed the values to rather long ones to see what happens. The learning is simple: put the shorter field first, otherwise the user may only see the first one. Also notice that I don&amp;rsquo;t have an option to unfold, but there is a tooltip if the record is hovered.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Key1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Key2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Key Fields are displayed in one row.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Key3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;If Key Fields are too long, they will be cut.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Key4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The first Key Field can even push out the second one.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/forms/Key5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Hovering the record will reveal the full Key Field values as tooltip.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;To add a new table to the forms in Copilot for Sales, the table needs to have a relationship to Account, Contact or Opportunity. If this relationship is indirect, you first need to add the intermediary table (you may disable create/edit on this one). You determine what fields are on the form with the selected view, where the filters matter for the search and the fields matter for the form.&lt;/p&gt;
&lt;p&gt;Fields will be required on both create/edit forms. If editing is disabled for a field, it will vanish from the create form. This may be interesting if you don&amp;rsquo;t want to allow a field to be set but still want to display it in the search. This can be achieved with the Key Fields, but using a field as a Key Fields also means it needs to be present on the form.&lt;/p&gt;
&lt;p&gt;Finally you want to decide wether creation and editing is allowed with those very simple forms available in Outlook. If you have reasons to disable creation in Outlook, you can still display a quick link to jump to the Dynamics creation form with all it&amp;rsquo;s Business Rules, JS, PCFs, &amp;hellip; But you should decide between the methods as leaving both enabled requires an extra click from the user.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[Video] Calling Bound Custom APIs with Power Pages Server Logic</title>
        <link>https://www.marius-wodtke.de/post/power-pages/serverlogic/bound-api/</link>
        <pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/serverlogic/bound-api/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/serverlogic/cover.jpg" alt="Featured image of post [Video] Calling Bound Custom APIs with Power Pages Server Logic" /&gt;&lt;p&gt;Microsoft has a new preview feature called Server Logic and it already includes a function to call bound custom APIs.&lt;/p&gt;
&lt;p&gt;With Flows and my solution &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt; this is our third option for custom APIs.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5ow4btEGiFI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5ow4btEGiFI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/serverlogic/bound-api/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Copilot for Sales: Email sidepane actions</title>
        <link>https://www.marius-wodtke.de/post/copilot/outlook/sidepane/</link>
        <pubDate>Sun, 15 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/copilot/outlook/sidepane/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/cover.jpg" alt="Featured image of post Copilot for Sales: Email sidepane actions" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/copilot/outlook/admin/&#34; &gt;the first article&lt;/a&gt; of this series we&amp;rsquo;ve deployed the &amp;ldquo;Sales&amp;rdquo; app to our Outlook users and explored the settings a bit. However, to understand the settings for the features better we certainly should understand what the app looks like to our users because that allows us to understand the consequences of our administrative actions.&lt;/p&gt;
&lt;h2 id=&#34;opening-the-app&#34;&gt;Opening the app&lt;/h2&gt;
&lt;p&gt;Once the app was deployed, it should be visible to the users after a few minutes; or up to 48 hours per the documentation, but usually they set absurdly high numbers here to avoid support cases that resolve themselves an hour later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Access1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Access2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then, the app is accessible in both toolbars of the email itself and the general one at the top. You may also use the apps button to find it. The apps button in the left toolbar also offers the Sales app, however, this button does not open the sidebar with actions for the email but the fullscreen app that offers learning paths and the environment switcher.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/UserView.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Home.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;saving-the-email&#34;&gt;Saving the email&lt;/h2&gt;
&lt;p&gt;For this one simply click the save button and select a regarding object. This can be the contact, but with the search box I can also find an opportunity that I&amp;rsquo;ve created for this matter.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;There is always the option to simply store the record to the contact.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;But also other regardings are possible.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save8.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;If the email contained attachments, they will be offered to be saved as well.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After selecting the record from the searchbox it appears in the radio list.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After saving is done new sections will open with more insights, as this is the first email for this opportunity there is not much to be gained here at the moment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The edit action will bring back the screen to select a regarding object.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Save7.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When the email was saved (automatically) without a regarding, instead of “Save email to Dynamics 365” the section will only offer to connect the record.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;creating-a-new-contact&#34;&gt;Creating a new Contact&lt;/h2&gt;
&lt;p&gt;With the plus in the top right corner the user can also create records. By the OOTB form settings, this is currently only enabled for Contacts. If you remember back to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/copilot/outlook/admin/&#34; &gt;the initial article about the settings&lt;/a&gt; you will recognize the required fields here. Also notice that all fields are editable in this view. The email field will not be editable when later editing the record, however the forms don&amp;rsquo;t make this well visible, a lock symbol or gray background is not present!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/New1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Which records are creatable in Outlook is set in the admin settings.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/New2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Required fields are set in the admin settings.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/New3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After saving the record is displayed as a card.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;searching-an-existing-record&#34;&gt;Searching an existing Record&lt;/h2&gt;
&lt;p&gt;Records with configured forms can also be searched, viewed and edited. This will direct you to a screen that looks really similar to the create form above, but some fields may not be editable depending on your settings.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Search1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Search2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connected-records&#34;&gt;Connected Records&lt;/h2&gt;
&lt;p&gt;The connected records at the bottom behave a little different! They will also show the cards and in the three dots you will find the edit, that will behave the same, but for Opportunity we get an additional AI summary that looks a lot like the ones inside Dynamics. And for the Contact we get up to 10 other emails in our mailbox (they don&amp;rsquo;t need to be saved to Dynamics) plus a notes field that is apparently not saved to Dynamics and there lends itself to private notes. For everything else you might want to include the description field to the form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Connected1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The contact includes other emails and private notes.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/Connected2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The opportunity includes an AI summary.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;ai-insights&#34;&gt;AI Insights&lt;/h2&gt;
&lt;p&gt;Sorry, but this one needs a revisit once I have my Teams back up&amp;hellip; Reassuring that these are enabled in the Access Settings and saving 5 more emails to the Opportunity yields no summary in these.&lt;/p&gt;
&lt;p&gt;For the email below it would have been nice to be informed about the the 5 licenses running out end of november, that is mentioned in the initial email because this specific email has a totally different focus.&lt;br&gt;
But at least we can see one change here: The summary got the fact that this email is linked to an opportunity now and gives me a quick glance about it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/sidepane/AIInsights1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Without any additional configuration the Copilot for Sales app allows the user to save emails to Dynamics and connect them to other objects like Opportunities, those connections can later be changed. If creation is configured even new records can be created, OOTB this is the case for Contacts. If emails included attachments those will also be saved, this is controllable by the user.&lt;br&gt;
A lot of these behaviors like available tables and fields are changeable by administrators in the settings and we will explore these changes further in this series.&lt;/p&gt;
&lt;p&gt;Although not all AI features worked for me in this exploration, Copilot for Sales summarizes emails helping the seller to quicker understand longer email chains.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Microsoft Power Pages Client APIs (preview)</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-mspreview/</link>
        <pubDate>Sun, 01 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-mspreview/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-MSPreview/cover.jpg" alt="Featured image of post [VIDEO] Microsoft Power Pages Client APIs (preview)" /&gt;&lt;p&gt;Microsoft published its own idea of a client API for Power Pages, let&amp;rsquo;s take a look!&lt;/p&gt;
&lt;p&gt;Microsoft documentation: &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-pages/configure/client-api&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://learn.microsoft.com/en-us/power-pages/configure/client-api&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5iQvb3e003A&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5iQvb3e003A&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-MSPreview/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Copilot for Sales: Admin Settings</title>
        <link>https://www.marius-wodtke.de/post/copilot/outlook/admin/</link>
        <pubDate>Sun, 15 Feb 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/copilot/outlook/admin/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/cover.jpg" alt="Featured image of post Copilot for Sales: Admin Settings" /&gt;&lt;p&gt;I&amp;rsquo;m not a big fan of AI for everything, but that&amp;rsquo;s a story of it&amp;rsquo;s own. Even the earliest versions of GPT already did a great job summarizing texts and thus having such a feature in Outlook to get the hang of long conversations that you didn&amp;rsquo;t follow until someone posted a question to you is a great thing.&lt;/p&gt;
&lt;p&gt;MS thought so too and thus we now have the Copilot for Sales now. The obviously diverted all development efforts from the established App for Outlook and has the disadvantage that depending on your existing licensing it will cost extra.&lt;br&gt;
But of course there is also additional value that might outweigh the cost quickly and especially if it is included in your existing licenses, why shouldn&amp;rsquo;t we prefer this over the App for Outlook?&lt;/p&gt;
&lt;p&gt;To skill myself up in this new way of offering Dynamics connectivity to Sellers in Outlook I will explore this tool in this series and as always I will document my experience in here, hoping that you may find some information that the official docs were missing.&lt;/p&gt;
&lt;h2 id=&#34;the-first-pitfall&#34;&gt;The first pitfall&lt;/h2&gt;
&lt;p&gt;And here goes the first one: &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/microsoft-sales-copilot/install-sales-as-an-integrated-app&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;The docs say&lt;/a&gt; simply go to M365 admin center -&amp;gt; Settings -&amp;gt; Integrated apps -&amp;gt; Get apps. Now search for &amp;ldquo;Sales&amp;rdquo; and chose get it now. And this is correct! However, the big screenshot right below still has the search term &amp;ldquo;copilot for sales&amp;rdquo; and a matching &amp;ldquo;Copilot for Sales&amp;rdquo; card shown&amp;hellip; Yeah, they&amp;rsquo;ve renamed it to Sales, so search for &amp;ldquo;Sales&amp;rdquo;! &lt;a class=&#34;link&#34; href=&#34;https://marketplace.microsoft.com/en-hk/product/office/WA200004460?tab=Overview&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Here is also a direct link to the App&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/M365admin.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Integrated Apps is found after selecting “Show All”&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Search.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The App was renamed from “Copilot for Sales” to “Sales”&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Sadly, I did not screenshot the installation wizard, but the only setting I could make was for which users it is available to. This is also available in the app details afterwards should you want to change it. Note here that for larger organizations you would choose to restrict the app to a group to only deploy it to users with the respective license.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Details.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Users.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Also note the &lt;strong&gt;Teams&lt;/strong&gt; integrated app needs extra deployment from the Teams admin center&lt;/p&gt;
&lt;h2 id=&#34;accessing-the-admin-settings&#34;&gt;Accessing the Admin Settings&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s first check the app is available, this may take a couple minutes; or up to 48 hours per the documentation, but usually they set absurdly high numbers here to avoid support cases that resolve themselves an hour later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/UserView.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;As we can see OOTB the button appears in an email, it connected me to an environment automatically on first open (likely my last used environment on this user) and offers to work with Contacts or saving the Email to Dynamics. So far so similar to the App for Outlook, but &amp;ldquo;Key sales info&amp;rdquo; provides a summary of the email as the copilot part. In this sample not really helpful, by the time I&amp;rsquo;ve read the summary I&amp;rsquo;ve also read the Email, plus, I need to double check because &amp;ldquo;ai-generated content may be incorrect&amp;rdquo;, but of course we all know those emails/email-chains that are much worse and the summary is a blessing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Apps.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The apps menu is found on the left.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Home.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Most importantly for regular users, the full-screen app contains an environment switcher should the automatic association be incorrect and the user dismissed the initial info box. If you have the System Administrator Role in the environment you will also see the settings tab.&lt;/p&gt;
&lt;p&gt;From here on out this article will be a collection of commented screenshots. I hope this helps you as much as it helps when I have to respond to client requests without having an environment and licensed Outlook at hand.&lt;/p&gt;
&lt;h2 id=&#34;tenant-settings&#34;&gt;Tenant Settings&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Tenant.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Tenant1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;If you have restrictions on the use of AI for compliance, legal, union or whatever reasons, you can turn off the AI parts of the app, you will miss the summaries etc. but for example saving emails to Dynamics will still be available. If you use the switch at the top, the bottom part will vanish as AI is forbidden for everyone.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Tenant2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;This seems to be Teams related, we’ll have to check the effects of this in a future article once I got my Teams license issues sorted.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;environment-settings&#34;&gt;Environment Settings&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;You can disable the AI features on environment level as well, but here you only disable for everyone not with include/exclude lists.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Access Setting has options for Meeting Insights, Access to Sales Chat and Sales Agent.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;These options again may be used with include/exclude lists.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;If a feature in this section is turned off, the options to restrict access will vanish, it is forbidden for everyone.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;These settings affect what can be manually synchronized. Remember that there are also settings for the automatic Server Side Synchronization in Dynamics! Also the field mapping will be interesting because the AI meeting notes being synchronized to the description field by default may have effects here on this existing synchronization!&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment7.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Classifications are offered to the user when saving an email/appointment, this can make the process more cumbersome, but might also be really helpful when your process requires additional information to be logged.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment8.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment10.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The list for adding a record type in Forms only lists tables that have a lookup to the already configured Contact, Account, Opportunity, that’s why only 98 tables are shown. Lead is available!&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment9.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment11.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Key fields at the bottom do not form a database key, but are important and should be shown alongside the primary field in cards.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment13.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;You may define more required fields in Outlook than in Dynamics.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment14.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;However, if a field is required in Dynamics you can not revert that in Outlook nor lock the field for editing. If you need this, use Business Rules to require the field in Dynamics.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment15.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Email is never editable!&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment16.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;You may also add fields to the form, based on the amount, all field types should be available.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment17.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;View was not editable for Contact! On Opportunity this opens a two step wizard where you first select the relationship to Contact for the synchronization and then which view of Opportunity should be used to select an existing record.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment18.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment19.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When edit is disabled, the settings for Allow Editing are not editable anymore.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment20.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Strangely this is also the case for required fields although this should also apply to the Create form.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment21.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Environment22.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Nice move here to allow for fine tuned system prompts with the OOTB sample for Account Summaries! I’m wondering if this will become available in more places.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;features&#34;&gt;Features&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Features1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Well, there is another point for a separate article…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Features2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;At least in the european space a highly disputable setting, but worst case always is that you forgot to start the recording/transcription and now have no summary. Maybe the frog boils slowly here and we will accept being recorded in every Daily/Weekly/…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Features6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Meeting preparations don’t work well for me as people are not providing much agenda, but if they do in your organization, this may be a great thing.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Features3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Daily Digest delivery is configurable in 30 minute intervals. To be tested if this adapts to the users timezone. Also it would be beneficial if the user has the possibility to overwrite the admins choice here, imagine different cultures where some countries start working at 7 and others at 9…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Features4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The chat feature was already present in other settings to enable it. Here we can fine tune its capabilities, I see adding important custom entities as the primary use case.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/copilot/outlook/admin/Features5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Once you find the app, deployment is straight forward. Think about whom you want to give access and if all of these users should receive all AI features. You can limit access to many features with fine granularity in the Settings of the Copilot for Sales app. And by the way, I will keep calling it Copilot for Sales instead of &amp;ldquo;Sales&amp;rdquo;, because I think this is confusing in a global context. It might be fine within the Outlook AppSource, but in the Power Platform space or even googling stuff it&amp;rsquo;s way to generic.&lt;/p&gt;
&lt;p&gt;This series will continue. For once we will take a general look at the users side of things, but we&amp;rsquo;ve also opened up room to investigate settings like required fields on create forms, Teams capabilities, daily digest, default views for contacts, enabling forms for custom tables, classifications, AI summary synchronization and the lead research agent. See you soon!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Enable custom tables</title>
        <link>https://www.marius-wodtke.de/post/teams/custom-table/</link>
        <pubDate>Sun, 15 Feb 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/custom-table/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/cover.jpg" alt="Featured image of post Teams Integration: Enable custom tables" /&gt;&lt;p&gt;In previous articles we already covered the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-basic/&#34; &gt;basic&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-enhanced/&#34; &gt;enhanced teams collaboration experience&lt;/a&gt;. For this article I assume that you have followed at least the setup for the basic collaboration, as otherwise you won&amp;rsquo;t have a Teams integration to work from!&lt;br&gt;
However, enabling this feature only enables the experience on a set of OOTB and Microsoft first party tables. At the time of writing &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/sales/teams-integration/teams-collaboration#record-types-that-support-microsoft-teams-integration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the documentation&lt;/a&gt; lists 35 tables.&lt;/p&gt;
&lt;p&gt;However, the docs also refers to an &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/integration-dev/teams-integration-using-code?view=op-9-1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;msdyn_SetTeamsDocumentStatus&lt;/a&gt; action that we may use to enable other tables, including custom tables, for Teams.&lt;/p&gt;
&lt;h2 id=&#34;using-postman&#34;&gt;Using Postman&lt;/h2&gt;
&lt;p&gt;It is unfortunate here that MS does not provide a UI to manipulate the status of this integration for individual tables. As the provided action is called with a body and the POST method, we cannot use a simple browser to call it (at least not without some extensions or Console JS).&lt;/p&gt;
&lt;p&gt;Spoiler: In the next section I will introduce you to an XrmToolbox tool that makes it effortless, but here I also want to introduce you to the way you can do such things with Postman if you come across a similar functionality that no one has written a tool for yet.&lt;/p&gt;
&lt;p&gt;Fortunately, I don&amp;rsquo;t have to start a full tutorial for setting up Postman for talking to Dynamics here, fellow MVP &lt;a class=&#34;link&#34; href=&#34;https://softchief.com/2021/08/18/step-by-step-connect-dynamics-365-online-from-postman-using-webapi-auth-2-0-authentication/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Sanjaya (Softchief) has already covered this well&lt;/a&gt;!&lt;br&gt;
From here we simply want to append the the name of the unbound action &lt;code&gt;msdyn_SetTeamsDocumentStatus&lt;/code&gt; and push the documented body, e.g. the following one to activate Teams Collaboration on the entity with the logical name &lt;code&gt;mwo_test&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{
    &amp;#34;Enable&amp;#34;: true,
    &amp;#34;LogicalEntityNames&amp;#34;: &amp;#34;[\&amp;#34;mwo_test\&amp;#34;]&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/postman.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Check Sanjayas tutorial linked above to see how &lt;code&gt;webapiurl&lt;/code&gt; is constructed, it is the base url of your environments OData API (e.g. &lt;a class=&#34;link&#34; href=&#34;https://XXX.crmY.dynamics.com/api/data/v9.2/%29&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://XXX.crmY.dynamics.com/api/data/v9.2/)&lt;/a&gt;.
Also notice that the double quotes inside the value of &lt;code&gt;LogicalEntityNames&lt;/code&gt; need to be escaped in order to ensure this parameter forms a valid list of strings.&lt;/p&gt;
&lt;p&gt;An one last note here: For me this action always returns a success as long as the table exists, no matter if the table is valid for activating teams. A prime example where this is never going to work is the &lt;code&gt;activityparty&lt;/code&gt; which is completely uncustomizable. You can check if the operation was successful in the entity metadata with the attribute &lt;code&gt;IsMSTeamsIntegrationEnabled&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;using-the-teams-integration-enabler&#34;&gt;Using the Teams Integration Enabler&lt;/h2&gt;
&lt;p&gt;As promised, here comes an easy tool to achieve this. As this is also my first tool for the XrmToolbox, bare with me, Tanguy already had to, because I didn&amp;rsquo;t have the workflows down yet&amp;hellip;&lt;/p&gt;
&lt;p&gt;As we have a graphical tool, we&amp;rsquo;ll have a graphical tutorial here as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;We start by making sure the tool is installed in the Tool Library.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Next we find it in Tools.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The tool consists of a list of tables on the left, a message area on the right and an actionbar at the top.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;You may use the seach bar to find your table quickly. Select the table in question and use Enable to start the customization.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;This will take some seconds…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;And hopefully now you are greeted with a success message.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolbox7.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After refreshing the tables, the custom table has turned green, indicating that the integration is enabled.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Please note that the table metadata is sometimes slower than we are clicking refresh tables. Should your table still be reported &amp;ldquo;red&amp;rdquo;, try the Load Tables button two or three more times, likely it will turn &amp;ldquo;green&amp;rdquo; soon ;)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/toolboxError.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Should you receive an error like this, you probably have not followed the steps in &amp;ldquo;Enabling Teams&amp;rdquo; of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-basic/&#34; &gt;the Basic Collaboration article&lt;/a&gt;. More specifically, a solution &lt;code&gt;msdynce_OfficeProductivity&lt;/code&gt; is required which seems to be bundled with the Dynamics standard apps of Customer Service and Customer Experience (Sales &amp;amp; Marketing). On a Dev-Environment, which does not have Dynamics apps by design, this error can be triggered reliably.&lt;/p&gt;
&lt;h2 id=&#34;result--summary&#34;&gt;Result &amp;amp; Summary&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom-table/result.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;No matter if you chose to do this with the XrmToolbox tool or Postman, the final result should look something like this. The enabled table now has the &lt;em&gt;Collaborate&lt;/em&gt; button, which will open the dialog to connect the record with a teams channel. From here of course the experience will change depending on wether you did configure the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-basic/&#34; &gt;basic&lt;/a&gt; or &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-enhanced/&#34; &gt;enhanced teams collaboration experience&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At the time of writing I am not aware of limitations for the custom tables, you can activate the integration for any table that allows customizations, also for OOTB and other first party or ISV tables. You can also deactivate the integration on the 35 preenabled tables if you for example would wish to only activate collaboration on opportunities but not on accounts.&lt;/p&gt;
&lt;p&gt;The enabled status is stored in the table property &lt;code&gt;IsMSTeamsIntegrationEnabled&lt;/code&gt;. This status will also be transferred with a solution, should you have selected the option to &lt;em&gt;Include Table Metadata&lt;/em&gt; at the table in question.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.xrmtoolbox.com/plugins/plugininfo/?id=7e01b65d-b6da-f011-92b8-7ced8d44e4bd&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;You can find the tool as well as links to the source here&lt;/a&gt;.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Xrm.Navigation in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi/navigation/</link>
        <pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi/navigation/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi/navigation/cover.jpg" alt="Featured image of post [VIDEO] Xrm.Navigation in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will explore Xrm.Navigation with openWebResource and openUrl as well as diving a bit into how bootstrap may help us with the openConfirmDialog method.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/tDPIUvWy25Q&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/tDPIUvWy25Q&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi/navigation/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Save an Email as EML with Powerautomate</title>
        <link>https://www.marius-wodtke.de/post/other/powerautomate-save-eml/</link>
        <pubDate>Sun, 18 Jan 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/powerautomate-save-eml/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/cover.jpg" alt="Featured image of post Save an Email as EML with Powerautomate" /&gt;&lt;p&gt;Let me use this article to give you a head start if you ever come across a similar requirement, this one was formulated as &amp;ldquo;Whenever an Email is related to an Opportunity, store it in our document management solution&amp;rdquo;. I will adapt this here a bit since I don&amp;rsquo;t see a good reason to communicate with a DMS in this article since yours will probably be different; instead we will store the result into a file column of the email.&lt;/p&gt;
&lt;p&gt;This might seem to have the benefit of allowing an easy download via clicking the file in the form, however, there is a &lt;em&gt;Download&lt;/em&gt; button in the ribbon already, which can achieve this OOTB. This button uses an unbound action &lt;code&gt;_Downloademailasattachment&lt;/code&gt;, one of those actions where I was unable to find documentation, rendering this option probably unsupported for our custom backend use case.&lt;/p&gt;
&lt;h2 id=&#34;the-eml-format&#34;&gt;The EML format&lt;/h2&gt;
&lt;p&gt;The well supported options for the Outlook client are .msg and .eml formats, for this article we are using EML. To get sample file simply use &lt;em&gt;Save as&lt;/em&gt; (New Outlook)/&lt;em&gt;Download&lt;/em&gt; (Web) on a single email.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/download.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Left: outlook.com; Right: New Outlook client&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;These samples usually contain A LOT OF headers, so we will take the final result of this article as it is much simpler to understand&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Subject: Fw: Test
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;From: mariuswodtke@mariuswodtkemvp.onmicrosoft.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;To: mariuswodtke@mariuswodtkemvp.onmicrosoft.com;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: multipart/mixed; boundary=&amp;#34;mwo_MainBoundary&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--mwo_MainBoundary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: multipart/related; boundary=&amp;#34;mwo_SecondaryBoundary&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--mwo_SecondaryBoundary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: text/html; charset=&amp;#34;UTF-8&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Transfer-Encoding: 8bit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;... html ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/head&amp;gt;&amp;lt;body dir=&amp;#34;ltr&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;... html ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;img size=&amp;#34;82516&amp;#34; height=&amp;#34;237&amp;#34; width=&amp;#34;421&amp;#34; id=&amp;#34;x_image_0&amp;#34; data-outlook-trace=&amp;#34;F:2|T:2&amp;#34; src=&amp;#34;cid:f64c0b69-6aaf-4691-87dc-69b19df912cd&amp;#34; style=&amp;#34;width:421px; height:237px; max-width:1249px&amp;#34; data-attachment-id=&amp;#34;1d7ea49e-1b90-4529-9f70-19cd3899c5d4&amp;#34; &amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;... html ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--mwo_SecondaryBoundary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: image/jpeg; name=&amp;#34;Logo.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Description: Logo.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Disposition: inline; filename=&amp;#34;Logo.jpg&amp;#34;; size=&amp;#34;82277&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-ID: &amp;lt;f64c0b69-6aaf-4691-87dc-69b19df912cd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Transfer-Encoding: base64
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/9j/4AAQSkZJRgABAQEAYABgAAD/ ... a lot more base64 stuff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--mwo_SecondaryBoundary--
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--mwo_MainBoundary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: application/pdf; name=&amp;#34;MVP-Award-Certificate-25.pdf&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Description: MVP-Award-Certificate-25.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Disposition: attachment; filename=&amp;#34;MVP-Award-Certificate-25.pdf&amp;#34;; size=&amp;#34;580208&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Transfer-Encoding: base64
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;JVBERi0xLjcKJanNxNIKMiAwIG9 ... a lot more base64 stuff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--mwo_MainBoundary--
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Main observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have headers with {key}: {value} notation&lt;/li&gt;
&lt;li&gt;Content-Type specifies a boundary, this is usually a randomized string, I&amp;rsquo;ve used a descriptive one here, prefixed with my publisher prefix to avoid collision (mwo_MainBoundary). This is used as &lt;code&gt;--mwo_MainBoundary&lt;/code&gt; and &lt;code&gt;--mwo_MainBoundary--&lt;/code&gt; to start and end the &amp;ldquo;body&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Inside this body another boundary is defined (mwo_SecondaryBoundary) which is used to split the html from the inline attachments (&lt;code&gt;--mwo_SecondaryBoundary&lt;/code&gt;).
&amp;ldquo;Real&amp;rdquo; attachments are split by the &lt;code&gt;--mwo_MainBoundary&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Inline images are using an &lt;code&gt;src&lt;/code&gt; attribute which is matched to the a header &lt;code&gt;Content-ID&lt;/code&gt; in other parts of the multipart content. This header encloses the id in &amp;lt;&amp;gt; (&lt;code&gt;Content-ID: &amp;lt;f64c0b69-6aaf-4691-87dc-69b19df912cd&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note here, that I certainly don&amp;rsquo;t have all the information for headers in the sample, you might also be interested in &lt;code&gt;Date: Fri, 26 Dec 2025 03:19:03 +0000&lt;/code&gt;. Just download an email that has the information you desire to find the header you are looking for!&lt;/p&gt;
&lt;h2 id=&#34;the-flow&#34;&gt;The Flow&lt;/h2&gt;
&lt;p&gt;I think here pictures say more than words, the main elements are a variable &lt;em&gt;EML Content&lt;/em&gt; which is filled slowly with content. The attachments of the email are listed and then iterated, images are added as inline attachments, everything else is added as normal attachment. Finally, the closing boundaries are added and the &lt;em&gt;EML Content&lt;/em&gt; is stored as a file.
Some more notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inline attachments should be added first, because of the boundaries. This is why we are leveraging the 2 helper variables. If you want to append directly to the &lt;em&gt;EML Content&lt;/em&gt;, use 2 queries with &amp;ldquo;mimetype starts with image/&amp;rdquo; and &amp;ldquo;mimetype does not start with image/&amp;rdquo; and iterate each of them instead of using the condition.&lt;/li&gt;
&lt;li&gt;When creating test cases, be aware that dynamics will not generate an inline attachment for images, instead it will push it as base64 directly into the image tag. To test the secondary boundary, generate an email with something like the outlook client and synchronize it into Dynamics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Main Flow structure&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Filling the main body&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Get all attachments of email&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Images are inline, the rest not&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Inline images are split with the secondary boundary&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow6.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Other attachments use the main boundary, a different Content-Disposition and miss the Content-ID&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/Flow7.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The final boundaries use the – at the end as well&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The Result&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-save-eml/result.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The final result with an inline attachment (jpg) and a pdf attachment&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And here you see that Outlook is indeed able to visualize this email. Since I did not include the &lt;code&gt;Date&lt;/code&gt; header, we don&amp;rsquo;t see it in the rendered result, showing the flexibility of the EML format.&lt;/p&gt;
&lt;p&gt;My version of this flow has a total of 9 actions, so I conclude that exporting Dynamics Email to EML files for automated processing is doable with reasonable effort.&lt;br&gt;
Please keep in mind that this is not needed for user interaction use cases. The frontend of Outlook as well as Dynamics already offer a &lt;em&gt;Download&lt;/em&gt; option for usage in not automated scenarios. However, the flow shown may help you to transfer emails in an automated fashion.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: Fully Automated AI Approvals in Power Platform | Streamline Business Processes with AI</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-advent/23/</link>
        <pubDate>Tue, 23 Dec 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-advent/23/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: Fully Automated AI Approvals in Power Platform | Streamline Business Processes with AI" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/lL3jJLfhd-I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/lL3jJLfhd-I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-advent/23/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: Power CAT Business Approval Kit Tutorial | Complete Setup &amp; Workflow Guide</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-advent/21/</link>
        <pubDate>Sun, 21 Dec 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-advent/21/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: Power CAT Business Approval Kit Tutorial | Complete Setup &amp; Workflow Guide" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/aYE_nDeDDVM&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/aYE_nDeDDVM&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-advent/21/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: Streamline Approvals in Dataverse with Power Automate | Step-by-Step Tutorial</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-advent/19/</link>
        <pubDate>Fri, 19 Dec 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-advent/19/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: Streamline Approvals in Dataverse with Power Automate | Step-by-Step Tutorial" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/WKoJ2iXFJXs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/WKoJ2iXFJXs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-advent/19/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Fix Unresolved Recipients in Dynamics 365 with Power Automate | Step-by-Step Guide</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-11/marius/</link>
        <pubDate>Sun, 30 Nov 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-11/marius/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-collab-cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Fix Unresolved Recipients in Dynamics 365 with Power Automate | Step-by-Step Guide" /&gt;&lt;p&gt;The concept of the Power Collab group is easy: Everyone shares and everyone learns! With people from different parts of the Power Platform, we can all learn something new for aspects we may not be using every day. Join us in this recording and learn as well!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/BmYZJlR1W-k&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/BmYZJlR1W-k&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-11/marius/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] formcontext.ui in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-ui/</link>
        <pubDate>Sun, 23 Nov 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-ui/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-ui/cover.jpg" alt="Featured image of post [VIDEO] formcontext.ui in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will explore formcontext.ui with getFormType and setFormNotification.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/eCCmUiKJTA4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/eCCmUiKJTA4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-ui/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Power Automate Debugging Made Easy – Upcoming Feature and Current Workaround</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-11/marius2/</link>
        <pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-11/marius2/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-collab-cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Power Automate Debugging Made Easy – Upcoming Feature and Current Workaround" /&gt;&lt;p&gt;The concept of the Power Collab group is easy: Everyone shares and everyone learns! With people from different parts of the Power Platform, we can all learn something new for aspects we may not be using every day. Join us in this recording and learn as well!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/IYuTrAD8aYA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/IYuTrAD8aYA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-11/marius2/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Creating an Import View</title>
        <link>https://www.marius-wodtke.de/post/pattern/import-view/</link>
        <pubDate>Sun, 09 Nov 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/import-view/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/import-view/cover.jpg" alt="Featured image of post Patterns: Creating an Import View" /&gt;&lt;p&gt;An Import View? Why would I need that? Yes, of course it is not per say necessary, but as you will learn here it is very helpful for steering the user!&lt;/p&gt;
&lt;p&gt;This pattern is helpful when users are somewhat regularly are importing data into a table. For the screenshots I took the Account, but if you think this is unrealistic because you have a tight master data concept for accounts, think about this instead: &amp;ldquo;When my sales people come back from a trade show, they will import their new leads via Excel, because they won&amp;rsquo;t find the time to record them to the system immediately.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;the-situation&#34;&gt;The situation&lt;/h2&gt;
&lt;p&gt;In theory this is pretty easy, select a view of the table with the appropriate fields. If there are fields missing, add them to the view and optionally save as a personal view if you need this regularly.
Now use &amp;ldquo;Export to Excel (static worksheet)&amp;rdquo; to get a good template to enter your data.
Remove all existing data to have a clean field to enter data and prevent any unwanted changes to the existing data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/import-view/Excel1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/import-view/Excel2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Enter your data, save and use &amp;ldquo;Import from Excel&amp;rdquo;. Because our headers were generated by &amp;ldquo;Export to Excel&amp;rdquo;, the data map will already be very good, maybe some lookup matching will need refinement.&lt;/p&gt;
&lt;p&gt;The data is now importing and then you will notice a BIG problem: You have just overwritten your existing records.
That&amp;rsquo;s because the Excel contained 3 hidden columns on the left (notice in the screenshots above that the columns start with D) and one of the is the Guid column, which will beat any other key in matching.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/import-view/Excel3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are finding this article because you already made this mistake, there are possibilities. Of course you can roll back to a backup, but this will reset all other tables as well. You can also restore a backup to a new environment, then use &amp;ldquo;Export to Excel&amp;rdquo; there and import it to your regular environment. However, the imported Excel needs to loose the hidden columns for Modified On and Row Checksum, otherwise Dynamics will complain that this data won&amp;rsquo;t match the data in the environment. If it needs to be more selective, you can also export another Excel from the target environment and match up the rows, e.g. via the Guid.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;To not run into this problem, we either need to keep all existing records in the Excel and only append to the end OR we also delete the content in the hidden columns. You may keep an empty Excel as a template in an accessible location like a SharePoint library, however, the reality says these are forgotten&amp;hellip;&lt;/p&gt;
&lt;p&gt;And believe it or not, I have seen this mistake happen from people that are totally capable of handling Dynamics, Administrators, Power Users, Consultants. Every human being is prone to this error, because the Excel looks okay since the columns are hidden. And they are hidden for good reason as well because tampering with these columns can lead to serious problems with the import, including fully duplicating all data contained. And I just suggested you to tamper with these columns to fix our problem, both before it occurs as well as fixing it afterwards&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;Obviously, we need something better! And that&amp;rsquo;s where our Import View comes in. First of all, we can easily fix the need to add columns to have all necessary fields in the view, just talk to your users and gather a list! It does not matter here if there are to many columns here for being displayed well in a Model Drive App, it&amp;rsquo;s only important that the exported Excel is usable.&lt;br&gt;
But more importantly, we can fix our big problem by simply ensuring that the View is always empty!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/import-view/View.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;By pushing impossible conditions to the filters, we make sure that the view is empty and therefore the Excel is empty and can be safely filled with data by the users. Compared to a pre-exported template, this is easily accessible from Dynamics itself and also adding needed columns &amp;ldquo;correctly&amp;rdquo; is very easy for the user. Correctly here meaning that it will be recognized automatically by the data maps, especially important for non-text fields, like Optionsets.&lt;/p&gt;
&lt;p&gt;Now you only need to ensure the view is shown in your app and we are ready to tell our users about the 4 easy steps of select the Import View, &amp;ldquo;Export to Excel&amp;rdquo;, add data, &amp;ldquo;Import from Excel&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/import-view/Result.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Prevent a data disaster by supplying your user with an Import view! This is a view that contains all necessary columns that users will need to do a successful import and contains conflicting filters in such a way that it will always be empty. This ensures that users won&amp;rsquo;t accidentally overwrite existing data by missing the fact that there are 3 hidden columns in any exported Excel from Dynamics. This pattern only applies to tables where &amp;ldquo;Import from Excel&amp;rdquo; is used on a regular basis.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to use File Manager PCF control in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/file-manager-pcf/</link>
        <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/file-manager-pcf/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/file-manager-pcf/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to use File Manager PCF control in Power Pages?" /&gt;&lt;p&gt;The File Manager PCF for Power Pages is a tool to allow users to easily upload one or more files. In contrast to the OOTB control, this will upload the data to an Azure Storage, rather than burdening your Dataverse storage. Want to learn more?&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/JCWzkCALu8g&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/JCWzkCALu8g&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/file-manager-pcf/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Dataverse Functions Preview – The Future of Low-Code Logic | Live Demo and Explanation</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-11/marius3/</link>
        <pubDate>Tue, 04 Nov 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-11/marius3/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-collab-cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Dataverse Functions Preview – The Future of Low-Code Logic | Live Demo and Explanation" /&gt;&lt;p&gt;The concept of the Power Collab group is easy: Everyone shares and everyone learns! With people from different parts of the Power Platform, we can all learn something new for aspects we may not be using every day. Join us in this recording and learn as well!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/z3gFIV3UpWI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/z3gFIV3UpWI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-11/marius3/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] formcontext.data in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-data/</link>
        <pubDate>Sun, 26 Oct 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-data/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-data/cover.jpg" alt="Featured image of post [VIDEO] formcontext.data in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will explore formcontext.data with getIsDirty, refresh and save.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/QUAm2XA4MUg&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/QUAm2XA4MUg&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-data/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: No Secrets, No Stress: Use Managed Identity in Dynamics 365 Plugins | Full Tutorial</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-10/marius/</link>
        <pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-10/marius/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-collab-cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: No Secrets, No Stress: Use Managed Identity in Dynamics 365 Plugins | Full Tutorial" /&gt;&lt;p&gt;The concept of the Power Collab group is easy: Everyone shares and everyone learns! With people from different parts of the Power Platform, we can all learn something new for aspects we may not be using every day. Join us in this recording and learn as well!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/t7eGYXFfyTg&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/t7eGYXFfyTg&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-10/marius/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to use FetchXml with Liquid in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/fetchxml-liquid/</link>
        <pubDate>Sun, 19 Oct 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/fetchxml-liquid/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/fetchxml-liquid/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to use FetchXml with Liquid in Power Pages?" /&gt;&lt;p&gt;Did you know you could directly query Dataverse from Liquid? This enables you to display data in a unique ways the OOTB methods wouldn&amp;rsquo;t allow. Want to learn more?&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/z8XNtMgWyQ8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/z8XNtMgWyQ8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/fetchxml-liquid/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Timeline Custom Connectors 4: Configuration</title>
        <link>https://www.marius-wodtke.de/post/timeline/custom/configuration/</link>
        <pubDate>Sun, 12 Oct 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/timeline/custom/configuration/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/configuration/cover.jpg" alt="Featured image of post Timeline Custom Connectors 4: Configuration" /&gt;&lt;p&gt;Timeline Custom Connectors allow you to display any form of data in the Dynamics/Model Drive Apps timeline. This &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/custom-connectors-timeline-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;generally available feature&lt;/a&gt; is however hard to implement and lacks official documentation, apart from the single, transpiled(!) &lt;a class=&#34;link&#34; href=&#34;https://github.com/microsoft/PowerApps-Samples/tree/master/model-driven-apps/custom-connectors/SecondaryRecordSource/solution&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;sample&lt;/a&gt; with incomplete interfaces, there is almost nothing to support you. This is where this blog tries to improve the situation, also check out &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/timeline/custom/&#34; &gt;the rest of the series&lt;/a&gt; for inspiration!&lt;br&gt;
BTW, if anyone from MS is reading this and is bothered by the tone: Give us some proper docs and ping me, I will happily refine these articles and commend you for fixing the situation!&lt;/p&gt;
&lt;p&gt;In this sample we will explore the option of passing an additional configuration to our custom connector. This configuration is specified in a separate WebResource which contains a JSON. This is immediately helpful if you want to use a timeline component differently at different forms/tables because you can reference different files, e.g. &lt;code&gt;ContactConfiguration.js&lt;/code&gt; and &lt;code&gt;AccountConfiguration.js&lt;/code&gt;.&lt;br&gt;
Should you want to change values between environments to for example change a URL needed for retrieving data, this is less ideal. You can still make it work by leveraging the layering system by stacking another (un)managed layer over the original configuration file. But I would suggest using the Environment Variables for this and either hardcode their schema name or reference their names via the configuration file.&lt;/p&gt;
&lt;h2 id=&#34;the-repository&#34;&gt;The repository&lt;/h2&gt;
&lt;p&gt;In general this series consists of programming samples since I expect you to have very specific requirements that can not be solved with standard methods. If they can be bent to fit something like a subgrid, do it! And depending on the pickiness of your stakeholders regarding styling, it might even be worth implementing a PCF instead because you have more control over the content. However, there are certainly situations where the UX of the timeline is preferred.&lt;/p&gt;
&lt;p&gt;You can find the full samples in the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;TimelineCustomConnector&lt;/a&gt; repository. It has a branch for each sample, for this article, navigate to &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/tree/Configuration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Configuration&lt;/a&gt;. As you will find in the repository, the samples are built with TypeScript and are then transpiled to JavaScript as I would consider using this feature a &amp;ldquo;real high-code development&amp;rdquo;. It is not done with few lines of code and the ability to debug transpiled MS code should be in your box of tools, this is nothing you can achieve with a bit of vibe-coding like you can sometimes with smaller form script use cases. As GitHub Copilot evolves quickly I feel I must add this: As soon as it is able to attach to the browser console, debug the problems and redeploy autonomously I might change my opinion regarding the last sentence.&lt;/p&gt;
&lt;h2 id=&#34;the-configuration&#34;&gt;The configuration&lt;/h2&gt;
&lt;p&gt;For the sample I introduced a new interface called &lt;code&gt;IConfiguration&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IConfiguration&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;footerField&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;bodyField&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headerField&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;sortField&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;selectFields&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;filterField&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;idField&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This interface defines all the properties I expect to be present. I&amp;rsquo;ve used all string here but you may use other simple as well as complex types, so you could also define something like &lt;code&gt;myField: ISubConfiguration&lt;/code&gt; that is another Interface with more properties inside.&lt;/p&gt;
&lt;p&gt;Crucially, this interface &lt;code&gt;extends JSON&lt;/code&gt;, this allows an easy conversion in &lt;code&gt;MyRecordSource.ts&lt;/code&gt; where the input to the init method must be JSON to comply with the interface (at least in TypeScript).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyRecordSource&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecordSource&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IControlData&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordSourceParams&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;!:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IConfiguration&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;!:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecordData&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;!:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IControlData&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordSourceParams&lt;/span&gt;&amp;gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;config?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;JSON&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;void&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IConfiguration&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Data&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Be careful! This feature is not very fault tolerant, putting an invalid JSON into your configuration file will break the whole component, you will simply not hit any breakpoint from the &lt;code&gt;init&lt;/code&gt; method forward and will not receive any feedback in the console or similar either. If you can hit a breakpoint in the first line of &lt;code&gt;init&lt;/code&gt; without a configuration and can&amp;rsquo;t hit it with a configuration, be sure to check the contents with a JSON validator as the OOTB timeline will run &lt;code&gt;JSON.parse&lt;/code&gt; for you before passing the configuration.&lt;/p&gt;
&lt;p&gt;Oh and another thing you might stumble into: While you specify the code file (Resource path) for the timeline custom connector starting with the publisher prefix, the configuration file (Configuration path) needs a &lt;code&gt;/WebResources/&lt;/code&gt; because unlike the code file, this is not automatically prepended and you will find a 404 call for the configuration in the Network tab of your Developer tools otherwise. So configuration should look a little something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/configuration/paths.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Resource Path and Configuration Path are specified with different prefixes.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;TBH I think this might be a bug, but fixing it would be a breaking change, so keep an eye out to the Network tab of the Developer tools to validate the behavior.&lt;/p&gt;
&lt;h2 id=&#34;using-configuration&#34;&gt;Using configuration&lt;/h2&gt;
&lt;p&gt;Once you have the configuration file was loaded successfully and you were able to verify the JSON arriving correctly at your breakpoint in &lt;code&gt;init&lt;/code&gt;, we can start using it. Fortunately we have control from here, so no weird things should happen from now on. You should only be aware that although I can specify properties to be mandatory, I can&amp;rsquo;t enforce this on the configuration file as it is independent, so we can run into null ref exceptions should the configuration not fully specify all properties.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JSON&#34; data-lang=&#34;JSON&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;footerField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdon&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bodyField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lastname&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;headerField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;firstname&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sortField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdon&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;entityName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;selectFields&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contactid,createdon,firstname,lastname&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;filterField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_parentcustomerid_value&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;idField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contactid&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a sample configuration rendering the contacts of an account, remember you can take a look at the full code in the branch &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/tree/Configuration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Configuration&lt;/a&gt;. The configuration is passed to the Data class in the &lt;code&gt;init&lt;/code&gt; method and is there used build the Dataverse query.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Data&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IControlData&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordSourceParams&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IConfiguration&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IControlData&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordSourceParams&lt;/span&gt;&amp;gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IConfiguration&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getData&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;any&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;[]&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;page&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`$filter=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filterField&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; eq &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`$select=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selectFields&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;contacts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;webAPI&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;retrieveMultipleRecords&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;`?&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;select&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;contacts&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entities&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And of course we can now use the configuration everywhere we might need it, for example when &amp;ldquo;rendering&amp;rdquo; the timeline record in &lt;code&gt;getRecordUX&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;createBody&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;factory&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Label&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            { &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordSourceInfo&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_body`&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;`Body: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bodyField&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ) &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And in this case I again have to emphasize that this is a coding sample, the code presented here is very unsafe considering the person crafting the configuration has to match all those separate fields together, ensuring that the bodyField is also found in the selectFields and that the selectFields are actually present in the table specified in entityName. A production ready component would need much more error handling!&lt;/p&gt;
&lt;p&gt;But we have a result!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/configuration/contact.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Both contacts from the grid are now present in the timeline.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;task-problems&#34;&gt;Task problems&lt;/h2&gt;
&lt;p&gt;I also tried this with the Task table, the configuration looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JSON&#34; data-lang=&#34;JSON&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;footerField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;prioritycode@OData.Community.Display.V1.FormattedValue&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bodyField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;headerField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sortField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdon&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;entityName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;task&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;selectFields&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;activityid,createdon,description,subject,prioritycode,activitytypecode&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;filterField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_regardingobjectid_value&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;idField&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;activityid&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this lead to problems!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/configuration/taskFailing.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Although records were returned and getRecordUX was executed, an error is displayed.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/configuration/taskError.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Fortunately, the error is not swallowed and bubbles up to the console.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;With debugging into this error I found that a property &lt;code&gt;dataMap&lt;/code&gt; (object) was missing which should specify an activitytypecode, however setting it in my code did not help as it was rebuilt as an empty object by the timeline. Comparing with the contact sample revealed that completely wrong turns were taken as the contact had a different derivative of the class where the activitytypecode was hardcoded as &amp;ldquo;customtest&amp;rdquo;, which also sounds odd.&lt;br&gt;
From here on I discovered that my custom task record was pooled with the OOTB task record, which had the dataMap built correctly. I can only assume what happens here, something like the same entity name leading to this bug? Nevertheless, the fix was to simply disabling Activities, the original task not being rendered lead my record to the correct classes again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/configuration/taskWorking.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;With deactivated Activities, the record renders correctly.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;You will also notice that my footer did not render here, but my nerves were already tested enough at this point, I will postpone this problem to another article.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Timeline Custom Connectors is a tricky to implement feature due to missing documentation and bad error handling. But you can do a lot of cool stuff with it and with configuration it gets even cooler!&lt;/p&gt;
&lt;p&gt;You need to put a (valid) JSON into a WebResource and reference it in the timeline configuration alongside the code file. Note that you will need to prepend a &lt;code&gt;/WebResources/&lt;/code&gt; for the configuration file as it is not automatically done.&lt;/p&gt;
&lt;p&gt;The content will be passed to the &lt;code&gt;init&lt;/code&gt; method in an already parsed state and from there to change the behavior of your component. I advise to define an interface for the configuration as it will ease tracking all necessary properties.&lt;/p&gt;
&lt;p&gt;On a side note, if you are trying to render activities that would usually also appear in the timeline anyway and run into issues with &amp;ldquo;Record failed to load&amp;rdquo;, try deactivating Activities in the timeline configuration as there might be a mixup between the OOTB functionality and your Timeline Custom Connector.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Subgrids in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-grids/</link>
        <pubDate>Sun, 28 Sep 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-grids/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-grids/cover.jpg" alt="Featured image of post [VIDEO] Subgrids in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will manipulate the display of cells in a Subgrid.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6NXjUjEOZl8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6NXjUjEOZl8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-grids/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>D365NameCombiner: New Samples</title>
        <link>https://www.marius-wodtke.de/post/other/name-combiner-2025/</link>
        <pubDate>Sun, 14 Sep 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/name-combiner-2025/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/cover.jpg" alt="Featured image of post D365NameCombiner: New Samples" /&gt;&lt;p&gt;Back when I started this blog, I wrote &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/&#34; &gt;a series of articles about Plugin Self Registration&lt;/a&gt; and it featured a specific use case for this technique in form of the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365NameCombiner&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365NameCombiner&lt;/a&gt; which was introduced in &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;post/plugin-self-registration/praxis/&#34; &gt;Putting it into practice&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In this post I want to revisit this tool as I still think it&amp;rsquo;s very relevant and just lacks some advertisement and samples.&lt;/p&gt;
&lt;h2 id=&#34;the-basics&#34;&gt;The Basics&lt;/h2&gt;
&lt;p&gt;The D365NameCombiner is installed as a managed solution that comes with the app &lt;em&gt;Name Combiner Administration&lt;/em&gt; where you create &lt;em&gt;Name Combinations&lt;/em&gt;. These specify and table, a target column and a formula(/format) for this column. This format is then applied synchronously whenever one of the fields below change.&lt;/p&gt;
&lt;p&gt;To have a very basic example, lets take a combined address format that is commonly used in german speaking countries.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Street Streetnumber
Zip City
Country
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To combine this into a single field the following Name Combination is created.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/AddressCombination.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The combination relies on four input columns.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/AddressInput.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Whenever one of the 4 columns changes…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/AddressOutput.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;…the output column is changed synchronously.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;A main advantage here is that this is applied synchronously. A flow doing the same thing would introduce a delay, which could be detrimental if someone tries to print the address right after changing it. They would now persist the old address in the generated document.&lt;/p&gt;
&lt;p&gt;This whole sample is really for illustration only. For once &lt;em&gt;address1_composite&lt;/em&gt; does have a very similar format already (it just swaps zip and city) and for example a Word Template for a letter could combine the address from the separate columns anyways. So in the next sections we will dive into some more practical scenarios.&lt;/p&gt;
&lt;h2 id=&#34;the-request&#34;&gt;The Request&lt;/h2&gt;
&lt;p&gt;In this sample a bank employee shall log the type of a customer request, for example &lt;em&gt;Balance&lt;/em&gt; or &lt;em&gt;Remaining Debt&lt;/em&gt;, in a custom entity. As any entity, this needs a primary column of type string that is shown for example in Lookups. However, the natural things for the employee to enter are an Optionset value (Request Type), a Lookup (Customer) and potentially a Date (Request Date). If we now combine a name in a Flow, the form will still show &amp;ldquo;New Customer Request&amp;rdquo; in the header after the request is saved until the flow has ran and the user refreshed the form, not ideal. So let&amp;rsquo;s try this scenario with the D365NameCombiner.&lt;/p&gt;
&lt;p&gt;This time, we will use additional formatters within the formula. These are behind the double-colon &amp;ldquo;:&amp;rdquo; and specify additional information for the logic. We have&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{0:fullname}&lt;/code&gt; for the Lookup, where fullname specifies the column of the entity behind the lookup.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{1:LCID=1033}&lt;/code&gt; for the Optionset, where LICD=1033 specifies that the english displayname of the selected option shall be used.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{2:yyyy-MM-dd}&lt;/code&gt; for the datetime field, which specifies that only the date part shall be printed (in ISO format) because it has the &lt;em&gt;Date Only&lt;/em&gt; subtype, making the time part unnecessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/RequestCombination.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Additional formatters are leveraged.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/RequestOutput.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The name does not contain raw values, but nicely formatted ones.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-loan-application&#34;&gt;The Loan Application&lt;/h2&gt;
&lt;p&gt;Continuing with our bank employee, they record loan applications by customers. Because much more data is entered here, giving the application a name would be feasible, but in practice, the names are all over the place. Therefore the organization wants to combine a name from some required fields.&lt;/p&gt;
&lt;p&gt;Compared to &lt;em&gt;&lt;a class=&#34;link&#34; href=&#34;#the-request&#34; &gt;The Request&lt;/a&gt;&lt;/em&gt; we have 2 additional formatters&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;{0:0.00} for the money field, formatting the amount to loan with 2 decimal places.&lt;/li&gt;
&lt;li&gt;{2:LCID=1033;Separator=&amp;amp;} for the Multi-Select-Optionset, specifying a separator in case the user selected multiple options.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This time the outcome is a more lengthy, but well formatted sentence.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/LoanCombination.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Not specifying a format for the decimal would render all available decimal places.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/name-combiner-2025/LoanOutput.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The result is a descriptive sentence of the loan application.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Generating names synchronously does not require a programmer! With the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365NameCombiner&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365NameCombiner&lt;/a&gt; solution everyone with maker knowledge can fulfill requirements for synchronous text field generation, even if methods like a formula column are not available because it is about the primary column.&lt;/p&gt;
&lt;p&gt;Additional formatters after a column allow for well formatted results. For example navigating to columns behind a lookup and printing the labels of optionsets.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Using the WebApi with the Power Pages Client API</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi/webapi/</link>
        <pubDate>Sun, 31 Aug 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi/webapi/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi/webapi/cover.jpg" alt="Featured image of post [VIDEO] Using the WebApi with the Power Pages Client API" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will manipulate data with scripting via the Power Pages WebApi.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/OVCkS4V7guA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/OVCkS4V7guA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi/webapi/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>CIJ Form Scripting: Working with Lookups</title>
        <link>https://www.marius-wodtke.de/post/cij/form-lookup-scripting/</link>
        <pubDate>Sun, 17 Aug 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/cij/form-lookup-scripting/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/cij/form-lookup-scripting/cover.jpg" alt="Featured image of post CIJ Form Scripting: Working with Lookups" /&gt;&lt;p&gt;I recently was tasked to prefill Marketing Forms based on query parameters and while this is relatively straight forward for normal field, as always, lookups are kind of special.&lt;/p&gt;
&lt;p&gt;And to make matters worse, I was absolutely unable to find anything about this online, so here we go.&lt;/p&gt;
&lt;p&gt;To have something visual for this article, the requirement may sound something like this &amp;ldquo;When someone clicks the Contact Us button on the Toys page, the category of the request should be prefilled with Toys, they should not have to select it manually again&amp;rdquo;. This can be solved with copying the form and setting a default value, however, this won&amp;rsquo;t scale well if you have many categories and maybe even combinations, for example for different countries.&lt;/p&gt;
&lt;h2 id=&#34;what-happens-in-lookup-fields&#34;&gt;What happens in Lookup fields?&lt;/h2&gt;
&lt;p&gt;To start the investigation I tried to find out what happens when I use the lookup as a user. Because when you first inspect the HTML, the lookup has no elements, but when I click on it, it certainly has! And yes, as soon as I click on the field, the Chrome Dev Tools (F12) will show a request for given lookup and the response is 10 records, if I scroll down, the next 10 are retrieved.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/cij/form-lookup-scripting/before.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Before the first user interaction, there are no elements in the lookup&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/cij/form-lookup-scripting/network.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;With the first click, the first 10 elements are loaded via Network Request&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/cij/form-lookup-scripting/after.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After the network request has finished, there is a list with the first 10 elements.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Also the input element in the HTML now has children! While the name should be pretty clear, the &lt;code&gt;data-value&lt;/code&gt; is not.
Applying a Base64 decode at least reveals, some guid, but it&amp;rsquo;s not the guid of the record and seems to be the same for all elements in the list. Decoding with a BinaryFormatter deemed it to be an illegal format, at least without further preprocessing.&lt;br&gt;
Fortunately for my case: The name was unique anyways and therefore was sufficient for selecting the correct record. I would assume this is the most common case for the readers as well, because if there are duplicate names, how would the user of the form know which one to select?
When the user now selects a record, the data-value of the selected option is copied into the data-value of the input element.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_leadcategory,IAAAAGRkMzJkODBmZjdiZTQ5YzI5ZTc0Mjk0YmFlZDQ1YmE5AAEAABAAAAA7mbQrnmv6ja2+ms04dQwDbcuW8msTK5TscxdandO3pUkN8rwBlgf4RyISNJIq4iHhBdGRaXYNatvBTj7Bvy9tcEwpCkIf7vTZBxVfgShYGB94+huu11BZEBen2Gxm0vRkFCoUnN05ncrpV87dap63PIFoBApJKYYMArmpIcIZ+hp350g3t72tTiMJXDpOr0u4/RgbpQ69YGyWfx3vlbd2dv0E+w+I06uNAiYP9j9Prd5egHBqK5HfI2048AzvylcB7v6p7qsifJm5/nQBMMXPiesj1JdrbzeF56hXM5MEBKwkM/lTyzugZKb4nbJO7gZPDpud5DcgZy7NZXVcWk3e3Rq43jsaBANrtY971yNIoqeX2sLhSfA8LsJAt6uBl+V+hr5BsJZANVMFymzDf11qZ2Q5o+rCvrCrWSCtM/V/gA==&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;recorid&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;98954879-9a47-f011-877a-0022488278a7&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Base64 of the value after the comma
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;decoded&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;���dd32d80ff7be49c29e74294baed45ba9������;+k8um˖k+sZӷI
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;G&amp;#34;4*!ёiv
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;jN&amp;gt;/mpL)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;B_(XxPYlfd*9Wj&amp;lt;h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;I)!wH7N#	\:NK`&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;l&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;vv&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ӫ&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;O&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;^&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pj&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;m8&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;W&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ω&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ԗko7W3&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;$3S&lt;/span&gt;;&lt;span style=&#34;color:#a6e22e&#34;&gt;dN&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;O&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;eu&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;\&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ZM&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;HI&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;.&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;S&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;l&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;jgd9¾Y&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/cij/form-lookup-scripting/selected.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Selecting a record changes the input element. Notice how it now has a new property “data-value”.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;setting-a-lookup&#34;&gt;Setting a Lookup&lt;/h2&gt;
&lt;p&gt;So, given a name, we can find the record in the list of options, then navigate one element up to get the data-value and copy it over into input element. Sounds doable, but there is still a problem: If the user has not interacted with the lookup yet, no options will be loaded. First idea is to fake a click, but due to the limit of 25 records retrieved even faking a scroll will soon get to its limit, especially if you have something like 1000+ options. Instead we can fake the user typing a search word to get the list down to less then 25 elements - well if names are unique, it will be 1 element - and then select from the resulting list. So with that, code would start to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setMyLookup&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input[name=mwo_category]&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;dispatchEvent&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;keyup&amp;#39;&lt;/span&gt;)); 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;keyup&lt;/code&gt; event signals to the MS form logic that there was something entered to the input, meaning someone searched for a value and thus it will fire the search request to the server.&lt;/p&gt;
&lt;p&gt;But now we still face the problem, that we don&amp;rsquo;t know when the request is finished and the MS form logic has populated the lookup with the options. We will solve this with an &lt;code&gt;MutationObserver&lt;/code&gt;. This is something that will observe an HTML element for changes and once observed, will call a function. On the inside we can check whether the observed mutation indeed included the option we wanted to select.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setMyLookup&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input[name=mwo_category]&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;dispatchEvent&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;keyup&amp;#39;&lt;/span&gt;)); 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupContainer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closest&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;div.lookupFormFieldBlock&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MutationObserver&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;trySetMyLookupValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;lookupContainer&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;disconnect&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;observe&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;lookupContainer&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;childList&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;subtree&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;trySetMyLookupValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;lookupContainer&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;disconnect&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are creating the MutationObserver and register it to observe the enclosing &lt;code&gt;div&lt;/code&gt; here, with its &lt;code&gt;childList&lt;/code&gt; and &lt;code&gt;subtree&lt;/code&gt; to also capture changes to the options. Whenever one of those objects changes, we want to execute &lt;code&gt;trySetMyLookupValue&lt;/code&gt; (which we will discuss soon) and if it is successful, we want to disconnect the observer to avoid hogging browser resources. Not disconnecting could also lead to some problems with the user setting a value manually etc. Finally we are checking manually once, just in case the options were already loaded, e.g. because the user already interacted or the new search value is the same as the old one.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;trySetMyLookupValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;lookupContainer&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ul&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupContainer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ul&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;matchingLi&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ul&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelectorAll&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;li&amp;#39;&lt;/span&gt;)).&lt;span style=&#34;color:#a6e22e&#34;&gt;find&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;li&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;optionDiv&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;li&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.ui-menu-item-wrapper&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;optionDiv&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;optionDiv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;textContent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;trim&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newVal&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;matchingLi&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dataValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;matchingLi&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getAttribute&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data-value&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;dataValue&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setAttribute&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data-value&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dataValue&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In &lt;code&gt;trySetMyLookupValue&lt;/code&gt; we are first checking if the list (&lt;code&gt;ul&lt;/code&gt;) is present at all, if not, there are no items loaded yet. Next we are checking the divs (identified by the class &lt;code&gt;.ui-menu-item-wrapper&lt;/code&gt;) inside the list items (&lt;code&gt;li&lt;/code&gt;) for one that matches our option. Remember that this code matches by name here.&lt;br&gt;
If a match is found, the data-value is copied from the option to the lookups input field to ensure that the submission of the form will include the selected option.
Only if an option was found, this function will return &lt;code&gt;true&lt;/code&gt; and thus disconnects the observer.&lt;/p&gt;
&lt;h2 id=&#34;reading-the-selected-option&#34;&gt;Reading the selected option&lt;/h2&gt;
&lt;p&gt;This one is much easier! Of course based on the previous knowledge we would need to compare the data-value to the options to find the selected option and then navigate to the div with the name.&lt;br&gt;
But there is a much easier way for this: You can simply read the value of the input field. It contains the name of the selected option as well and you can react to changes simply by using an onChange handler.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;checkMyLookupChanged&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;matchedVal&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input[name=mwo_category]&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;lookupField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;matchedVal&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ... the logic
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input[name=mwo_category]&amp;#39;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;change&amp;#39;&lt;/span&gt;, () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;checkMyLookupChanged&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Toys&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/cij/form-lookup-scripting/value.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The value of the input is the name of the selected option. Here I’ve selected by id, but I think selecting by name is more readable.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;As always, lookups are a little more tricky.&lt;br&gt;
To work with lookups in frontend scripting you first need to understand that the selectable values are not preloaded with the form. Instead they are retrieved on the fly as the user uses the field by clicking (first 10), scrolling (next 10) or typing (up to 10 matching the text fragment).&lt;/p&gt;
&lt;p&gt;To set a value, you can fake the user searching by setting the input field value to the desired name and then dispatching the &lt;code&gt;keyup&lt;/code&gt; event. Now observe the changes to containing &lt;code&gt;div&lt;/code&gt; element to be notified once the list has finished loading. Finally, find the value in the list of options and copy over the data-value property.&lt;/p&gt;
&lt;p&gt;To read a value, you can simply read the value property. When the user selects an option, this will also trigger a change event which you can listen to.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Installing the Power Pages Client API</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi/installation/</link>
        <pubDate>Sun, 03 Aug 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi/installation/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi/installation/cover.jpg" alt="Featured image of post [VIDEO] Installing the Power Pages Client API" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will set up the Power Pages Client API for your own projects in 2 different ways, both for when you are using JavaScript and for when you are using TypeScript with a bundler.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/yzdWePHB71E&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/yzdWePHB71E&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi/installation/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Help your users!</title>
        <link>https://www.marius-wodtke.de/post/other/custom-help/</link>
        <pubDate>Sun, 20 Jul 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/custom-help/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/cover.jpg" alt="Featured image of post Help your users!" /&gt;&lt;p&gt;You probably have documentation for your users, right? Something describing processes, tips on how to use Dynamics and so on. Good news is: There is an easy way to link your users to this guidance!&lt;/p&gt;
&lt;p&gt;There is this little question mark symbol in the Command Bar right next to your user profile picture. It usually links to &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/#pivot=business-apps&amp;amp;panel=customer-engagement&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Microsoft documentation&lt;/a&gt;, but we can make it link to our own pages as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/help.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The help button is found in the top-right ribbon.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;enabling-custom-help-in-the-admin-center-configuration&#34;&gt;Enabling Custom Help in the Admin Center (Configuration)&lt;/h2&gt;
&lt;p&gt;To change the link, go to the &lt;em&gt;Power Platform Admin Center (PPAC)&lt;/em&gt; -&amp;gt; &lt;em&gt;Manage&lt;/em&gt; -&amp;gt; &lt;em&gt;Environments&lt;/em&gt;. Select you Environment and go to &lt;em&gt;Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Product&lt;/em&gt; -&amp;gt; &lt;em&gt;Features&lt;/em&gt;. Here you will look for a section called &lt;em&gt;Help Features&lt;/em&gt; and switch on &lt;em&gt;Custom help for customizable entities&lt;/em&gt; which will allow you to to set the &lt;em&gt;Global custom help URL&lt;/em&gt; where you enter the home page of your documentation site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/ppac1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/ppac2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/ppac3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now don&amp;rsquo;t forget to hit save at the bottom to save your changes!&lt;/p&gt;
&lt;p&gt;The changes will become active almost immediately, however, as these elements are cached, make sure to perform a &amp;ldquo;Clear Cache Refresh&amp;rdquo; when trying out the effects.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve simply linked to my blog here and no matter where I click the help button in this environment now, it will link me to the home page of this blog.&lt;/p&gt;
&lt;h2 id=&#34;context-parameters&#34;&gt;Context Parameters&lt;/h2&gt;
&lt;p&gt;Below the &lt;em&gt;Global custom help URL&lt;/em&gt; you will also find a checkbox &lt;em&gt;Append parameters to URL&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/params1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If we check this and save, Dynamics will append following query-parameters to the given URL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;userlcid&lt;/strong&gt; (e.g. &lt;code&gt;userlcid=1033&lt;/code&gt;)&lt;br&gt;
The language of the logged in user&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;typename&lt;/strong&gt; (e.g. &lt;code&gt;typename=contact&lt;/code&gt;)&lt;br&gt;
The logicalname of the table if at a grid or form&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;entrypoint&lt;/strong&gt; (e.g. &lt;code&gt;entrypoint=form&lt;/code&gt;)&lt;br&gt;
Not appended for grids or dashboards, the only place I found it is when clicking help from a form&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;formid&lt;/strong&gt; (e.g. &lt;code&gt;formid=1fed44d1-ae68-4a41-bd2b-f13acac4acfa&lt;/code&gt;)&lt;br&gt;
Only appended on forms, contains the guid of the system form&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sample URLs reach from&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/?userlcid=1033&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://www.marius-wodtke.de/?userlcid=1033&lt;/a&gt;&lt;br&gt;
when opened from a Dashboard to&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/?userlcid=1033&amp;amp;typename=contact&amp;amp;entrypoint=form&amp;amp;formid=1fed44d1-ae68-4a41-bd2b-f13acac4acfa&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://www.marius-wodtke.de/?userlcid=1033&amp;typename=contact&amp;entrypoint=form&amp;formid=1fed44d1-ae68-4a41-bd2b-f13acac4acfa&lt;/a&gt;&lt;br&gt;
when opened from a specific form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/params2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Append parameters to URL adds up to 4 query parameters&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;one-link-per-table-customization&#34;&gt;One link per table (Customization)&lt;/h2&gt;
&lt;p&gt;However, likely your documentation is already present and did not plan for these parameters. Or your documentation platform is simply not configurable to support these. You would have the option now to link to a proxy service that rewrites the url. For example you enter &lt;code&gt;my-proxy.com&lt;/code&gt; in the PPAC and it translates the userlcid and typename parameters to route parameters instead. Calling &lt;code&gt;my-proxy.com?userlcid=1033&amp;amp;typename=contact&lt;/code&gt; would then forward to &lt;code&gt;my-docs.com/en-US/contact&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, if a) the proxy needs a dictionary because the routes do not match (e.g. &lt;code&gt;typename=mwo_article&lt;/code&gt; needs to become &lt;code&gt;/Articles/&lt;/code&gt;) and b) automatically resolving the language parameter is not that important, there might be an easier solution.&lt;/p&gt;
&lt;p&gt;Navigate to your solution and select a table. Now click on &lt;em&gt;Properties&lt;/em&gt; -&amp;gt; &lt;em&gt;Advanced options&lt;/em&gt; -&amp;gt; &lt;em&gt;For this table&lt;/em&gt; and enable &lt;em&gt;Provide custom help&lt;/em&gt;. Then enter the documentation for this specific table in &lt;em&gt;Help URL&lt;/em&gt; and save.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/sol1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Custom help is found under the advanced options of the table properties&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now clicking the help button in the context of the table, no matter if you are on the grid or a form, the button will link to the link you entered for this table. If you have &lt;em&gt;Append parameters to URL&lt;/em&gt; configured, they will still be appended!&lt;/p&gt;
&lt;p&gt;Also note, that the properties of a table are only transported with the solution if &lt;em&gt;Include table metadata&lt;/em&gt; is enabled in the &lt;em&gt;Table segmentation properties&lt;/em&gt; (you can find this under &lt;em&gt;Advanced&lt;/em&gt; at the table ribbon). If you fail to enable this, deploying the solution will not deploy the custom help link.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/sol2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Custom help links are only transported if Include table metadata is enabled&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;This option also works if custom help is not enabled for the environment in PPAC! That can be helpful for ISVs as they can link their tables to their own documentation, after all, the user won&amp;rsquo;t find anything helpful in the MS docs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/custom-help/sol3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When clicking help from the account now, the user is directed to a specific article on my blog&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Linking your own documentation might be more helpful to the user than the official docs of MS.
We can manipulate the help button (? in the top-right ribbon) to link to any url.&lt;/p&gt;
&lt;p&gt;For once there is a configuration option in Power Platform Admin Center which is valid for the whole environment. This will link to a single URL and has the option to append query-parameters, most importantly containing the users language and table they are currently on.&lt;/p&gt;
&lt;p&gt;However, many systems won&amp;rsquo;t be able to interpret these parameters and thus the customization option in Make might be used. Here a URL is entered per table, which will overrule the PPAC configuration for these tables.&lt;/p&gt;
&lt;p&gt;Most organizations will therefore run a combination of both methods, the PPAC configuration to catch all instances that are not in the context of a table, plus links for each table that have a documentation available. If the OOTB parameters are planned from the beginning, the effort of entering the specific URL for each table may be saved, in this case no further customization is necessary.&lt;/p&gt;
&lt;p&gt;If you do not have control over the environment because you just deliver the solution, use the customization option to pack your help links with the solution.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Attribute.addOnChange in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-addonchange/</link>
        <pubDate>Sun, 06 Jul 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-addonchange/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-addOnChange/cover.jpg" alt="Featured image of post [VIDEO] Attribute.addOnChange in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will extend with the common method of addOnChange to allow reacting to user input!&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6NXjUjEOZl8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6NXjUjEOZl8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-addOnChange/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: What are the correct Table Segmentation Properties?</title>
        <link>https://www.marius-wodtke.de/post/pattern/table-segmentation/</link>
        <pubDate>Sun, 22 Jun 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/table-segmentation/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/table-segmentation/cover.jpg" alt="Featured image of post Patterns: What are the correct Table Segmentation Properties?" /&gt;&lt;p&gt;&amp;ldquo;Table Segmentation Properties&amp;rdquo; or &amp;ldquo;Root Component Behavior&amp;rdquo; is an important concept that everyone working with solutions should understand thoroughly!
The terms describe how much of a table is in the solution and will affect deployment of it.&lt;/p&gt;
&lt;h2 id=&#34;when-to-use-which-behavior&#34;&gt;When to use which behavior&lt;/h2&gt;
&lt;p&gt;When exporting a solution and looking at the solution.xml file you will find a section about root components like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;RootComponents&amp;gt;
    &amp;lt;RootComponent type=&amp;#34;1&amp;#34; schemaName=&amp;#34;account&amp;#34; behavior=&amp;#34;1&amp;#34; /&amp;gt;
    &amp;lt;RootComponent type=&amp;#34;1&amp;#34; schemaName=&amp;#34;contact&amp;#34; behavior=&amp;#34;2&amp;#34; /&amp;gt;
    &amp;lt;RootComponent type=&amp;#34;1&amp;#34; schemaName=&amp;#34;mwo_test&amp;#34; behavior=&amp;#34;0&amp;#34; /&amp;gt;
    &amp;lt;RootComponent type=&amp;#34;80&amp;#34; schemaName=&amp;#34;msdynce_saleshub&amp;#34; behavior=&amp;#34;0&amp;#34; /&amp;gt;
&amp;lt;/RootComponents&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, there 3 tables inside, account, contact and mwo_test, which is a custom table created within the solution.
They all have a different value in the &lt;code&gt;behavior&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;Behavior &amp;ldquo;2&amp;rdquo; contains the least and should be your default for any table that you do not create yourself. If I only add a column to the contact table and create/modify a form for it, this behavior is the perfect choice, it fully complies with the rule of thumb &amp;ldquo;Include only what is necessary to a solution to minimize export/import times and avoid conflicts with Managed Solution updates&amp;rdquo;. It does not really have a name, but I will call it &amp;ldquo;Only include selected subcomponents&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Switching to behavior &amp;ldquo;1&amp;rdquo;, which is called &amp;ldquo;Include Table Metadata&amp;rdquo; is necessary as soon as you start to change the Properties of a table. These are things like the Display Name, Enable Audit, Track Changes etc.
This a bit tricky, because you can set these properties also in a solution containing the table as &amp;ldquo;Only include selected subcomponents&amp;rdquo; and they will take affect on the source environment, however they will not be exported and thus not transported to the target environment. You will learn how to check which behavior is active in the next section, however this is easy to overlook and it would be nice if Make would at least warn you about the currently selected behavior. Environment Variables for example have a much better UX in terms of what is included to the solution.&lt;/p&gt;
&lt;p&gt;Behavior &amp;ldquo;0&amp;rdquo; is sometimes necessary but should be used with caution. It was preselected for me by Dynamics when I created the Test table in the solution and that is because the first time a new table is introduced to a target system via solution import, it needs to be with behavior &amp;ldquo;0&amp;rdquo;. It&amp;rsquo;s called &amp;ldquo;Include all objects&amp;rdquo;.
The only other situation where you would choose this behavior is when you have created a custom entity in another solution but now want to transfer it to the current one for deployment as you are not going to deploy the other solution first. This is because &amp;ldquo;Include all objects&amp;rdquo; will be needed for successful creation of the table in the target environment. If you would choose a different behavior your solution would have a dependency on the table being present in the target environment.
However, you cannot misuse this to fully include a table from a managed solution into your solution as the solution will still report the dependency on the managed solution containing the entity. Furthermore, you will run into bigger deployment issues when doing this to frequently used OOTB tables like account or contact. Including all objects means you will create dependencies to all managed solutions defining a component in these tables and this virtually means that your target environment has to have exactly the same setup as your source.
To make this more visual: Someone started a PoC for Field Service on the dev environment, great, now you can&amp;rsquo;t deploy to production anymore since the solution has around 20 dependencies to various Field Service solutions. You now have the option to install Field Service to production or remove the account table from the solution and readd it with only the components needed. By the way, the last option is of course the correct option, but what are the needed components? And you better not make a mistake when adding them because failing to add a custom column will delete it from production and thus loose its data!
To summarize: Never use &amp;ldquo;Include all objects&amp;rdquo; (behavior &amp;ldquo;0&amp;rdquo;) on OOTB entities. But this option is almost always the right choice for your custom entities.&lt;/p&gt;
&lt;h2 id=&#34;how-to-set-the-behavior&#34;&gt;How to set the behavior&lt;/h2&gt;
&lt;p&gt;As explained in the previous section, when creating a table in a solution the behavior is automatically set to &amp;ldquo;Include all objects&amp;rdquo;. When adding an existing table you can choose the behavior via 2 checkmarks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/table-segmentation/addExisting.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The 3 different behaviors are settable via 2 checkboxes&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;But what if I already included the table? It will not appear in the &lt;em&gt;Add Existing&lt;/em&gt; -&amp;gt; &lt;em&gt;Table&lt;/em&gt; dialog and when selecting &lt;em&gt;Add Existing&lt;/em&gt; at the respective table, the dialog will already be advanced to selecting subcomponents.
The behavior can be checked and modified at the table under &lt;em&gt;Advanced&lt;/em&gt; -&amp;gt; &lt;em&gt;Table Segmentation Properties&lt;/em&gt;. Here we find the same checkmarks to set.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/table-segmentation/tsp1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Table Segmentation Properties is found under Advanced at the table overview screen&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/table-segmentation/tsp2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The checkmarks of Table Segmentation Properties are the same as the first step of Add Existing Table&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Note that you are able to select the checkmarks and you are also able to deselect &amp;ldquo;Include table metadata&amp;rdquo; again after it was selected. However, once you selected and saved &amp;ldquo;Include all objects&amp;rdquo;, you will not be able to deselect it again. And as illustrated above: Following the suggestion to remove and readd the component is the correct fix, but you really want to avoid getting into this situation in the first place. So just be careful with the &amp;ldquo;Include all objects&amp;rdquo; behavior.&lt;/p&gt;
&lt;p&gt;Also note that if &amp;ldquo;Include all objects&amp;rdquo; is chosen this also means that table metadata is included as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/table-segmentation/tsp3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Include all objects is only deselectable here because I have not saved yet&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;For OOTB/Managed tables, leave the checkmarks &amp;ldquo;Include table metadata&amp;rdquo; and &amp;ldquo;Include all objects&amp;rdquo; unselected by default. If you start changing properties (e.g. Display Name or Audit) of a table, check the &lt;em&gt;Table Segmentation Properties&lt;/em&gt; under &lt;em&gt;Advanced&lt;/em&gt; and make sure &amp;ldquo;Include table metadata&amp;rdquo; is enabled. There is virtually no situation where you would want to turn on &amp;ldquo;Include all objects&amp;rdquo; for an OOTB/Managed table and it can be tricky/risky to resolve the issue if the option was selected.&lt;/p&gt;
&lt;p&gt;For custom tables use &amp;ldquo;Include all objects&amp;rdquo; by default. This makes sure the table can be created in the target environment and ensures that all dependencies within the table are fulfilled. This will be preselected for you if you create the table within the solution.&lt;/p&gt;
&lt;p&gt;If you hear someone refer to &lt;em&gt;Root Component Behavior&lt;/em&gt; this is because the &lt;em&gt;Table Segmentation Properties&lt;/em&gt; are reflected as a property called &lt;code&gt;behavior&lt;/code&gt; when exporting the solution.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Hiding Sections in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-sectionvisibility/</link>
        <pubDate>Sun, 08 Jun 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-sectionvisibility/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-sectionVisibility/cover.jpg" alt="Featured image of post [VIDEO] Hiding Sections in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will extend with the common methods of setVisible and getVisible for Sections in forms!&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/eYxUCuKaAdU&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/eYxUCuKaAdU&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-sectionVisibility/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Implementing efficiently with Earlybound Interfaces</title>
        <link>https://www.marius-wodtke.de/post/other/earlybound-interfaces/</link>
        <pubDate>Sun, 25 May 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/earlybound-interfaces/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/earlybound-interfaces/cover.jpg" alt="Featured image of post Implementing efficiently with Earlybound Interfaces" /&gt;&lt;p&gt;&lt;strong&gt;Avoid duplication&lt;/strong&gt;, that&amp;rsquo;s one of the principles of clean code. But sometimes this collides with our wish to work with Earlybounds, which also has the argument of &lt;strong&gt;Fail early&lt;/strong&gt; on its side.&lt;/p&gt;
&lt;p&gt;So, lets consider the following requirement &amp;ldquo;We would like to alleviate the user from entering the country when entering the address of an Account. It should be approximated from Line1, City and PostalCode&amp;rdquo;. How this works is not relevant for my sample, but if it happens in a PreOperation-Plugin, the code would look something like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; AutocompleteCountry(ILocalPluginContext localPluginContext, Account account)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(account.Address1_Country))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; guessedCountry = GuessCountry(account);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(guessedCountry))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    account.Address1_Country = guessedCountry;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;rsquo;s the crux now? If this works well, the next request will be &amp;ldquo;Make it happen on Contact, Lead and MyCustomAddress as well&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;applying-interfaces&#34;&gt;Applying interfaces&lt;/h2&gt;
&lt;p&gt;The problem now is, that we can&amp;rsquo;t simply pass a Contact to the function. Even though it would have the necessary Address1_ fields, Account and Contact are two different objects for the compiler. It would work with Latebound entities, however that would loose our Fail Early advantage.&lt;/p&gt;
&lt;p&gt;The solution is interfaces. Let&amp;rsquo;s define one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IAddress&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_Line1 { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_City { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_PostalCode { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_Country { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Guid Id { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now we only need to apply this to the given entities. Fortunately, if you are using popular tools like the Earlybound Generator (v2) from the XrmToolbox, they will define the entities as &lt;code&gt;partial&lt;/code&gt;, allowing us to define additional properties. Just make sure that you are working in the same namespace as the generated class, in my sample this is the default namespace &lt;code&gt;DataverseModel&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; DataverseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;partial&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Account&lt;/span&gt; : IAddress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;partial&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Contact&lt;/span&gt; : IAddress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;partial&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Lead&lt;/span&gt; : IAddress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it for Contact and Lead. You will now understand why I chose this specific address sample, the entities simply comply to the interface and we can go on to adapt our country logic.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; AutocompleteCountry(ILocalPluginContext localPluginContext, IAddress address)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(address.Address1_Country))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; guessedCountry = GuessCountry(address);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;.IsNullOrEmpty(guessedCountry))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address.Address1_Country = guessedCountry;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This worked without any weird passing of &lt;code&gt;Func&lt;/code&gt; for getters and &lt;code&gt;Action&lt;/code&gt; for setters, splitting out the parameters for &lt;code&gt;GuessCountry&lt;/code&gt; and so on. No, simply the input was changed to &lt;code&gt;IAddress&lt;/code&gt; and since the Account is and &lt;code&gt;IAddress&lt;/code&gt;, we can pass it.&lt;/p&gt;
&lt;h2 id=&#34;making-entities-comply&#34;&gt;Making entities comply&lt;/h2&gt;
&lt;p&gt;But if we try the same for the MyCustomAddress, problems appear.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/earlybound-interfaces/error.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;By default the custom entity won’t comply with the interface.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The only way to make it comply would be to name the logical names as close to the Address1_ fields as possible and somehow get rid of the inevitable publisher prefix in the generated class. That sounds like more effort than duplication and might be completely unreasonable if there is already data in the table.&lt;/p&gt;
&lt;p&gt;Fortunately there is a better solution!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; DataverseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;partial&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MwO_MyCustomAddress&lt;/span&gt; : IAddress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_Line1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; =&amp;gt; MwO_Line1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt; =&amp;gt; MwO_Line1 = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_City
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; =&amp;gt; MwO_City;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt; =&amp;gt; MwO_City = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_PostalCode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; =&amp;gt; MwO_PostalCode;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt; =&amp;gt; MwO_PostalCode = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; Address1_Country
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; =&amp;gt; MwO_Country;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt; =&amp;gt; MwO_Country = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, I can simply rewire the Properties to write the desired fields. When the logic reads &lt;code&gt;Address1_City&lt;/code&gt;, it will redirect to &lt;code&gt;MwO_City&lt;/code&gt; which (in the generated code) returns &lt;code&gt;this.GetAttributeValue&amp;lt;string&amp;gt;(&amp;quot;mwo_city&amp;quot;)&lt;/code&gt;.&lt;br&gt;
When the logic sets &lt;code&gt;Address1_Country&lt;/code&gt; it will redirect to &lt;code&gt;MwO_Country&lt;/code&gt;which (in the generated code) calls &lt;code&gt;this.SetAttributeValue(&amp;quot;mwo_city&amp;quot;, value)&lt;/code&gt;. The fields can be used interchangeably.&lt;/p&gt;
&lt;p&gt;Simple to read and without any further problems we can pass our custom address table into the function. This is the method you will use in many scenarios where the entities won&amp;rsquo;t line up as nice as the Address1_ fields do. If it is really a one time edge case where logic is applied to fields that are not close or don&amp;rsquo;t have a natural naming, you can even consider inventing a separate prefix and route the fields in all entities, e.g. TaxCodes_Country (-&amp;gt; to not confuse with an address country here).&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;When logic should be applied the same way to multiple tables, applying interfaces to the given Earlybound entities is a great way of achieving reusable code that avoids duplication. This is possible because Earlybound entities are usually marked as partial and thus can be extended.&lt;/p&gt;
&lt;p&gt;You first define an interface with properties. Then you create another definition of an entity in a separate file - not in the Earlybounds folder - with the same namespace as the original Earlybound definition. Apply the interface to the new (partial) class.&lt;/p&gt;
&lt;p&gt;If your table does not and maybe cannot fit the property names in the interface, you can simply rewire the getter and setter to the property you would like to work with. Once all entities are prepared with the interface, you simply swap the concrete entity with the interface in your logic and can now pass all relevant tables to the code.&lt;/p&gt;
&lt;p&gt;Below you will find a short writeup of all the syntax we&amp;rsquo;ve used. Everything that is enclosed in _ (underscore) should be replaced with your own variable names.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; _InterfaceNameSpace_ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_MyInterface_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; _MyCommonProperty_ { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; _MyPropertyWrittenDifferently_ { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; _EarlyboundNameSpace_
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;partial&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_MyEntity_&lt;/span&gt; : _MyInterface_
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; _MyPropertyWrittenDifferently_ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; =&amp;gt; _MyActualProperty_; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt; =&amp;gt; _MyActualProperty_ = &lt;span style=&#34;color:#66d9ef&#34;&gt;value&lt;/span&gt;; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; _LogicNameSpace_
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_MyLogic_&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; _MyFunction_(_MyInterface_ record) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// Do stuff with multiple tables!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
        </item>
        <item>
        <title>[VIDEO] Hiding Tabs in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-tabsvisibility/</link>
        <pubDate>Sun, 18 May 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-tabsvisibility/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-tabsVisibility/cover.jpg" alt="Featured image of post [VIDEO] Hiding Tabs in Power Pages" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will extend with the common methods of setVisible and getVisible for Tabs in forms!&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/AJQxj_tR3Vk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/AJQxj_tR3Vk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-tabsVisibility/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Easy Guide to Mask Sensitive Columns | Step-by-Step Tutorial (No Coding)</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-05/marius/</link>
        <pubDate>Wed, 07 May 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-05/marius/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-05/marius/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Easy Guide to Mask Sensitive Columns | Step-by-Step Tutorial (No Coding)" /&gt;&lt;p&gt;In the April edition of the Power Platform Learners Group, I demoed a feature that reached GA with the current wave: Masking data in fields with column security.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/Vx3DKBPGDww&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/Vx3DKBPGDww&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-05/marius/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to use Environment Variables for Site Settings in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/envvar/</link>
        <pubDate>Tue, 29 Apr 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/envvar/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/envvar/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to use Environment Variables for Site Settings in Power Pages?" /&gt;&lt;p&gt;Welcome to the Episode 25th of the Power Pages Quick Tips Series. In this episode, we are going to talk about How to use Environment Variables for Site Settings in Power Pages?&lt;/p&gt;
&lt;p&gt;Environment variables are the variable that holds configuration values specific to an environment that the application or features can reference.&lt;/p&gt;
&lt;p&gt;When working with environment variables for site settings? , it&amp;rsquo;s important to follow best practices to ensure efficient and effective management. Use environment variables to define site settings values across various environments with the enhanced data model. While the standard data model relies on deployment profiles, the enhanced model allows for dynamic configuration of environment-specific settings using environment variables.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ZdQ8xUVJ9Xk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ZdQ8xUVJ9Xk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/envvar/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Functionality by Role</title>
        <link>https://www.marius-wodtke.de/post/pattern/by-role/</link>
        <pubDate>Sun, 27 Apr 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/by-role/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/by-role/cover.jpg" alt="Featured image of post Patterns: Functionality by Role" /&gt;&lt;p&gt;Have you heard of the requirement &amp;ldquo;When X visits (a record of) Table Y, they should not see Z&amp;rdquo;, whereX is a role and Z is a button, control, section or tab? Although Dynamics works with privileges, stakeholders usually think in roles and therefore produce concepts which may only work with roles and not with privileges. And the other issue may be, that the request needs frontend scripting with JavaScript anyways and nitpicking about privileges vs roles is no value.&lt;br&gt;
If Z is a button, the discussion might be worth it! That is because there is an out of the box display rule (EntityPrivilegeRule) that can check for the presence of a privilege, that could skip a custom implementation fully. If there is no natural privilege, it may be an option to create an dummy entity for this purpose (but document this well!).&lt;/p&gt;
&lt;p&gt;I specifically write this article because I recently came across a situation where someone implemented this hush-hush with Copilot and it used the &amp;ldquo;old&amp;rdquo; style of querying role names. And although you will find the &amp;ldquo;new&amp;rdquo; style of implementing at probably half dozen other articles, there apparently need to be more so that chances rise that humans and AI find the better way of comparing roles.&lt;/p&gt;
&lt;p&gt;To have something concrete for our samples, lets consider this: &amp;ldquo;Only the european users have access to the the Power BI report of the (european) customer in the &amp;lsquo;Report&amp;rsquo; Tab on the Account form. We may roll out this report for other regions soon. However, the others should not see it for now to avoid confusion and support requests regarding the missing permissions error&amp;rdquo;.&lt;br&gt;
This can be solved with pure customizing by creating a copy of the form, adding the Tab, restricting it to a Role &amp;ldquo;Report User&amp;rdquo; and adding the form to the app. However, this is often unpopular with stakeholders and users when they are not used to switching forms. Therefore the scripting solution is preferred.&lt;/p&gt;
&lt;h2 id=&#34;basics-and-the-old-way&#34;&gt;Basics and the old way&lt;/h2&gt;
&lt;p&gt;Like any other record in Dataverse, Roles have Guids. However, the same role exists for every Business Unit in the environment! So if we have a Role &lt;em&gt;Report User&lt;/em&gt; and the Business Units &lt;em&gt;Root&lt;/em&gt;, &lt;em&gt;Europe&lt;/em&gt; and &lt;em&gt;Asia&lt;/em&gt;, &lt;em&gt;Report User&lt;/em&gt; will be present 3 times with different Guids. If you assign &lt;em&gt;Report User&lt;/em&gt; to &lt;em&gt;User1 (Europe)&lt;/em&gt; and &lt;em&gt;User2 (Asia)&lt;/em&gt; they will physically have different roles although conceptually they have &amp;ldquo;the same permissions&amp;rdquo;. The quotation marks are necessary here because we have this concept on Business Units, because of their different assignments the two users will have different permissions depending on the Owning Business Unit of records.&lt;/p&gt;
&lt;p&gt;Traditionally there was the property &lt;code&gt;Xrm.Utility.getGlobalContext().userSettings.securityRoles&lt;/code&gt; to get the roles of the current user. It returned a list of Guids. However, usually you want to check for a roles name, so it needed to be retrieved and code looked something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// DO NOT USE THIS CODE, BETTER ALTERNATIVES BELOW!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HasRole&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;userRoles&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getGlobalContext&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;userSettings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;securityRoles&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;userRoles&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleId&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;promise&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WebApi&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;retrieveRecord&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;role&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleId&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?$select=name&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error getting role name: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;promise&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Promise.&lt;span style=&#34;color:#a6e22e&#34;&gt;all&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Roles of the user: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;; checking against: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;includes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Problematic here also, it&amp;rsquo;s asynchronous, so it gets a bit more tricky in using because it is unknown how long it will take until the names are retrieved. Usually it will be below a second, but it could also be well above, resulting in poor UX.&lt;/p&gt;
&lt;h2 id=&#34;new-an-better-style&#34;&gt;New an better style&lt;/h2&gt;
&lt;p&gt;Microsoft extended the Client API with a &lt;code&gt;Xrm.Utility.getGlobalContext().userSettings.roles&lt;/code&gt; property some time ago, and it&amp;rsquo;s much better! Its output is a list of objects and each object has a &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; property. So there is no need anymore to fire a query for every role of the user, instead we can directly use it. Here is a sample output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;e7dd9bc6-d239-ea11-a813-000d3a35b14a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Report User&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;733ea175-0aeb-4442-a634-1972703f9750&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Sales Team&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The resulting code is much simpler:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HasRole&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;userRoles&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getGlobalContext&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;userSettings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;roles&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;userRoles&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Roles of the user: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;; checking against: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;includes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And if you really want it as a single line, here you go &lt;code&gt;var hasRole = Xrm.Utility.getGlobalContext().userSettings.roles.getAll().map(_ =&amp;gt; _.name).includes(roleNameToCheck);&lt;/code&gt;, but I thinks it&amp;rsquo;s too long and &lt;code&gt;getAll()&lt;/code&gt; is &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/collections&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;not officially documented&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next comes a TypeScript class with the same functionality.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Roles&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ReportUser&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Report User&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HasRole&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;userRoles&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getGlobalContext&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;userSettings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;roles&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;userRoles&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Roles of the user: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;; checking against: &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNameToCheck&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roleNames&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;includes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;roleName&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Usage:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// if (Roles.HasRole(Roles.ReportUser)) { ... }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this is synchronous, so we can show/hide with minimal delay, making for a better UX than the previous solution.&lt;/p&gt;
&lt;h2 id=&#34;d365ribbonhelpers&#34;&gt;D365RibbonHelpers&lt;/h2&gt;
&lt;p&gt;In the intro I already told you: Try to switch ribbon button rules to privileges. But it is virtually the only application where you could use the code from this article in isolation. And sometimes you can&amp;rsquo;t convince stakeholders to change their requirements, so maybe you need this for hiding/showing a button.&lt;/p&gt;
&lt;p&gt;So, we create a custom enable rule, use the file &lt;code&gt;mwo_/Roles.js&lt;/code&gt; and the function &lt;code&gt;HasRole&lt;/code&gt;. Then we add a string parameter with the name of the role we are looking for. Now the rule is associated to the command of the button and it should work!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/by-role/rule.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If you are wondering what is happening, the console log line might help you, here are outputs with and without the role of the sample:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/by-role/console1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/by-role/console2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The addition of the &lt;code&gt;roles&lt;/code&gt; property in &lt;code&gt;Xrm.Utility.getGlobalContext().userSettings&lt;/code&gt; was very welcome and makes checking roles in frontend code simpler and more efficient. You can use the code samples in the article for your own implementations or leverage &lt;code&gt;mwo_/Roles.js&lt;/code&gt;:&lt;code&gt;HasRole(&amp;quot;myRole&amp;quot;)&lt;/code&gt; from the D365RibbonHelpers solution.&lt;/p&gt;
&lt;p&gt;If you are dealing with a button, you may be better of by discussing whether the requirement can also be fulfilled with checking the presence of a privilege as this is supported by standard display rules. If your requirement is already formulated for a privilege and is not regarding a button, you may want to take a closer look at the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/xrm-utility/getglobalcontext/usersettings#getsecurityroleprivilegesinfo-method&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;getSecurityRolePrivilegesInfo&lt;/a&gt; method.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Create and Customize a Card Gallery in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/cardgallery/</link>
        <pubDate>Thu, 24 Apr 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/cardgallery/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/cardgallery/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Create and Customize a Card Gallery in Power Pages?" /&gt;&lt;p&gt;Welcome to the Episode 24th of the Power Pages Quick Tips Series. In this episode, we are going to talk about How to Create and Customize a Card Gallery in Power Pages?&lt;/p&gt;
&lt;p&gt;A card gallery in Power Pages is a data control that displays information from a table and view in a card format. It allows makers to present data visually, making it easier for users to browse and interact with the information.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/yENJeXxexks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/yENJeXxexks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/cardgallery/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] setVisible &amp; getVisible in Power Pages with ClientAPI</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-setvisible/</link>
        <pubDate>Sun, 13 Apr 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-setvisible/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-setVisible/cover.jpg" alt="Featured image of post [VIDEO] setVisible &amp; getVisible in Power Pages with ClientAPI" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will extend with the common methods of setVisible and getVisible.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5xqsCy1k5NQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5xqsCy1k5NQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/ClientAPI-setVisible/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My first shot at Managed Identities for Plugin Assemblies</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/managed-identity/</link>
        <pubDate>Sun, 06 Apr 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/managed-identity/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/cover.jpg" alt="Featured image of post My first shot at Managed Identities for Plugin Assemblies" /&gt;&lt;p&gt;For over a year I have been following the feature &amp;ldquo;Use managed identities for Dataverse plugins&amp;rdquo; and after multiple delays it should finally become generally available and I had some time this weekend to implement a first attempt to test it out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/Feature.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;general-idea&#34;&gt;General idea&lt;/h2&gt;
&lt;p&gt;You create an App Registration/Service Principle in Azure and an identity in Dataverse to represent it.
We connect this Dataverse identity with the Plugin Assembly where we would like to use it. &lt;em&gt;In a classical Username/Password situation, this connection would determine the Username used&lt;/em&gt;.
And then we sign the Plugin Assembly with a Certificate, &lt;em&gt;this would be the Password&lt;/em&gt;. Finally the &lt;code&gt;IServiceProvider&lt;/code&gt; can resolve an &lt;code&gt;IManagedIdentityService&lt;/code&gt; that will create JWTs for accessing resources.&lt;/p&gt;
&lt;p&gt;And this is super exiting! While the tokens will only work with services that have OAuth2 authentication and even then might still have problems with cross-tenant situation (or a million other reasons), this opens a significant bootstrap: Azure KeyVault.&lt;br&gt;
In Dataverse we don&amp;rsquo;t have a really secure storage for secrets, I argue that even things like the Secure Configuration of Plugins is not as secure as the name suggests.
But even with a legacy ERP system only supporting Username and Password I can now store these in a KeyVault, enable the App Registration to read them and then retrieve the values in a Plugin to integrate that system without exposing those credentials.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-platform/admin/set-up-managed-identity&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Microsoft already has documentation on this&lt;/a&gt; and I suggest also considering it, because such a new feature might receive some changes. In this article we will however have more pictures and some tips for problems not explained in the official version.&lt;/p&gt;
&lt;h2 id=&#34;the-app-registration&#34;&gt;The App Registration&lt;/h2&gt;
&lt;p&gt;In the Azure Portal, navigate to EntraId and select Add -&amp;gt; App Registration at the top. Then enter a better name than I did (your company probably has a convention for AppReg naming) and hit register.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/AppRegNew.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;From the Overwiew we will need the AppId and TenantId in the next section, you can copy them now or come back when you need them. We will jump to &lt;em&gt;Certificate &amp;amp; Secrets&lt;/em&gt; next.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/AppRegOverview.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Here we need a new credential in Federated Credentials. This means someone else (the PowerPlatform) will validate the credentials and can request a token for this AppRegistration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/AppRegNewCredential.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now we need a certificate, this can be any certificate. MS suggests against self-signed certificates for production use cases, however I don&amp;rsquo;t know how this makes it more secure, since only the thumbprint is checked. If you have an explanation, I would be glad if you comment it (there is a switch to enable comments on the bottom left).
For creating a self-signed certificate I usually ask Google for &amp;ldquo;windows create self signed certificate&amp;rdquo; and then take an article leveraging &lt;em&gt;OpenSSL&lt;/em&gt; since I can&amp;rsquo;t remember the commands by heart. This reminds me, that I should leverage a CoPilot for this question in the future, saving a minute. Whatever way you get it: You need a pfx certificate with the private key included, also generating the public part as cer/crt file is helpful.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: The next section on the Subject Identifier is out of date! With the move to GA, this got changed, check &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-platform/admin/set-up-managed-identity#subject-identifier&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official docs&lt;/a&gt; for more info!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For the credential values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The name may have any value, however it cannot be changed later. I think using the certificate name(/subject) and thumbprint might be a good name, because it both reflects situations like multiple developers having different certificates as well as &amp;ldquo;the same&amp;rdquo; certificate (by subject) having 2 credentials because one certificate is nearing expiration.&lt;/li&gt;
&lt;li&gt;For the issuer you want to get Environment ID from the Power Platform Admin Center. It is found in the Environment Details. Then we want to remove all hyphens and split off the last two character/numbers by a dot. So &amp;ldquo;7efcc31a-c410-e20e-9b95-dd448cedf653&amp;rdquo; becomes &amp;ldquo;7efcc31ac410e20e9b95dd448cedf6.53&amp;rdquo;. Now we embed this to the sts-url which is &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://7efcc31ac410e20e9b95dd448cedf6.53.environment.api.powerplatform.com/sts%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://7efcc31ac410e20e9b95dd448cedf6.53.environment.api.powerplatform.com/sts&#34;&lt;/a&gt; in this sample.&lt;/li&gt;
&lt;li&gt;For the value we will also need the thumbprint of the certificate. You can get it by inspecting the cer/crt files details. &lt;strong&gt;Important: Value is case sensitive, in my case the thumbprint needed to be in upper-case!&lt;/strong&gt; And for this value we also need the environment id in its unmodified version, the final value looks like &amp;ldquo;component:pluginassembly,thumbprint:C67255AD07AB5E6A0A3F1979DAC2F928A580E6F4,environment:7efcc31a-c410-e20e-9b95-dd448cedf653&amp;rdquo; where you of course need to replace the values in thumbprint and environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The audience is also case-sensitive. In my testing it needed to be all lower-case!&lt;/strong&gt; So the default value &amp;ldquo;api://AzureADTokenExchange&amp;rdquo; becomes &amp;ldquo;api://azureadtokenexchange&amp;rdquo;. The error message in this regard was quite good as it specified that the requested audience (api://azureadtokenexchange) was not matched with EntraId.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/AppRegEditCredential.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/EnvironmentId.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/Thumbprint.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Windows list the thumbprint as lower-case, but in EntraId it needs to be upper-case&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;plugin-code&#34;&gt;Plugin Code&lt;/h2&gt;
&lt;p&gt;Since my goal was to generate a token at all and I did not have a concrete project use case, you will find a very minimal code sample. But this is very nice after the extensive setup, we can get away with 2-3 lines of code to get a valid token.&lt;/p&gt;
&lt;p&gt;First we use the &lt;code&gt;IServiceProvider&lt;/code&gt; to resolve the &lt;code&gt;IManagedIdentityService&lt;/code&gt;.&lt;br&gt;
Then we define a scope with a list of strings. With newer OAuth versions they scrapped the resource, which would be &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://vault.azure.net/%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://vault.azure.net/&#34;&lt;/a&gt;, with the scope further detailing the actions you want to take within the resource. If you don&amp;rsquo;t have that you can usually just append &amp;ldquo;.default&amp;rdquo;, however, there might be resources preventing that, requiring you to specify your intents and this is exactly the situation where you would use the list part of this parameter.
And lastly we can use the &lt;code&gt;AcquireToken&lt;/code&gt; with the given scope(s) to get a token that we would then use to access the resource with something like a &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-C#&#34; data-lang=&#34;C#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Class1&lt;/span&gt; : IPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; Execute(IServiceProvider serviceProvider)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; trace = serviceProvider.Get&amp;lt;ITracingService&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        trace.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Acquiring Token&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; managedIdentity = serviceProvider.Get&amp;lt;IManagedIdentityService&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; scope = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://vault.azure.net/.default&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; token = managedIdentity.AcquireToken(scope);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#if&lt;/span&gt; DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        trace.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Token: {token}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;AFTER building and BEFORE uploading the assembly we need to sign it with a certificate. The following command assumes that we have the Developer Powershell in Visual Studio in the bin/Debug (or bin/Release) folder, with the certificate being present in the same folder: &lt;code&gt;signtool sign /f myCertificate.pfx /p myPW /fd SHA256 myProject.dll&lt;/code&gt;. Here everything with the &lt;em&gt;my&lt;/em&gt; prefix needs your own values.&lt;/p&gt;
&lt;p&gt;After that, everything runs like a normal Plugin Assembly.&lt;/p&gt;
&lt;h2 id=&#34;managed-identity-association&#34;&gt;Managed Identity Association&lt;/h2&gt;
&lt;p&gt;We have an App Registration in Azure and the signed Plugin Assembly now. Next we need to tell Dataverse how it should connect the two.
First we need to create a record in the managedidentity table. For this we will need the AppId and TenantId from the AppRegs Overview page. I&amp;rsquo;ve used Postman to create the record, but there also seems to be a tool in the XrmToolbox (Plugin Identity Manager).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-http&#34; data-lang=&#34;http&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;POST https://mariuswodtke-mvp.crm4.dynamics.com/api/data/v9.2/managedidentities
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;    &amp;#34;applicationid&amp;#34;:&amp;#34;a56f4eac-dab5-4920-b005-e84146adaadc&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;    &amp;#34;managedidentityid&amp;#34;:&amp;#34;a56f4eac-dab5-4920-b005-e84146adaadc&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;    &amp;#34;credentialsource&amp;#34;:2,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;    &amp;#34;subjectscope&amp;#34;:1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;    &amp;#34;tenantid&amp;#34;:&amp;#34;738016bf-2f59-46ca-9660-357d1de3b865&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve set the managedidentityid to the AppId here as well, MS generally does not like it when you assign guids not generated by the platform, but I hope there will never be that many Managed Identity records that it would lead to partitioning inefficiencies.&lt;/p&gt;
&lt;p&gt;Next we associate the Managed Identity record with the plugin Assembly by setting the respective lookup. We will need the managedidentityid and the pluginassemblyid for this, the latter I grabbed from the details section of the Plugin Registration Tool.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-http&#34; data-lang=&#34;http&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;PATCH https://mariuswodtke-mvp.crm4.dynamics.com/api/data/v9.2/pluginassemblies(478f1797-e33c-4d42-933f-c08107c24614)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;    &amp;#34;managedidentityid@odata.bind&amp;#34;: &amp;#34;/managedidentities(a56f4eac-dab5-4920-b005-e84146adaadc)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/Assembly.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it, we created all necessary credentials and joined them in Dataverse, now we need to run the plugin and check the results.&lt;/p&gt;
&lt;h2 id=&#34;resulting-token&#34;&gt;Resulting Token&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/managed-identity/Result.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;As you saw in the code sample above, I printed the token to the tracelogs for debug builds. For production this is of course not ideal, but for the initial tests it helps in identifying issues. Websites like &lt;a class=&#34;link&#34; href=&#34;https://jwt.ms/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;jwt.ms&lt;/a&gt; can decode the token, to check values like the audience and appId to clarify 401 errors received from target resources. So here is a sample of a (now expired) token I was able to generate.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;typ&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;JWT&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;alg&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;RS256&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x5t&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;JDNa_4i4r7FgigL3sHIlI3xV-IU&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kid&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;JDNa_4i4r7FgigL3sHIlI3xV-IU&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}.{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aud&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cfa8b339-82a2-471a-a3c9-0fc0be7a4093&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iss&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://sts.windows.net/738016bf-2f59-46ca-9660-357d1de3b865/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iat&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1742668105&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nbf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1742668105&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;exp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1742672005&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aio&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;k7RgYPDft6dY2dl8+ik3puM7L7+sWrrUUrOpWHado3CwUbu83D8A&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appid&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a56f4eac-dab5-4920-b005-e84146adaadc&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appidacr&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;idp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://sts.windows.net/738016bf-2f59-46ca-9660-357d1de3b865/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;idtyp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;app&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;oid&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8911b4f4-2b4e-46ce-b854-ebeebffe7bc6&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rh&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.AEsAvxaAc1kvykaWYDV9HeO4ZTmzqM-ighpHo8kPwL56QJNGAQBLAA.&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sub&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8911b4f4-2b4e-46ce-b854-ebeebffe7bc6&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tid&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;738016bf-2f59-46ca-9660-357d1de3b865&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;uti&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;43CV3HaJykiHcPnF8XDWAA&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ver&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xms_ftd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Kflhs7-A9NMBFFQe6G1y0PBjOPke2Jzw3uk-TfA9yOw&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xms_idrel&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;7 16&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xms_rd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.42LlYBJi9AYA&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}.[&lt;span style=&#34;color:#a6e22e&#34;&gt;Signature&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Did you notice the Guid in the audience as well? I remember getting back a named audience for KeyVault, but verifying with Postman, I also got the guid. Likely this is the id of the KeyVault Service.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Finally we have the bootstrap for secrets in Plugins! We got that a while back for Power Automate with the secrets option for Environment Variables, but Low-Code also has it&amp;rsquo;s limits and whenever we need High-Code, we should have an option now.&lt;/p&gt;
&lt;p&gt;The App Registration approach already offers many options for integrating with Azure Resources and APIs that leverage OAuth2. For everything else Azure KeyVault can store the needed credentials and we can securely access them with the Managed Identity.&lt;/p&gt;
&lt;p&gt;After a somewhat cumbersome setup (which might be improved through community tools like the &amp;ldquo;Plugin Identity Manager&amp;rdquo;), usage in code is remarkably easy and performance was quite good as well. With a warmed up environment the executions took about 100ms, I expected worse.&lt;/p&gt;
&lt;p&gt;To make it happen you will need a certificate with a private key (.pfx), an App Registration in EntraId with  Federated Credentials pointing to the certificate and Environment. Then you can sign the Plugin Assembly with the certificate and connect it with a record in the managedidentity table, holding a reference to the App Registration. Once all this is set up, you can create a token for an Azure resource like Key Vault with only 2-3 lines of code.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] getValue &amp; setValue in Power Pages with ClientAPI</title>
        <link>https://www.marius-wodtke.de/post/power-pages/clientapi-getvalue/</link>
        <pubDate>Sun, 16 Mar 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/clientapi-getvalue/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi-getvalue/cover.jpg" alt="Featured image of post [VIDEO] getValue &amp; setValue in Power Pages with ClientAPI" /&gt;&lt;p&gt;To achieve Power Pages code that is portable from your Model Driven App forms to Power Pages it would be nice to have the MDA Client API available in Power Pages. But sadly it is not there yet, but we will try to make it happen!&lt;/p&gt;
&lt;p&gt;In this video we will start with the common methods of getValue and setValue.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesClientAPI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/WsAlsFYZZ_Q&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/WsAlsFYZZ_Q&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/clientapi-getvalue/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Opening a &#39;dialog&#39; for a record</title>
        <link>https://www.marius-wodtke.de/post/pattern/ribbon-dialog/</link>
        <pubDate>Sun, 02 Mar 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/ribbon-dialog/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/cover.jpg" alt="Featured image of post Patterns: Opening a &#39;dialog&#39; for a record" /&gt;&lt;p&gt;In the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/pattern/ribbon-flow/&#34; &gt;&amp;ldquo;trigger flow from ribbon&amp;rdquo; pattern&lt;/a&gt; we already took a look at triggering logic from the ribbon. But what if we require additional input from the user?&lt;/p&gt;
&lt;p&gt;For this article consider the following requirement as a sample when you might need this pattern: &amp;ldquo;We need to copy records. When copying, we want to decide how many copies we need and the system should create the according amount.&amp;rdquo; The discussion in this scenario showed that the maximum number of copies would be 5, so we could still handle this with a flyout or splitbutton that vary input. But of course we can easily imagine inputs that break the &amp;ldquo;half dozen variants&amp;rdquo; barrier where separate buttons start to fail.&lt;/p&gt;
&lt;p&gt;This pattern relies on the usage of JavaScript, but you can also download the solution from &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Ribbon Helpers repository&lt;/a&gt; to skip programming. In that case you only need to work with either Ribbon Workbench (XrmToolbox) or&lt;/p&gt;
&lt;h2 id=&#34;custom-pages&#34;&gt;Custom Pages&lt;/h2&gt;
&lt;p&gt;Back in the days there were the dialogs for this, but they are long deprecated. But we&amp;rsquo;ve gotten Custom Pages instead, this is essentially a Canvas App for a single screen. And important for the use case: &lt;code&gt;Xrm.Navigation.navigateTo&lt;/code&gt; allows us to pass an &lt;code&gt;entityName&lt;/code&gt; and &lt;code&gt;recordId&lt;/code&gt; to the page and there we can read these values with &lt;code&gt;Param(&amp;quot;entityName&amp;quot;)&lt;/code&gt; and &lt;code&gt;Param(&amp;quot;recordId&amp;quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, we start with building a Custom Page, I&amp;rsquo;ve put a Label and Input for the amount of copies in there and a &amp;ldquo;Submit&amp;rdquo; button, which will trigger a flow with the id (or ids, more on that later) and amount of copies to then read the record and create the records in a loop.&lt;/p&gt;
&lt;p&gt;Additionally I&amp;rsquo;ve put up two labels with the entityName and recordId, but this is mainly for debugging purposes, I would advise to hide or even delete those labels before shipping to production. Unless of course your users like this kind of debug information, but experience says that almost no one values it.&lt;/p&gt;
&lt;h2 id=&#34;code&#34;&gt;Code&lt;/h2&gt;
&lt;p&gt;This is fortunately very short, we only need to call &lt;code&gt;Xrm.Navigation.navigateTo(input,options)&lt;/code&gt; where input and options are relatively simple objects. For options in theory it would be enough to specify wether the page should be opened fullscreen (&lt;code&gt;target: 1&lt;/code&gt;) or as overlay (&lt;code&gt;target: 2&lt;/code&gt;). For input you need the &lt;code&gt;pageType: &amp;quot;custom&amp;quot;&lt;/code&gt; and then the name contains the logical name of the custom page, so for example &lt;code&gt;name: &amp;quot;mwo_dialog_da9e9&amp;quot;&lt;/code&gt;. And then, for the scenario in this pattern, we also want to pass &lt;code&gt;entityName&lt;/code&gt; and &lt;code&gt;recordId&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Below you will find the full function that is part of &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Ribbon Helpers repository&lt;/a&gt;. It also allows to control the size on screen, where you can pass width and height as &amp;ldquo;px&amp;rdquo; or &amp;ldquo;%&amp;rdquo; (unit). In the next section we will take a closer look on how to leverage this function from the Ribbon Workbench.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OpenDialog&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pageName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;}&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toLowerCase&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArray&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;}&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toLowerCase&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;pageType&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;custom&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pageName&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;navigateTo&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Dialog opened&amp;#34;&lt;/span&gt;), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;single-record-dialog&#34;&gt;Single Record Dialog&lt;/h2&gt;
&lt;p&gt;Now we will start with operating the Ribbon workbench. I will spare you the buttons and so on. The easy sample uses the CRM Parameters &amp;ldquo;SelectedEntityTypeName&amp;rdquo; and &amp;ldquo;FirstSelectedItemId&amp;rdquo; to get logicalname and id. Additionally a string parameter specifies the logical name of the custom page, that&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/singleCommand.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;For forms you can also use “PrimaryEntityTypeName” and “FirstPrimaryItemId”&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For the button to be shown at the right time you should also add a selection count rule, this ensures it is only shown when exactly one record is selected, everything else might lead to unexpected behaviors where the dialog is opened without a record or only with the first one, although multiple were selected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/singleEnable.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And the result then looks like&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/single.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Obviously the default 80% of the screen is way too much for this simple dialog, we will explore the sizing options a bit in the next section, but essentially this is a trial and error for every page for me.&lt;/p&gt;
&lt;h2 id=&#34;multi-record-dialog&#34;&gt;Multi Record Dialog&lt;/h2&gt;
&lt;p&gt;First of all &lt;strong&gt;consider this unsupported&lt;/strong&gt;. So recordId (and also entityName) does not have a reasonable character limit, this means we can pass more than one record. Technically &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/xrm-navigation/navigateto#custom-page&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MS does not specify a limit&lt;/a&gt; but their language talks about a &amp;ldquo;table record&amp;rdquo;. Of course this does not hinder us from passing more, but we can&amp;rsquo;t be sure this will be considered as a bug and being fixed without a prior notice.&lt;/p&gt;
&lt;p&gt;But well, if you are brave enough and the function is not that business critical, we can can switch &amp;ldquo;FirstSelectedItemId&amp;rdquo; to &amp;ldquo;SelectedControlSelectedItemIds&amp;rdquo; and adapt the selection count rule to something reasonable, lets say 50 records.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/multiCommand.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;You can also see the usage of the sizing parameters here, which limit the size of the dialog to 500x500 pixels. This is not optimal as well, we would require more width and less height, but for showcasing the feature, it will have to do.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/multi.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;modern-commandingpowerfx&#34;&gt;Modern Commanding/PowerFx&lt;/h2&gt;
&lt;p&gt;Theoretically, CustomPages can also be called with PowerFx from the Modern Commanding ribbon. However, even &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/commanding-use-powerfx#navigate&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official documentation for Navigate&lt;/a&gt; prominently links to the JS functions, since you cannot pass the recordId and entityName parameters.&lt;/p&gt;
&lt;p&gt;But we can use the same function with the same parameters that we used previously in the Ribbon Workbench. The sample below uses the multi record parameter &amp;ldquo;SelectedControlSelectedItemIds&amp;rdquo;, remember that this is kind of unsupported.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/powerFxCall.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;We could also fix this by only showing the button when only one record is selected, visibility can be controlled with the function &lt;code&gt;CountRows(Self.Selected.AllItems)&lt;/code&gt;, compared to the sample below you could specify it to be &lt;code&gt;= 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/powerFxVisible.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;passing-no-record&#34;&gt;Passing no record&lt;/h2&gt;
&lt;p&gt;Like I mentioned with PowerFx, you can open a custom page without passing a record, this is also possible with the OpenDialog function pictures above. However, using JS extensions for this use-case seems silly to me; instead, you can simply add the page to the navigation of the Model-Driven App if you do not need a record association. But, to be complete, this is how it would work:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/noneCommand.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-dialog/none.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;With custom pages and the possibility to pass in records we got a much better version of dialogs! You can either implement on your own with &lt;code&gt;Xrm.Navigation.navigateTo&lt;/code&gt; or install &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Ribbon Helpers Solution&lt;/a&gt; and then use &lt;code&gt;OpenDialog&lt;/code&gt; with Ribbon Workbench or Modern Commanding to open a prepared custom page with the context of a record. The custom page then uses &lt;code&gt;Param(&amp;quot;entityName&amp;quot;)&lt;/code&gt; and &lt;code&gt;Param(&amp;quot;recordId&amp;quot;)&lt;/code&gt; to read these parameters.&lt;/p&gt;
&lt;p&gt;Because there is no reasonable character limit on these parameters, also multiple records can be passed as a comma separated list to the page. However, this should be considered unsupported and therefore should be avoided for business critical functionality. Also keep in mind that in this case, the custom page needs to be aware of this behavior and must split the list of ids again.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Enable multiple-language in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/language/</link>
        <pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/language/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/language/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Enable multiple-language in Power Pages?" /&gt;&lt;p&gt;The &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/snippets/&#34; &gt;content snippets&lt;/a&gt; do not only allow changing texts on standard pages, they also allow us to translate pages. In this video we are exploring how we can enable a new language in Power Pages.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/WYKe_w1q29M&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/WYKe_w1q29M&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/language/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Leverage Sharepoint in Model Driven Apps?</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-gbbp/</link>
        <pubDate>Wed, 19 Feb 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-gbbp/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-gbbp/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Leverage Sharepoint in Model Driven Apps?" /&gt;&lt;p&gt;In the 2024 edition of the Global Power Platform Bootcamp I showed the capabilities of the Sharepoint integration in Model Driven Apps. The recording is now up for everyone to follow along!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/AuYjwqb2isY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/AuYjwqb2isY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-gbbp/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Configure Power Automate cloud flows in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/flow/</link>
        <pubDate>Sat, 15 Feb 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/flow/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/flow/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Configure Power Automate cloud flows in Power Pages?" /&gt;&lt;p&gt;Need some server logic for your Power Page? Power Automate is here to help! With this tip you will easily integrate your Power Pages Website with a Power Automate Flow.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/UBltc6haCwQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/UBltc6haCwQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/flow/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: PrettifyMyWebApi for Microsoft Dataverse</title>
        <link>https://www.marius-wodtke.de/post/ppl/2025-02/marius/</link>
        <pubDate>Fri, 14 Feb 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2025-02/marius/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-02/marius/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: PrettifyMyWebApi for Microsoft Dataverse" /&gt;&lt;p&gt;This time in the Power Platform Learners call I pitched for the Chrome extension PrettifyMyWebApi, a useful tool for Dataverse developers and admins.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/8-m01p2KZOY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/8-m01p2KZOY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2025-02/marius/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Pages Actions: The parser is wrong!</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api-parser/</link>
        <pubDate>Sun, 09 Feb 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api-parser/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-parser/cover.jpg" alt="Featured image of post Power Pages Actions: The parser is wrong!" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/custom-api-parameters/&#34; &gt;a recent article&lt;/a&gt; I detailed the possible parameters with Power Pages Actions and how the parser will recognize them.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;However, Harish Karyattu reached out and made me aware of a problem he was facing with this. He had a string input for his Custom API and the parser misinterpreted the inputs for a Guid. The input in question here stemmed from a payment gateway, so changing the input was definitely not an option.&lt;/p&gt;
&lt;p&gt;Simple fix you would say: &amp;ldquo;Just change the parameter to Guid and you are good!&amp;rdquo; But a) that might have lead to a conflict if you are using the same API from a Dataverse interface where it is stored as string and b), even more challenging, what if it just fits the format but is technically not Guid? You could run into a situation where some inputs are recognized as string because they are unparsable, while others are forwarded as a Guid.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;d4918dfd-4c34-4497-a69b-3d8844ae462c&amp;rdquo; is a Guid,&lt;br&gt;
&amp;ldquo;d4918d&lt;strong&gt;G&lt;/strong&gt;d-4c34-4497-a69b-3d8&lt;strong&gt;Z&lt;/strong&gt;44ae462c&amp;rdquo; is not (notice the G and Z)!&lt;/p&gt;
&lt;p&gt;An obvious fix would be for Power Pages Actions to read the type from metadata, however, this would definitely be a performance hit, rendering the solution less useful.&lt;/p&gt;
&lt;h2 id=&#34;the-fix&#34;&gt;The fix&lt;/h2&gt;
&lt;p&gt;So we need something to tell the parser a specific type. The version 1.3.0.0 of Power Pages Actions therefore introduces an option to specify the type of simple parameters with an @. So for the problem above we would use @string like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Input@string&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00000000-0000-0000-0000-000000000001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will make sure that that &amp;ldquo;Input&amp;rdquo; is passed on to the Custom API as a string, even though it technically fits the Guid scheme.
Remember that you will need to change the style of property definition to the array accessor&lt;br&gt;
&lt;code&gt;{Input: &amp;quot;00000000-0000-0000-0000-000000000001&amp;quot;}&lt;/code&gt; -&amp;gt;&lt;br&gt;
&lt;code&gt;{[&amp;quot;Input@string&amp;quot;]: &amp;quot;00000000-0000-0000-0000-000000000001&amp;quot;}&lt;/code&gt;!&lt;br&gt;
Of course the syntax of this gets more verbose, but remember that you will only need to use this option if your inputs might match another type.&lt;br&gt;
I could also imagine this being the case when you have user inputs in a string field that might match number formats.&lt;/p&gt;
&lt;h2 id=&#34;other-modifiers&#34;&gt;Other modifiers&lt;/h2&gt;
&lt;p&gt;The full list of modifies includes all simple parameters but not the complex objects like Entity.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@string&lt;/li&gt;
&lt;li&gt;@int&lt;/li&gt;
&lt;li&gt;@decimal&lt;/li&gt;
&lt;li&gt;@float&lt;/li&gt;
&lt;li&gt;@bool&lt;/li&gt;
&lt;li&gt;@guid&lt;/li&gt;
&lt;li&gt;@datetime&lt;/li&gt;
&lt;li&gt;@picklist&lt;/li&gt;
&lt;li&gt;@money&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;@picklist and @money deserve special attention as they might be a less verbose option for your code, because they convert complex parameters to simple ones.&lt;/p&gt;
&lt;p&gt;Optionset:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;PicklistParam&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;OptionSetValue&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// can also be written as
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PicklistParam@picklist&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Money:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;MoneyParam&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Money&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// can also be written as
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MoneyParam@money&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;First of all, thanks Harish for mentioning this! Feedback is important and you should not be shy to message someone or open an issue on Github for an open source project. While OS solutions might only have few maintainers with a work and private life, obstructing bigger changes, they also do not have to run through big organizations with a Bug/Feature.&lt;/p&gt;
&lt;p&gt;With the parser directives it is now possible to fix issues with Power Pages Actions recognizing parameters incorrectly on your own with minimal adaptions. Simply add &lt;code&gt;@&amp;lt;type&amp;gt;&lt;/code&gt; to your input property where type is a simple type like string or decimal.&lt;br&gt;
This is only needed in case mismatches are happening or are expected, e.g. due to uncontrollable input.&lt;br&gt;
To finish this article, here is a sample how such a mismatch and its correction will look like in the network tab.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-parser/cover.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GoLive Plans with Azure DevOps Wiki and MermaidJS</title>
        <link>https://www.marius-wodtke.de/post/devops/golive-plan/</link>
        <pubDate>Sun, 02 Feb 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/devops/golive-plan/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/devops/golive-plan/cover.jpg" alt="Featured image of post GoLive Plans with Azure DevOps Wiki and MermaidJS" /&gt;&lt;p&gt;There is more to a project than just customizing/coding correctly, we all know that. But as I&amp;rsquo;m a technician, I always look out for things in Azure DevOps that may help me succeed. And one of those things is &lt;a class=&#34;link&#34; href=&#34;https://mermaid.js.org/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Mermaid diagrams&lt;/a&gt;, which you can use in Azure DevOps Wikis by enclosing them in a code block like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;::: mermaid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;diagram type&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;diagram content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:::
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;They are super useful to for example include flow diagrams in documentation. This has the great benefit that the source of the diagram is directly in the article and can be changed in the article should some details change.&lt;br&gt;
Of course a real tool like MS Visio is more powerful and could export an image to be embedded, but you would also have to link to the SharePoint (or similar) location where the diagram source is stored and you expect everyone to have access there and the necessary tool and so on. My experience says that it is likely people will then not adapt the diagrams and just leave them as they were, over time leading to incorrect documentation.&lt;/p&gt;
&lt;p&gt;But I did not want to use this article to argue for the general use of mermaid with Azure DevOps, instead I wanted to discuss a more creative way of using them: A gantt chart can help us to visualize and track the progress towards a GoLive.&lt;br&gt;
Of course there are actual tools for that like MS Project, but who has access to those? Everyone who has a stake, or just the project leads?
I my projects the Wiki is much more accessible and therefore a good place to collaborate and make progress visible.&lt;/p&gt;
&lt;p&gt;A gantt chart works like this&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;gantt
    dateFormat YYYY-MM-DD
    excludes weekends

    Sprint 1: s1, 2025-02-03, 10d
    Sprint 2: s2, after s1, 10d
    Sprint 3: s3, after s2, 10d
&lt;/pre&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;::: mermaid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gantt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dateFormat YYYY-MM-DD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    excludes weekends
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprint 1: s1, 2025-02-03, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprint 2: s2, after s1, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprint 3: s3, after s2, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:::
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So it nicely renders out blocks for the events and obeys the given order (&amp;ldquo;after s1&amp;rdquo;). We first give a display name, then a double colon, an id, then the date or an order (which you should definitely prefer!) and a duration.&lt;br&gt;
It will also render a marker for the current date, I will provide that as a picture as it will vanish once we are outside of the date period in the diagrams.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/devops/golive-plan/DateMarker.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The next thing we are going to introduce are states, these are before the id.&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;gantt
    dateFormat YYYY-MM-DD
    excludes weekends

    Sprint 1: done, s1, 2025-02-03, 10d
    Feature A complete: done, milestone , fa, after s1, 1d
    Sprint 2: active, s2, after s1, 10d
    Feature B: crit, fb, after s1, 6d
    Sprint 3: s3, after s2, 10d
    Sprints complete: milestone, sc, after s3, 1d
&lt;/pre&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;::: mermaid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gantt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dateFormat YYYY-MM-DD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    excludes weekends
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprint 1: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, s1, 2025-02-03, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Feature A complete: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, milestone, fa, after s1, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprint 2: active, s2, after s1, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Feature B: crit, fb, after s1, 6d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprint 3: s3, after s2, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sprints complete: milestone, sc, after s3, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:::
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we can mark active, completed and tasks in danger. Milestones receive a special symbol and can also be marked completed.&lt;/p&gt;
&lt;p&gt;One more concept are sections, these help to differentiate parts of the project. Usually they are &amp;ldquo;independent&amp;rdquo;, like a production setup is not directly tied to User Acceptance Testing (UAT), of course if UAT fails, it will have effects on the production timeline, but the setup is likely to have an almost independent timeline.&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;gantt
    dateFormat YYYY-MM-DD
    excludes weekends

    section Dev
        Sprint 1: s1, 2025-02-03, 10d
        Sprint 2: s2, after s1, 10d
        Sprint 3: s3, after s2, 10d
    
    section UAT
        Create Training Material (2025-03-16): ctm, 2025-02-20, 20d
        Create UAT Environment: cue, after s2, 1d
        Setup Users on UAT Env: sue, after cue, 2d
        Deploy to UAT Env: due, after s3 cue, 2d
        Execute UAT: uat, after due, 3d
        Structure Feedback: feedback, after uat, 3d
        Fix Bugs: bugs, after feedback, 7d

    section Prod
        GoLive Decision - Bernd: milestone, gld, after feedback, 1d
        Create Production Environment: cpe, after gld, 1d
        Setup Users on Prod Env: spe, after cpe, 3d
        Deploy to Prod Env: dpe, after cpe bugs, 2d
&lt;/pre&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;::: mermaid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gantt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dateFormat YYYY-MM-DD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    excludes weekends
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    section Dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 1: s1, 2025-02-03, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 2: s2, after s1, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 3: s3, after s2, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    section UAT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Create Training Material &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;2025-03-16&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: ctm, 2025-02-20, 20d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Create UAT Environment: cue, after s2, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Setup Users on UAT Env: sue, after cue, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Deploy to UAT Env: due, after s3 cue, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Execute UAT: uat, after due, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Structure Feedback: feedback, after uat, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Fix Bugs: bugs, after feedback, 7d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    section Prod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        GoLive Decision - Bernd: milestone, gld, after feedback, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Create Production Environment: cpe, after gld, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Setup Users on Prod Env: spe, after cpe, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Deploy to Prod Env: dpe, after cpe bugs, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:::
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, we now have different lanes, where some lanes start before others are finished. Someone from the business might start the user documentation while developers are still developing and the administrator is free to setup a new environment while others are still fixing stuff. Also notice here that some tasks have multiple dependencies, the last one counts!&lt;/p&gt;
&lt;p&gt;For all possible elements, check &lt;a class=&#34;link&#34; href=&#34;https://mermaid.js.org/syntax/gantt.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now towards some &amp;ldquo;best practices&amp;rdquo;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Give yourself some time, of course a deployment only takes a day, but assigning it 2d produces buffer. Think about these ranges as latest end. And if the predecessors are done, a task that has not reached its earliest start may be completed already!&lt;/li&gt;
&lt;li&gt;If you know who will do a task, write it down, e.g. &amp;ldquo;GoLive Decision - Bernd&amp;rdquo;&lt;/li&gt;
&lt;li&gt;If you need to move a task, write done the original date, e.g. &amp;ldquo;Create Training Material (2025-03-16)&amp;rdquo; where the duration was extended beyond that planned end date.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t number IDs. Ok, I&amp;rsquo;ve used it here for the sprints, but for everything else, it makes it harder to insert new tasks to the middle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And now we just need to encourage everyone to set their tasks active and done, maybe also crit if they want to raise something in the synchronization meetings.&lt;/p&gt;
&lt;p&gt;And here is a bigger table as a final sample, have fun creating your own GoLive plan with Azure DevOps Wikis and Mermaid diagrams.&lt;/p&gt;
&lt;p&gt;BTW, I think this looks better in light mode than in dark!&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;gantt
    title GoLive Timeline
    dateFormat YYYY-MM-DD
    excludes weekends

    section Dev
        Sprint 1: done, s1, 2025-02-03, 10d
        Sprint 2: done, s2, after s1, 10d
        Sprint 3: done, s3, after s2, 10d
        Sprint 4: done, s4, after s3, 10d
        Sprint 5: done, s5, after s4, 10d
        Development complete: done, milestone, dc, after s5, 1d
    
    section UAT
        Create Training Material (2025-04-05) - Hugo: done, crit, ctm, 2025-02-20, 40d
        Create UAT Environment - Nigel: done, cue, after s4, 1d
        Setup Users on UAT Env - Nigel: done, sue, after cue, 2d
        Deploy to UAT Env - Mary: done, due, after s5 cue, 2d
        Execute Manual Steps (See below) - Mary: done, ems, after due, 1d
        Perform Domain Authentication - Nigel: active, pda, after ems, 3d
        Import Sample Data - Claire: active, isd, after due, 2d
        UAT Preparations complete - Bernd: milestone, upc, after ems isd pda, 1d
        Execute UAT - Hugo/Bernd: uat, after upc, 3d
        Structure Feedback - Hugo/Bernd: feedback, after uat, 3d
        Fix Bugs - Mary: bugs, after feedback, 7d
        Deploy final fixes - Mary: dff, after bugs, 1d
        Retest fixes - Hugo/Claire: rf, after dff, 3d

    section Prod
        GoLive Decision - Bernd: milestone, gld, after rf, 1d
        Create Production Environment - Nigel: cpe, after gld, 1d
        Setup Users on Prod Env - Nigel: spe, after cpe, 3d
        Deploy to Prod Env - Mary: dpe, after cpe bugs, 2d
        Execute Manual Steps (See below) - Mary: emsp, after dpe, 1d
        Perform Domain Authentication - Nigel: pdap, after emsp, 3d
        Import OnPremise Data - Claire: iod, after dpe, 4d
        Disable Admin Mode - Nigel: dam, after iod pdap, 1d
        Announce System Availability - Bernd: milestone, asa, after dam, 1d
&lt;/pre&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;::: mermaid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gantt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title GoLive Timeline
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dateFormat YYYY-MM-DD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    excludes weekends
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    section Dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 1: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, s1, 2025-02-03, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 2: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, s2, after s1, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 3: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, s3, after s2, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 4: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, s4, after s3, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Sprint 5: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, s5, after s4, 10d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Development complete: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, milestone, dc, after s5, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    section UAT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Create Training Material &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;2025-04-05&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; - Hugo: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, crit, ctm, 2025-02-20, 40d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Create UAT Environment - Nigel: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, cue, after s4, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Setup Users on UAT Env - Nigel: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, sue, after cue, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Deploy to UAT Env - Mary: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, due, after s5 cue, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Execute Manual Steps &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;See below&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; - Mary: &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;, ems, after due, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Perform Domain Authentication - Nigel: active, pda, after ems, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Import Sample Data - Claire: active, isd, after due, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        UAT Preparations complete - Bernd: milestone, upc, after ems isd pda, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Execute UAT - Hugo/Bernd: uat, after upc, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Structure Feedback - Hugo/Bernd: feedback, after uat, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Fix Bugs - Mary: bugs, after feedback, 7d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Deploy final fixes - Mary: dff, after bugs, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Retest fixes - Hugo/Claire: rf, after dff, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    section Prod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        GoLive Decision - Bernd: milestone, gld, after rf, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Create Production Environment - Nigel: cpe, after gld, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Setup Users on Prod Env - Nigel: spe, after cpe, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Deploy to Prod Env - Mary: dpe, after cpe bugs, 2d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Execute Manual Steps &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;See below&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; - Mary: emsp, after dpe, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Perform Domain Authentication - Nigel: pdap, after emsp, 3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Import OnPremise Data - Claire: iod, after dpe, 4d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Disable Admin Mode - Nigel: dam, after iod pdap, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Announce System Availability - Bernd: milestone, asa, after dam, 1d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:::
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
        </item>
        <item>
        <title>[VIDEO] Handling complex code in Power Pages (with bundling)</title>
        <link>https://www.marius-wodtke.de/post/power-pages/framework-1/</link>
        <pubDate>Sun, 19 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/framework-1/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/framework-1/cover.jpg" alt="Featured image of post [VIDEO] Handling complex code in Power Pages (with bundling)" /&gt;&lt;p&gt;With bundling, we can write in as many files as we want and the result will still be only one JavaScript output file. This is great for more complex use cases in Power Pages, where creating webfiles is a hassle and managing dependencies can be tricky.&lt;/p&gt;
&lt;p&gt;Find the sample repository here: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesTemplate&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesTemplate&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Deeplink to Scotts course: &lt;a class=&#34;link&#34; href=&#34;https://learn.develop1.net/courses/building-javascript-web-resources-using-typescript/dashboard&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://learn.develop1.net/courses/building-javascript-web-resources-using-typescript/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/_tQReD43LK8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/_tQReD43LK8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/framework-1/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Timeline: Are auto-posts annoying you?</title>
        <link>https://www.marius-wodtke.de/post/timeline/standard/auto-post/</link>
        <pubDate>Sun, 05 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/timeline/standard/auto-post/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/standard/auto-post/cover.jpg" alt="Featured image of post Timeline: Are auto-posts annoying you?" /&gt;&lt;p&gt;Posts in the timeline are certainly a topic for debate. Do you allow users to create them, why shouldn&amp;rsquo;t they use Notes instead? And of course the question &amp;ldquo;Do you really need an element describing that the current record was created?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;There are reasons to have it, for example because the date is included you could economize the Created On field from a form with a Timeline and it is kind of a close to the timeline.&lt;/p&gt;
&lt;p&gt;But on the other hand, this information is completely redundant and does occupy space in the database! A good reason to forgo auto-posts for organizations with a lot of records that will have auto-posts enabled by default.&lt;/p&gt;
&lt;p&gt;Fortunately we can control auto-posts to a certain degree. Microsoft predefined for which events auto-posts may be generated and you can decide to enable or disable this feature for the given events.&lt;/p&gt;
&lt;p&gt;And if you are wondering what exactly an auto-post is, it&amp;rsquo;s these messages within the timeline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/standard/auto-post/Autopost.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;preconditions&#34;&gt;Preconditions&lt;/h2&gt;
&lt;p&gt;This might only apply to you if you came here to enable auto-posting: It is only available on environments with &lt;em&gt;Dynamics 365 Apps&lt;/em&gt; enabled! So for example you won&amp;rsquo;t be able to test this on a developer environment even though you are able to set up timelines.&lt;/p&gt;
&lt;p&gt;The other thing I&amp;rsquo;d like to mention: Definitely check your active rules. For this post I created a new environment and there were far less active rules than environments created in the past. For example the mentioned rules for the creation of contacts, accounts and leads were not active by default! So apparently Microsoft is adjusting these as well to have less default auto-posting, maybe I should change the title of this post to &amp;ldquo;Are you missing auto-posts?&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;So first of all I have to apologize for the looks of these pictures, somehow my environment was created in german and the translation only partly worked. I will try my best to also describe the things seen in english.&lt;/p&gt;
&lt;p&gt;So you will find the auto-post rules in the &lt;em&gt;Power Platform Environment Settings&lt;/em&gt; app under &lt;em&gt;System&lt;/em&gt;, &lt;em&gt;Activity Feed Rules&lt;/em&gt;. Here you find a list of the predefined rules of Microsoft, the names are in the base language of the environment. For the sample I took &amp;ldquo;New Contact&amp;rdquo; configuration and activated it. After that of course when I create a new Contact it immediately receives the corresponding auto-post you see in the picture above.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/standard/auto-post/Config1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Auto-post rules are found in the Power Platform Environment Settings app under System, Activity Feed Rules&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/standard/auto-post/Config2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Auto-post rules are activated or deactivated.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;When you activate an auto-post rule that creates a plugin step. The name of the plugin is a little off, but we can see from the xml in the Unsecure Configuration that this plugin is configurable and is configured to work for Contact creation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/standard/auto-post/Plugin.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;You can also see that the step is synchronous. This ensures that the timeline post is already there when the form refreshes after the Contact creation, when the timeline loads.&lt;/p&gt;
&lt;p&gt;This however has performance implications. If you want to push a lot contacts you might to spare any synchronous step on creation. Also consider this for rules that work on N:N relationships (Associate). Because associate cannot be filtered down to specific relationships or entities, having a synchronous step here will impact all associates happening in the system!&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The performance implications of auto-post rules are a good reason to sit together with IT and Business to decide on what rules offer a benefit and should be used, everything else should be omitted. While I always talk about reducing the amount of active rules, it might also be the case that you find very useful rules that are not active in your environment and I definitely don&amp;rsquo;t want to discourage you from activating them! I just want to raise awareness that they are executed in a synchronous manner, something you try to usually limit.&lt;/p&gt;
&lt;p&gt;To find out what rules are active, head to the &lt;em&gt;Power Platform Environment Settings&lt;/em&gt; app, &lt;em&gt;System&lt;/em&gt;, &lt;em&gt;Activity Feed Rules&lt;/em&gt;. Here you can select rules and (de-)activate them. However, the menu point is only found in environments that had the switch for &lt;em&gt;Dynamics 365 Apps&lt;/em&gt; active when they were created.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: Wrap up Advent Calendar series &amp; a BIG thank you</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-advent/24/</link>
        <pubDate>Tue, 24 Dec 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-advent/24/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: Wrap up Advent Calendar series &amp; a BIG thank you" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/r4PTj0ArVo4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/r4PTj0ArVo4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/24/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: Translation with Open AI and Integrate with Dataverse</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-advent/22/</link>
        <pubDate>Sun, 22 Dec 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-advent/22/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: Translation with Open AI and Integrate with Dataverse" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/HrDeELAlXJo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/HrDeELAlXJo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/22/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: Integrating Translation - Retrieval Augmented Generation</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-advent/13/</link>
        <pubDate>Fri, 13 Dec 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-advent/13/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: Integrating Translation - Retrieval Augmented Generation" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/dWL3i1KHiwI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/dWL3i1KHiwI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/13/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: How to integrate translation by using AI - Options for the dictionary?</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-advent/07/</link>
        <pubDate>Sat, 07 Dec 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-advent/07/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: How to integrate translation by using AI - Options for the dictionary?" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce AI in Power Platform and learn something about Power Pages. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/7-uPmnHs9pk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/7-uPmnHs9pk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-advent/07/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Pages Actions vs Flow with Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api-vs-flow/</link>
        <pubDate>Sun, 17 Nov 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api-vs-flow/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-vs-flow/cover.jpg" alt="Featured image of post [VIDEO] Power Pages Actions vs Flow with Power Pages" /&gt;&lt;p&gt;Custom APIs offer a lot of flexibility, but cannot be called directly from PowerPages. With this video and the accompanying repository/solution, this is going to change! We are going to call custom functionality with an interface that is similar to what you know from the JavaScript of Model-Driven Apps.&lt;/p&gt;
&lt;p&gt;However by now there is also the possibility to use a Flow as a proxy to call the API. In this video we will compare some of the differences of both methods.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/b2KfA8ZJZI8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/b2KfA8ZJZI8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-vs-flow/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Copilot agents, Timeline custom connectors, Power Platform catalogs and Gen AI search in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-11/</link>
        <pubDate>Sun, 17 Nov 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-11/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-11/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Copilot agents, Timeline custom connectors, Power Platform catalogs and Gen AI search in Power Pages" /&gt;&lt;p&gt;While we get closer to the advent calendar season, the group still has new tips for you in store ;)&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/PksE1J1_sPQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/PksE1J1_sPQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-11/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to set the Page Permissions in Power Pages ?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/page-permissions/</link>
        <pubDate>Fri, 15 Nov 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/page-permissions/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/page-permissions/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to set the Page Permissions in Power Pages ?" /&gt;&lt;p&gt;This time in the Power Pages Quick Tips Series we discuss Page Permissions which allow you to show certain pages to certain user groups by web role.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/MvZS3G0QVYA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/MvZS3G0QVYA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/page-permissions/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>What should have been my first Dataset-PCF</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/no-dataset-pcf/</link>
        <pubDate>Sun, 03 Nov 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/no-dataset-pcf/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/no-dataset-pcf/cover.jpg" alt="Featured image of post What should have been my first Dataset-PCF" /&gt;&lt;p&gt;I recently wanted to solve a suboptimal experience with a subgrid on the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/custom-api/&#34; &gt;Power Pages Action Configuration&lt;/a&gt; entity and thought I would build on my first shot with a &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/pcf-react/&#34; &gt;React PCF&lt;/a&gt;, trying a &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/component-framework/reference/dataset&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;dataset PCF&lt;/a&gt; for the first time.&lt;/p&gt;
&lt;p&gt;Unlike &amp;ldquo;normal&amp;rdquo; PCF which bind to fields, as the name says, dataset PCFs bind to a dataset. Dataset we could also call list or grid, so in theory this is perfect to customize the experience of my subgrid.&lt;/p&gt;
&lt;h2 id=&#34;controlmanifest-differences&#34;&gt;ControlManifest differences&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ControlManifest.Input.xml&lt;/code&gt; is where you specify the bindings. A regular PCF will specify one or more &lt;code&gt;property&lt;/code&gt; nodes in the &lt;code&gt;control&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-XML&#34; data-lang=&#34;XML&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;control&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;namespace=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;constructor=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;version=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.0.5&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;control-type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;virtual&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;isrestrictedtoauthenticated&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Restrict to Authenticated Users&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Boolean wether only authenticated users are allowed&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;of-type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TwoOptions&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;usage=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bound&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;required=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/control&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The dataset PCF will use a &lt;code&gt;data-set&lt;/code&gt; with &lt;code&gt;property-set&lt;/code&gt; nodes inside. The &lt;code&gt;data-set&lt;/code&gt; also resides in the &lt;code&gt;control&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-XML&#34; data-lang=&#34;XML&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;control&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;namespace=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;constructor=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;version=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.0.5&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;control-type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;virtual&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;data-set&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;powerpagecomponentdataset&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Power_Page_Component_Dataset&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property-set&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;componentname&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;componentname_Name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;componentname_Desc&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;of-type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SingleLine.Text&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;usage=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bound&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;required=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/data-set&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/control&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can pregenerate such a manifest by using the &lt;code&gt;--template dataset&lt;/code&gt; parameter when using &lt;code&gt;pac pcf init&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When customizing the form, you can select it from the &lt;em&gt;Components&lt;/em&gt; Workspace, pull it to the form and will be obliged to select the table and view for the dataset. Next would be to select the bound &lt;em&gt;componentname&lt;/em&gt;, you can skip that, the data of the view will be delivered to you anyways, but a &lt;code&gt;property-set&lt;/code&gt; might make it easier to implement generic components.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/no-dataset-pcf/AddDataset.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-catch&#34;&gt;The catch&lt;/h2&gt;
&lt;p&gt;There is a problem for my subgrid though. You can&amp;rsquo;t mix a bound &lt;code&gt;property&lt;/code&gt; with &lt;code&gt;data-set&lt;/code&gt;! But that&amp;rsquo;s exactly what I was trying to achieve, selecting something in the subgrid should manipulate fields on the form (or vice versa).&lt;/p&gt;
&lt;p&gt;But when you try to use both nodes as shown below, the PCF won&amp;rsquo;t be selectable in a form. Meaning we will need a different solution.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-XML&#34; data-lang=&#34;XML&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;control&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;namespace=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;constructor=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;version=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.0.5&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PowerPagesActionsRoleSelector description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;control-type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;virtual&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;isrestrictedtoauthenticated&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Restrict to Authenticated Users&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Boolean wether only authenticated users are allowed&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;of-type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TwoOptions&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;usage=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bound&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;required=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;data-set&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;powerpagecomponentdataset&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display-name-key=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Power_Page_Component_Dataset&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- DOES NOT WORK! --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/control&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;a-solution&#34;&gt;A solution&lt;/h2&gt;
&lt;p&gt;What could we do now? Well we can query data from regular PCFs but do not have a supported way to interact with the form apart from a bound &lt;code&gt;property&lt;/code&gt;. Therefore, I will skip the dataset PCF again, do a regular PCF and if I needed to make the dataset configurable I would need to either pass a FetchXML as string or multiple strings for the OData parameters as inputs.&lt;/p&gt;
&lt;p&gt;The biggest challenge now is to render a proper grid. Fortunately, Fabric with it&amp;rsquo;s &lt;em&gt;office-ui-fabric-react&lt;/em&gt; library can help us achieve a quite decent look without much styling effort. The react component for a grid with selection looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-react&#34; data-lang=&#34;react&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Fabric&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;style&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{ &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;100%&amp;#34;&lt;/span&gt; }}&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;MarqueeSelection&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;} &amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;DetailsList&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;items&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;columns&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;columns&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;setKey&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;layoutMode&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;DetailsListLayoutMode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;justified&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;selectionPreservedOnEmptyClick&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;ariaLabelForSelectionColumn&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Toggle selection&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;ariaLabelForSelectAllCheckbox&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Toggle selection for all items&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;checkButtonAriaLabel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Row checkbox&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;MarqueeSelection&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Fabric&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see the only styling I needed was to set the width of the &lt;code&gt;Fabric&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think it makes too much sense to detail more code here. What you get in data and columns will definitely change, so I will &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions/tree/main/PowerPagesActionsRoleSelector/PowerPagesActionsRoleSelector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;link you to the repository here&lt;/a&gt; and only touch a bit on the selection since it took me some time to understand. If you don&amp;rsquo;t need to act on record selection, you may skip the next section.&lt;/p&gt;
&lt;h2 id=&#34;making-selections&#34;&gt;Making selections&lt;/h2&gt;
&lt;p&gt;My idea was to show all available options in the grid and let the user associate via simply selecting the row. This makes associating a single button click. And of course disassociating as well. The approach will of course struggle in scenarios with a large number of selectable records, but with WebRoles, this should be fine.&lt;/p&gt;
&lt;p&gt;Both the &lt;code&gt;MarqueeSelection&lt;/code&gt; and the &lt;code&gt;DetailsList&lt;/code&gt; share the &lt;code&gt;this.selection&lt;/code&gt; object.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Selection&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;getKey&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;onSelectionChanged&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selectionChanged&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The onSelectionChanged passes the event up to a function defined in the &lt;code&gt;props&lt;/code&gt;, this is because the associate/disassociate happens in a different class and &lt;code&gt;selectionChanged&lt;/code&gt; is the hook that is tied to an instance of it.&lt;/p&gt;
&lt;p&gt;But the selection items do not work on their own! You need to explicitly pass the data and what is currently selected. So actually the &lt;code&gt;MarqueeSelection&lt;/code&gt; and the &lt;code&gt;DetailsList&lt;/code&gt; also implicitly share the data as well.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setItems&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setKeySelected&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selected&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I guess this is clear, we first set the data for the selection and then iterate the data so preselect the items that are already associated(/selected). But the 2 boolean parameters might need explanation.&lt;/p&gt;
&lt;p&gt;The second parameter of &lt;code&gt;setItems&lt;/code&gt; determines if the data shall just be added or if the collection should first be cleared and then the items are added. This is of course what we want here since we likely retrieved all selectable objects from the database.&lt;/p&gt;
&lt;p&gt;The third parameter of &lt;code&gt;setKeySelected&lt;/code&gt; determines whether to &amp;ldquo;anchor&amp;rdquo; the row. So basically highlighting it as the currently selected one by changing the background color of the row. This is unwanted here, I only want to set the checkmark at the start of the row since this code is executed without the user interacting with the grid, it is just executed after the data is being loaded successfully.&lt;/p&gt;
&lt;p&gt;Lastly lets have a quick look at the interface of data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecord&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IObjectWithKey&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;site&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;selected&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we have the &lt;code&gt;key&lt;/code&gt; we need for the selection (natural selection here is the guid) and &lt;code&gt;selected&lt;/code&gt; to feed the &lt;code&gt;setKeySelected&lt;/code&gt; function. Whether a column is selected is determined by it being associated to the current record in my case. &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;site&lt;/code&gt; are irrelevant for the selection, they are for columns rendered in the &lt;code&gt;DetailsList&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;While dataset PCFs are the primary solution to provide a custom experience for grids and subgrids, they do not work well, when you also need to manipulate the form. As a workaround we can also use a regular PCF and then leverage the React library of Fabric (also found as &lt;a class=&#34;link&#34; href=&#34;https://developer.microsoft.com/en-us/fluentui#/get-started&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Microsoft Fluent UI&lt;/a&gt;) to render a grid and use bound properties to interact with the form. The result looks pretty similar to a regular grid. Let me &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions/tree/main/PowerPagesActionsRoleSelector/PowerPagesActionsRoleSelector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;link to the full code here again&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/no-dataset-pcf/cover.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The final N:N relation grid where you associate by simply selecting a record. The existing associations will be preselected on loading.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If you do not have the need to interact with the form and just want to make a custom grid experience, let me link you to some helpful resources from others:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://javascript.plainenglish.io/building-pcf-dataset-control-using-react-e2e67c1e1ba2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Rohans explanation with React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://dianabirkelbach.wordpress.com/category/pcf/dataset/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Dianas collection of Dataset PCF articles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://carldesouza.com/building-a-dataset-pcf-control-with-styling-without-react/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Carls explanation without React&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to customize content by using Content Snippets in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/snippets/</link>
        <pubDate>Wed, 23 Oct 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/snippets/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/snippets/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to customize content by using Content Snippets in Power Pages?" /&gt;&lt;p&gt;This time in the Power Pages Quick Tips Series we discuss the Content Snippets you can use to customize the login and registration pages.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/nWyGKEqbhqA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/nWyGKEqbhqA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/snippets/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Content Snippets for Power Pages Login Page</title>
        <link>https://www.marius-wodtke.de/post/power-pages/contentsnippets-login/</link>
        <pubDate>Sun, 20 Oct 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/contentsnippets-login/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets-login/cover.jpg" alt="Featured image of post Content Snippets for Power Pages Login Page" /&gt;&lt;p&gt;To accompany &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=nWyGKEqbhqA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;our recent video&lt;/a&gt; over on &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/@PowerPlatformLearners&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Power Platform Learners channel&lt;/a&gt;, here is a list of all the content snippet I know for customizing the login and registration pages of Power Pages&lt;/p&gt;
&lt;h2 id=&#34;signin&#34;&gt;SignIn&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Account/SignIn/RememberMeLabel&lt;/li&gt;
&lt;li&gt;Account/SignIn/SignInExternalDeprecatedFormHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/SignInExternalFormHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/SignInLocalFormHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/TermsAndConditionsAgreementText&lt;/li&gt;
&lt;li&gt;Account/SignIn/TermsAndConditionsButtonText&lt;/li&gt;
&lt;li&gt;Account/SignIn/TermsAndConditionsCopy&lt;/li&gt;
&lt;li&gt;Account/SignIn/TermsAndConditionsHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/UsernameLabel&lt;/li&gt;
&lt;li&gt;Account/SignIn/AccountConversionIdentityUsedErrorHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/AccountConversionIdentityUsedErrorText&lt;/li&gt;
&lt;li&gt;Account/SignIn/MinorNotAllowedCopy&lt;/li&gt;
&lt;li&gt;Account/SignIn/MinorNotAllowedHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/MinorWithoutParentalConsentNotAllowedCopy&lt;/li&gt;
&lt;li&gt;Account/SignIn/MinorWithoutParentalConsentNotAllowedHeading&lt;/li&gt;
&lt;li&gt;Account/SignIn/PageCopy&lt;/li&gt;
&lt;li&gt;Account/SignIn/PasswordLabel&lt;/li&gt;
&lt;li&gt;Account/SignIn/PasswordResetLabel&lt;/li&gt;
&lt;li&gt;Account/SignIn/SignInLocalButtonText&lt;/li&gt;
&lt;li&gt;Authentication/OpenIdConnect/AzureAD/Caption&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;register&#34;&gt;Register&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Account/Register/ConfirmPasswordLabel&lt;/li&gt;
&lt;li&gt;Account/Register/ExternalAuthenticationFailed&lt;/li&gt;
&lt;li&gt;Account/Register/PageCopy&lt;/li&gt;
&lt;li&gt;Account/Register/PasswordLabel&lt;/li&gt;
&lt;li&gt;Account/Register/RegisterExternalFormHeading&lt;/li&gt;
&lt;li&gt;Account/Register/RegisterLocalFormHeading&lt;/li&gt;
&lt;li&gt;Account/Register/RegistrationDisabledMessage&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;redeem-invitation&#34;&gt;Redeem (Invitation)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Account/Redeem/RedeemByLoginLabel&lt;/li&gt;
&lt;li&gt;Account/RedeemInvitation/PageCopy&lt;/li&gt;
&lt;li&gt;Account/Redeem/InvitationCodeFormHeading&lt;/li&gt;
&lt;li&gt;Account/Redeem/InvitationCodeLabel&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;setpassword&#34;&gt;SetPassword&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Account/SetPassword/ConfirmPassword&lt;/li&gt;
&lt;li&gt;Account/SetPassword/Email&lt;/li&gt;
&lt;li&gt;Account/SetPassword/PageCopy&lt;/li&gt;
&lt;li&gt;Account/SetPassword/SetPasswordButtonText&lt;/li&gt;
&lt;li&gt;Account/SetPassword/Username&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;passwordreset&#34;&gt;PasswordReset&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Account/PasswordReset/EmailLabel&lt;/li&gt;
&lt;li&gt;Account/PasswordReset/EmailInstructionsText&lt;/li&gt;
&lt;li&gt;Account/PasswordReset/ForgotPasswordFormHeading&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;navigation&#34;&gt;Navigation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Account/Nav/Register&lt;/li&gt;
&lt;li&gt;Account/Nav/SignIn&lt;/li&gt;
&lt;li&gt;Account/Nav/Redeem&lt;/li&gt;
&lt;li&gt;Profile/SecurityNav/ChangePassword&lt;/li&gt;
&lt;li&gt;Profile/SecurityNav/SetPassword&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;special-thanks-and-references&#34;&gt;Special Thanks and References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ulrikke.akerbak.com/2020/02/16/customize-the-sign-in-and-registration-page-in-power-app-portals/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Ulrikke Akerbæk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://community.dynamics.com/forums/thread/details/?threadid=d68d0750-b902-4363-a46e-ff9742c6fb1c&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Arpit Shrivastava/meenoo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://community.dynamics.com/forums/thread/details/?threadid=a5ba9bed-b3d1-4050-99bb-0d27a40602c6&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Oliver Rodrigues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.linkedin.com/pulse/customizing-microsoft-dynamics-crm-365-portal-login-valenti-clari/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Edwin Valenti Clari&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/OOlashyn/PowerAppsPortalSiteSettingsAndSnippets&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Oleksandr Olashyn/Oliver Rodrigues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pictures&#34;&gt;Pictures&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets-login/cover.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets-login/register.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets-login/redeem.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets-login/profilenav.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets-login/passwordreset.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Translating Power Pages with Content Snippets</title>
        <link>https://www.marius-wodtke.de/post/power-pages/contentsnippets/</link>
        <pubDate>Sun, 13 Oct 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/contentsnippets/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets/cover.jpg" alt="Featured image of post [VIDEO] Translating Power Pages with Content Snippets" /&gt;&lt;p&gt;In a recent video on the &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/@PowerPlatformLearners&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;@PowerPlatformLearners&lt;/a&gt; channel, we showcased Content Snippets with the login page. But this time we are going to use them for our very own content, showing button labels in german.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/bUif_3k-PJY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/bUif_3k-PJY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/contentsnippets/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Timeline Custom Connectors 3: Adding Filters</title>
        <link>https://www.marius-wodtke.de/post/timeline/custom/filter/</link>
        <pubDate>Sun, 29 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/timeline/custom/filter/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/filter/cover.jpg" alt="Featured image of post Timeline Custom Connectors 3: Adding Filters" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/timeline/custom/data/&#34; &gt;the last post&lt;/a&gt; of this series about Timeline Custom Connectors we added contacts of an account to the timeline. But we have not included them at all in the filters of the timeline, meaning you can&amp;rsquo;t view the contacts in isolation or filter them out to only show activities.&lt;/p&gt;
&lt;h2 id=&#34;prior-considerations&#34;&gt;Prior considerations&lt;/h2&gt;
&lt;p&gt;You don&amp;rsquo;t need to implement filtering, you can skip the &lt;code&gt;getFilterDetails&lt;/code&gt; at all, it&amp;rsquo;s optional. And considering my field experiences, there is a considerable amount of user-groups that use the timeline to get a glance at recent activity. Filters are totally irrelevant for them since they are able to identify the relevant records by simply scrolling through.&lt;/p&gt;
&lt;p&gt;But of course this is not true for all organizations. The more data is displayed in the timeline, the more users crave for filtering. And us adding more data could be the reason, users finally say &amp;ldquo;why can&amp;rsquo;t I only display X&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;When implementing &lt;code&gt;getFilterDetails&lt;/code&gt; we can return a partial set of filters that will be merged with the filters returned by the other modules (implemented by MS). We can both extend existing filters as well as introduce custom, new filter categories. For the sample I extended the &lt;em&gt;Module&lt;/em&gt; which usually has things like Activities, Posts and Notes with the new value &lt;em&gt;Contacts&lt;/em&gt;. Also we are going to introduce a new filter for the gender of contacts. I don&amp;rsquo;t have a good use case for this as I can&amp;rsquo;t imagine contacts being a good fit for the timeline, but it will show well that you can have both Checkboxes (&lt;code&gt;MultiSelect&lt;/code&gt;) as well as Radiobuttons (&lt;code&gt;SingleSelect&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The reason why I kind of hinted to skip implementing filters if possible is the &lt;code&gt;count&lt;/code&gt; property. An option with zero records will not be shown, if a category (&lt;code&gt;FilterGroup&lt;/code&gt;) has no option with at least one records, it will be hidden. But you also can&amp;rsquo;t just put one to all options, if an &amp;ldquo;invalid&amp;rdquo; combination of filters is chosen, the timeline will start to misbehave with infinite loads. So the only option for us is implementing the count correctly, which is harder than you might think. ;)&lt;/p&gt;
&lt;p&gt;To help you getting started yourself, this article has a branch in the sample repository called &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/tree/Filters&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Filters&lt;/a&gt;. Here you will find the full code of the commented snippets we discuss in the coming sections.&lt;/p&gt;
&lt;h2 id=&#34;changes-to-contactdata&#34;&gt;Changes to ContactData&lt;/h2&gt;
&lt;p&gt;Just for completeness: The data being retrieved from contact in the previous branch &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/DataverseContactData/src/ContactData.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;DataverseContactData&lt;/a&gt; did not include the gender. So I added the gender as a number to the &lt;code&gt;IRecord&lt;/code&gt; interface, retrieved it from Dataverse and mapped it to the record. This will allow us to filter on this property.&lt;/p&gt;
&lt;h2 id=&#34;a-new-class&#34;&gt;A new class&lt;/h2&gt;
&lt;p&gt;Now to the real thing, the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/Filters/src/ContactFilter.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ContactFilter&lt;/a&gt; class.&lt;/p&gt;
&lt;p&gt;In this class I introduced a &lt;code&gt;getFilterDetails&lt;/code&gt; that will not only get the &lt;code&gt;IFilterRequest&lt;/code&gt; like the method in the official interface, but also the records we retrieved in the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/Filters/src/ContactData.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ContactData&lt;/a&gt; class. And then we can just define a filter like this and return it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;FilterGroupName.RecordType&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FilterGroupType&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MultiSelect&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Contacts&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;label&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Contacts&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But remember, we still need to cover the count properly before this is usable, but the &amp;ldquo;Record Type&amp;rdquo; filter should now show a new option &amp;ldquo;Contacts (1)&amp;rdquo;. Alongside we will see &amp;ldquo;Activities (x)&amp;rdquo; from Microsofts Activities module. So extending an existing filter just requires you to match the name, in the FilterGroupName enum you will find more predefined filters.&lt;/p&gt;
&lt;p&gt;With our custom filter we can do very similarly, we are just going to define more options and make use of the fact that label and value are two separate properties, allowing us to match the OptionsetValues with the values of the filter options. Also I will introduce variables for the strings here as a best practice since I will need them multiple times.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gender&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Gender&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;male&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Male&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maleNumber&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;female&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Female&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;femaleNumber&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.gender&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FilterGroupType&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SingleSelect&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;label&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.male&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.maleNumber&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;label&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.female&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.femaleNumber&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need the correct count. For this we have the method &lt;code&gt;getFilteredRecords(records: IRecord[], filter?: IFilterRequest, additional?: IAdditionalFilter)&lt;/code&gt;. It also takes the current records plus the filter and should return all records matching the filters. This is of course necessary to determine what the &lt;code&gt;getRecordsData&lt;/code&gt; function from the official interface should return when filters are applied. But the optional &lt;em&gt;additional&lt;/em&gt; parameter will represent &amp;ldquo;what if the user selected this option?&amp;rdquo;. So the combination of existing filters being passed to us by the pipeline plus an imaginary additional filter will determine the final count.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filterExecutors&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { [&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;ISelectedFilter&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#a6e22e&#34;&gt;FilterGroupName&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RecordType&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filterModule&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;gender&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filterGender&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;getFilteredRecords&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;[], &lt;span style=&#34;color:#a6e22e&#34;&gt;filter?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IFilterRequest&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;additional?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IAdditionalFilter&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecord&lt;/span&gt;[] {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilters&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getSelectedFilters&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;additional&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecords&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilters&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filterExecutor&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filterExecutors&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;filterExecutor&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecords&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecords&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filterExecutor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecords&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filterModule&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;ISelectedFilter&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;some&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;contacts&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filterGender&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;ISelectedFilter&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selectedFilter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;some&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;gender&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What you can see here that the methods &lt;code&gt;filterModule&lt;/code&gt; and &lt;code&gt;filterGender&lt;/code&gt; are custom for each type of filter since I could imagine an interface that will fit for any situation. Just take the RecordType as a sample: To have something like a &lt;em&gt;matching property .ToString matches the value&lt;/em&gt; for the record type, we would need to enrich our IRecord interface with a RecordType that is static for all records.&lt;/p&gt;
&lt;p&gt;Instead here the dictionary &lt;code&gt;filterExecutors&lt;/code&gt; just stores functions for each FilterGroup and those functions can do all the fancy stuff we need to determine the correct outcome.&lt;/p&gt;
&lt;p&gt;For the method &lt;code&gt;getSelectedFilters&lt;/code&gt; I will just link you to &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/Filters/src/ContactFilter.ts#L90&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the sample repository&lt;/a&gt;, since it&amp;rsquo;s a little to long for display. In essence it just extracts all the groups that have at least one option selected, all the others are ignored in filtering. Afterwards, if present, the additional filter either needs to be added, or just the new option needs to be added or in case of SingleSelect, the existing option might need to be replaced.&lt;/p&gt;
&lt;p&gt;So last thing to do, apply this new function to the count, here is a sample.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;FilterGroupName.RecordType&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FilterGroupType&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MultiSelect&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.contacts&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;label&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.contacts&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.getFilteredRecords&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;FilterGroupName.RecordType&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.contacts&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FilterGroupType&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MultiSelect&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;                }).&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;changes-to-mydatasource&#34;&gt;Changes to MyDataSource&lt;/h2&gt;
&lt;p&gt;Well, a) &lt;code&gt;getFilterDetails&lt;/code&gt; needs to call &lt;code&gt;getFilterDetails&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getFilterDetails&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;filter?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IFilterRequest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IFilterGroup&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;[]&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;contactFilter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getFilterDetails&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecord&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and b) we should also the selected filters to &lt;code&gt;getRecordsData&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordsData&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecordsDataRequest&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;filter?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IFilterRequest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordsDataResponse&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;contactData&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordsData&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecords&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;contactFilter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getFilteredRecords&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecord&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;requestId&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;request.requestId&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.records.filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecords&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;some&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecord&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filteredRecord&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-result&#34;&gt;The result&lt;/h2&gt;
&lt;p&gt;Now, what will it look like in the system? Let&amp;rsquo;s look at some pictures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/filter/Filters1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Filters are opened with the symbol on the top right of the timeline.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/filter/Filters2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Selecting Activities leaves no valid options for Gender. But the Contacts option is still shown!&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/filter/Filters3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When Selecting Contacts as well, our Gender filter comes up again&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/filter/Filters4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;With only Contacts selected, the timeline shows only contacts. Also the “standard” filters will vanish.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/filter/Filters5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;And we can also filter by Gender. Single Select filters can only be cleared with the clear filters button on the top right.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;We can nicely see how selecting filters affects other groups, even between different modules. We can also see the importance of those &amp;ldquo;imaginary filters&amp;rdquo; we added in calculating the count of records.&lt;/p&gt;
&lt;p&gt;With the Single Select I noticed that once you selected the filter, you cannot deselect it unless you use the button to clear all filters, which will also clear the other groups. This is not ideal and might be a reason to simply go with Multi Select everywhere to avoid this behavior, even if a Single Select might be a proper choice like our gender use case.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Implementing filters requires quite some code! But once implemented it has a nice feel and gives the user options to find what they need in crowded timelines.&lt;/p&gt;
&lt;p&gt;To implement filters you have the option to extend existing ones by matching their &lt;em&gt;FilterGroupName&lt;/em&gt; or adding new groups. Implementing filters is optional, but when you choose to, you will have to make sure the count of remaining records if that option was chosen is calculated correctly. When you use &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/Filters/src/ContactFilter.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the sample&lt;/a&gt; as a template you can do so by just implementing a function for each &lt;em&gt;FilterGroup&lt;/em&gt; and then registering it to the &lt;code&gt;filterExecutors&lt;/code&gt;. Afterwards you will just have to add the according &lt;em&gt;FilterGroup&lt;/em&gt; in the list returned from &lt;code&gt;getFilterDetails&lt;/code&gt;. Still: The ContactFilter class is the biggest we had so far in this series and it&amp;rsquo;s code is much less trivial than the samples before.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Set up the Microsoft Identity provider in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/identity-microsoft/</link>
        <pubDate>Sat, 21 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/identity-microsoft/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/identity-microsoft/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Set up the Microsoft Identity provider in Power Pages?" /&gt;&lt;p&gt;Power Pages has multiple Identity providers for handling Authentication and naturally Microsofts own Entra Id is one of them. Learn here how to set it up to enable login with a Microsoft account to for example allow your employees to login to your Power Page.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/i3DmsbveH2Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/i3DmsbveH2Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/identity-microsoft/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Connections vs. N:N Relationship vs. Connection Table</title>
        <link>https://www.marius-wodtke.de/post/pattern/connection-nn-entity/</link>
        <pubDate>Sun, 15 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/connection-nn-entity/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/cover.jpg" alt="Featured image of post Patterns: Connections vs. N:N Relationship vs. Connection Table" /&gt;&lt;p&gt;Often we have to design something that connects N records of Table A with M records of Table B. And often it is not that simple as &amp;ldquo;they are connected&amp;rdquo;. More information needs to be collected, like in which way are they connected? When were they connected and when disconnected?&lt;/p&gt;
&lt;h2 id=&#34;when-to-use-nn-relationships&#34;&gt;When to use N:N relationships&lt;/h2&gt;
&lt;p&gt;N:N Relationships are perfect whenever there is no additional information or processing required. But I want to stress that you should discuss whether something could be on the horizon. Once we established the relationship it&amp;rsquo;s hard to change it to one of the other options after it&amp;rsquo;s established.&lt;/p&gt;
&lt;p&gt;Some information might even be retrievable for N:N relationships: If you enabled Auditing for the involved tables, you will see the associations in the Audit History. However, keep in mind that Audit Data is not that easily analyzed!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/Audit1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Audit History only displays the operation.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/Audit2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;But opening the line reveals more information.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;One more thing to consider is processing: There is no Power Automate Trigger for Associate/Disassociate! And also if you are capable in high code, the Plugin Steps cannot be filtered down to a certain relationship. That means you will trigger on any Associate anywhere in the system and then in code you filter down to your tables. It might not sound like much, but platform operations like loading your assembly are heavy. Especially if it is synchronous logic, it will affect the whole system. Just one more sample: There is such a logic OOTB for Knowledge Article Attachments and it will affect the performance of an Associate between two custom tables.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/KBAttachment.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A synchronous Associate Plugin affects the performance of all associates, not just the relationship you intent to monitor.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Long story short: I advise against putting logic to Associate/Disassociate until we have effective measurements to filter on platform level. Even more so if the logic shall be synchronous as this may be a straight path to performance issues with N:N Relationships.&lt;/p&gt;
&lt;p&gt;There is also a big advantage to N:N relationships: You can associate multiple records to the one you are currently on at the same time. Users love that feature ;)&lt;br&gt;
You will find such a relationship OOTB at the Opportunity for Competitors.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/Competitors.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Competitors grid is found at Opportunity.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/SelectRecordsMulti.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Add Existing Records opens Select Records where multiple competitors may be selected.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Another subtle advantage over the other methods is the fact that this is only &amp;ldquo;one table far&amp;rdquo;. Some functionality in the platform can only span one table from the current record, for example Word Templates. So lets assume you&amp;rsquo;d connect the Competitors with a Connection instead of N:N, then you would only be able to print information from the Connection and not from the Competitor itself. You can sometimes get around this limitation by pulling information via calculated fields, but of course this approach does not scale.&lt;/p&gt;
&lt;h2 id=&#34;when-to-use-connections&#34;&gt;When to use Connections&lt;/h2&gt;
&lt;p&gt;Connections is another predefined concept by Microsoft. It is essentially what we will later look at as a &amp;ldquo;Connection Table&amp;rdquo;, but it is ready to use with your Dataverse. All you need to do is activate &amp;ldquo;Can have Connections&amp;rdquo; on the table(s) and you will be able to connect that table to any other table that has connections enabled. Here comes the first disadvantage: After being enabled, it cannot be disabled again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/ActivateConnection.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The “Can have Connections” setting is found on the table properties.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/ConnectionNavigation.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Tables that have connections enabled have navigation item in Related.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Connections not only have a &amp;ldquo;left&amp;rdquo; and &amp;ldquo;right&amp;rdquo; but they may also have roles, a description and a start and end date. And maybe important for your use case, it has the regular fields of a table like Owner, Created By, Modified On, etc.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/Connection.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Connections let us capture much more information out of the box and can even be customized! We can add more fields, however you should always keep in mind that Connections are a general concept. If we have 10 different relationships we&amp;rsquo;d like to manage with this feature and define 1-2 fields for each, the user will simply be overwhelmed by the options presented on the form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/ConnectionsManaged.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;There are some parts in the OOTB experience where MS already customized the experience for the users. The Stakeholders and Sales Team at the Opportunity are such examples. When you select + New Connection at these grids, the Select Records experience will open, you choose the Contact/User and the Connection Role will be auto-selected for you.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/SalesTeam.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Sales Team grid is found at Opportunity.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/SelectRecords.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;&amp;#43; New Connection opens Select Records instead of a Create Form.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;When you make a custom subgrid for connections on a form, the + New Connection will open a Create Form. Unfortunately for some reason Quick Create is disabled for this table&amp;hellip; And this is problematic for the user experience when users need to associate multiple records to the current one. This is also why &lt;a class=&#34;link&#34; href=&#34;https://crmtipoftheday.com/1233/make-connections-via-quick-create/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;other authors&lt;/a&gt; shy away from using this feature more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/MyConnection.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;My Connection is a regular subgrid I put to the form.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/MyConnectionForm.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;&amp;#43; New Connection opens a Create Form, leaving the current record.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;One more note regarding the roles: These can represent concrete concepts, like we see here with a user being the Account Manager of a customer. We could also define the Technical Sales, Escalation Manager and Service Team Members. In an onsite event scenario we could connect Accounts to the Event as Caterer, Light Tech, Security and so on to capture which of our partners participated in the execution.&lt;br&gt;
And the best thing is that these roles can be added to a solution, so we already have an ALM process for such scenarios.&lt;/p&gt;
&lt;p&gt;Here we have not that big of a problem with custom logic. Flows can be triggered on create or update of the Connection table and since we know the Ids of the Connection Roles even after deployment, we can even use the trigger filters.&lt;/p&gt;
&lt;h2 id=&#34;when-to-use-a-custom-connection-table&#34;&gt;When to use a custom connection table&lt;/h2&gt;
&lt;p&gt;We can always build something like the Connection ourselves. Just create an table C and put Lookups to the tables A and B on it, create a form and probably a Quick create form and we are done. And because it is that easy, &amp;ldquo;complex&amp;rdquo; relationships are often built like this.&lt;/p&gt;
&lt;p&gt;But should we? After all this is a significant customization, the resulting tables are not &amp;ldquo;standard&amp;rdquo;. There are good reason why projects use these a lot and a big reason is that they are NOT generalized. So when the user opens the form what he sees has a clear purpose, not to few, not to many fields, the Lookups point exactly to the tables he needs for the current process and not half the systems tables. But when we have competent users that know their ways/are able to learn their ways around Connections, in which situations should we still lean towards a custom Connection Table?&lt;/p&gt;
&lt;p&gt;Good identifiers are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have multiple or complex logic. A custom table just offers clear separation, especially if you need fields for processing states.&lt;/li&gt;
&lt;li&gt;You require non general fields for your process. If you do need fields that are specific to one process they will be shown on all Connection records and their forms. Unless you do JS evaluating the connected record types. But that is a quick road to moving away from the standard and doing heavy coding.&lt;/li&gt;
&lt;li&gt;You are building a core business process that justifies a customized experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I strongly advise you to enable and create a Quick Create Form as this will speed up the association process a lot for the user. To showcase this I created a Sponsor entity that connects an Event to a Contact but also needs to additional information specific to the process (while Amount could be general, Recurring Sponsor is not) and processing information that might be updated or triggers logic (Transaction Complete).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/Sponsors.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Sponsors is a regular subgrid I put to the form.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/connection-nn-entity/SponsorsForm.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;&amp;#43; New opens a Quick Create Form, the user still sees the current record.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I think when you started to read the article you already had an idea when to use what. But it&amp;rsquo;s a good idea to have some clear rules in your head why you choose one thing over another. This makes prior discussions and documentation afterwards much easier.&lt;/p&gt;
&lt;p&gt;You should use N:N relationships when you don&amp;rsquo;t need to capture any additional information on the relationship, you don&amp;rsquo;t need logic when such an association happens and will likely not need to in the future. Then you get the advantage that it is light in customization, the interface is very clear and users can associate multiple items at the same time.&lt;/p&gt;
&lt;p&gt;You should use Connections when the information you need to capture fits the schema of this OOTB table or the use case requires little customization that is of a more generic nature. Custom logic is possible here, but it might become challenging to separate multiple logics on the same table. Users need to associate records one by one. In a system that leverages Connections a lot users have a lot of flexibility in associating records, this may be a good or bad thing depending on your user base.&lt;/p&gt;
&lt;p&gt;When you need special fields on the relationship or heavy logic, that&amp;rsquo;s a sign for custom relationships. Also complex business processes and lack of training for the users might be reasons to choose a custom table for associating two tables. This gives the customizer the most control and isolation but also requires more customization and maintenance. Users will have to associate records one by one.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Copilot in Power Platform Admin center, New Grid feature, Virtual network in Power Platform &amp; more</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-08/</link>
        <pubDate>Mon, 09 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-08/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-08/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Copilot in Power Platform Admin center, New Grid feature, Virtual network in Power Platform &amp; more" /&gt;&lt;p&gt;The summer season has come and gone, but we still are able to get together and share some ideas and news around Power Platform in our group and I&amp;rsquo;m really grateful for that!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/kA2IENsxJWw&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/kA2IENsxJWw&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-08/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Pages Actions - Authorization Configuration Improvements</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api-auth-improvements/</link>
        <pubDate>Sun, 01 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api-auth-improvements/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-auth-improvements/cover.jpg" alt="Featured image of post [VIDEO] Power Pages Actions - Authorization Configuration Improvements" /&gt;&lt;p&gt;Custom APIs offer a lot of flexibility, but cannot be called directly from PowerPages. With this video and the accompanying repository/solution, this is going to change! We are going to call custom functionality with an interface that is similar to what you know from the JavaScript of Model-Driven Apps.&lt;/p&gt;
&lt;p&gt;This time we are discovering some improvements I made to configuring authorization.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/O39U-tgh4as&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/O39U-tgh4as&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-auth-improvements/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to leverage Security scan in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/securityscan/</link>
        <pubDate>Thu, 22 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/securityscan/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/securityscan/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to leverage Security scan in Power Pages?" /&gt;&lt;p&gt;Security Scan is a new feature checking your Power Page for known security issues. A great tool in our box of quick and easy checkups!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/JCWzkCALu8g&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/JCWzkCALu8g&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/securityscan/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Pages Actions: Pass any Parameter</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api-parameters/</link>
        <pubDate>Sun, 18 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api-parameters/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-parameters/cover.jpg" alt="Featured image of post Power Pages Actions: Pass any Parameter" /&gt;&lt;p&gt;Previous videos showed passing &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/custom-api/&#34; &gt;a string&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/custom-api-bound/&#34; &gt;an entity reference&lt;/a&gt;, but the other parameters are missing an explanation. And while for some types, like integer, this might not be needed, but with complex types, like EntityCollection, it&amp;rsquo;s not that clear. So here is a reference for all possible inputs for Custom APIs and their respective outputs.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;general-concepts&#34;&gt;General concepts&lt;/h2&gt;
&lt;p&gt;The Actions Adapter needs to guess your input, so you will find, that for complex types, there is something chosen to identify them correctly. For example, both OptionSetValue (Picklist) and Money have a Value property in their definition in C#. But how would the Actions Adapter would decide what &lt;code&gt;{ Value: 3 }&lt;/code&gt; is?* So instead &lt;code&gt;{ OptionSetValue: 3 }&lt;/code&gt; and &lt;code&gt;{ Money: 3 }&lt;/code&gt; make for a clear differentiation.&lt;/p&gt;
&lt;p&gt;This concept does not apply to the outputs. They are pretty much left untouched and passed as is. So both an OptionSetValue as well as a Money field might be passed back to you as &lt;code&gt;{ Value: 3 }&lt;/code&gt; assuming you know by the name of the output what field type is behind this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;*: The adapter could evaluate the Metadata to be sure, but that would have a performance impact with additional queries to Dynamics.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Should the automatic parsing fail to recognize a type due to the value matching multiple types (e.g. &amp;ldquo;5&amp;rdquo; can be an integer, but also a string), the correct type can be annotated with an @, see the respective section (&amp;ldquo;Parser overwrite&amp;rdquo;) for the correct name and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/custom-api-parser/&#34; &gt;this article&lt;/a&gt; for more details. Sample:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// likely to be misinterpreted as Integer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   String&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Will be passed to the Custom API as String
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String@string&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;field-types&#34;&gt;Field Types&lt;/h2&gt;
&lt;p&gt;For this article, I created a simple Custom API that returns all inputs to the outputs. For every type, there was an input and output defined named like the type. So both the &lt;code&gt;Custom API Request Parameter&lt;/code&gt; and &lt;code&gt;Custom API Response Property&lt;/code&gt; for the type &lt;code&gt;String&lt;/code&gt; are called &lt;em&gt;String&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The following sections will show an example of an input object passing exactly one &lt;code&gt;Custom API Request Parameter&lt;/code&gt; and the construction of an output object with the corresponding &lt;code&gt;Custom API Response Property&lt;/code&gt;. Some sections may contain additional explanations.&lt;/p&gt;
&lt;h3 id=&#34;string&#34;&gt;String&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   String&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    String&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @string&lt;/p&gt;
&lt;h3 id=&#34;stringarray&#34;&gt;StringArray&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;StringArray&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;StringArray&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;guid&#34;&gt;Guid&lt;/h3&gt;
&lt;p&gt;If a string is parsable as Guid, it will be converted accordingly.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Guid&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00000000-0000-0000-0000-000000000001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Guid&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00000000-0000-0000-0000-000000000001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @guid&lt;/p&gt;
&lt;h3 id=&#34;integer&#34;&gt;Integer&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Integer&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Integer&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @int&lt;/p&gt;
&lt;h3 id=&#34;float&#34;&gt;Float&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Float&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Float&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @float&lt;/p&gt;
&lt;h3 id=&#34;decimal&#34;&gt;Decimal&lt;/h3&gt;
&lt;p&gt;To mark something as a decimal, pass it as a string with an &amp;ldquo;m&amp;rdquo; at the end.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Decimal&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10.4m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Decimal&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10.4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @decimal&lt;/p&gt;
&lt;h3 id=&#34;boolean&#34;&gt;Boolean&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Boolean&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Boolean&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @bool&lt;/p&gt;
&lt;h3 id=&#34;datetime&#34;&gt;DateTime&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;DateTime&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2024-08-03T15:27:41Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;DateTime&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2024-08-03T15:27:41Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @datetime&lt;/p&gt;
&lt;h3 id=&#34;picklist&#34;&gt;Picklist&lt;/h3&gt;
&lt;p&gt;Notice here that the required property for the input is &lt;code&gt;OptionSetValue&lt;/code&gt; while in the output it is called &lt;code&gt;Value&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Picklist&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;OptionSetValue&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// or
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Picklist@picklist&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Picklist&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @picklist&lt;/p&gt;
&lt;h3 id=&#34;money&#34;&gt;Money&lt;/h3&gt;
&lt;p&gt;Notice here that the required property for the input is &lt;code&gt;Money&lt;/code&gt; while in the output it is called &lt;code&gt;Value&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Money&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Money&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// or
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Money@money&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Money&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Parser overwrite: @money&lt;/p&gt;
&lt;h3 id=&#34;entityreference&#34;&gt;EntityReference&lt;/h3&gt;
&lt;p&gt;To be recognized as an EntityReference, the properties &lt;code&gt;LogicalName&lt;/code&gt; and &lt;code&gt;Id&lt;/code&gt; should be present.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityReference&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;LogicalName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4121902e-0530-ef11-8409-6045bd9e7366&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityReference&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4121902e-0530-ef11-8409-6045bd9e7366&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;LogicalName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;KeyAttributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;RowVersion&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;entity&#34;&gt;Entity&lt;/h3&gt;
&lt;p&gt;To be recognized as an Entity, the properties &lt;code&gt;LogicalName&lt;/code&gt; and &lt;code&gt;Attributes&lt;/code&gt; should be present. &lt;code&gt;Id&lt;/code&gt; is optional but is displayed here as a common use case.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Entity&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;LogicalName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4121902e-0530-ef11-8409-6045bd9e7366&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Attributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;firstname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Marius&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;lastname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wodtke&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Entity&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4121902e-0530-ef11-8409-6045bd9e7366&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;LogicalName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Attributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {           
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;firstname&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Marius&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lastname&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wodtke&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;FormattedValues&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;KeyAttributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;RelatedEntities&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;RowVersion&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;entitycollection&#34;&gt;EntityCollection&lt;/h3&gt;
&lt;p&gt;To be recognized as an EntityEntityCollection, the properties &lt;code&gt;EntityName&lt;/code&gt; and &lt;code&gt;Entities&lt;/code&gt; should be present. &lt;code&gt;Entities&lt;/code&gt; then contains &lt;a class=&#34;link&#34; href=&#34;#entity&#34; &gt;Entity objects as seen above&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityCollection&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Entities&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;LogicalName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;Id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;userid&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;Attributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;firstname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Marius&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;lastname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wodtke&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Entities&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4121902e-0530-ef11-8409-6045bd9e7366&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;LogicalName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Attributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {           
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;firstname&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Marius&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lastname&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wodtke&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;FormattedValues&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;KeyAttributes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [], 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;RelatedEntities&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;RowVersion&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;EntityName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;MinActiveRowVersion&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;MoreRecords&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;PagingCookie&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;TotalRecordCount&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;TotalRecordCountLimitExceeded&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Power Pages Actions solution can handle all possible input types. Some types like Decimal might need special handling and complex parameters like Entity need extra care to ensure that the properties are named correctly.&lt;/p&gt;
&lt;p&gt;The solution can hand out all possible output parameters. Here the properties are what you would find in C#.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Site visibility in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/sitevisibility/</link>
        <pubDate>Fri, 09 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/sitevisibility/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/sitevisibility/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Site visibility in Power Pages" /&gt;&lt;p&gt;This time in the Power Pages Quick Tips Series we discuss the Site Visibility, what is it and when you should use it. Learn about an important feature for your GoLive in less than 10 minutes.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/4TPogYXR7pw&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/4TPogYXR7pw&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/sitevisibility/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Timeline Custom Connectors 2: Retrieving Data</title>
        <link>https://www.marius-wodtke.de/post/timeline/custom/data/</link>
        <pubDate>Sun, 04 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/timeline/custom/data/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/data/cover.jpg" alt="Featured image of post Timeline Custom Connectors 2: Retrieving Data" /&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/timeline/custom/basics/&#34; &gt;The first post&lt;/a&gt; just displayed a dummy record, not actually that useful. This time we will integrate an entity that would usually not be a fit for the timeline. I initially wanted to use the opportunity, as seeing the opportunities of a customer in their timeline might be a valid request from a sales organization. But my Development Environments of course can&amp;rsquo;t install the Sales module, so instead we will show the contacts of the account. Since it&amp;rsquo;s on the OOTB account form right beside the timeline anyway we can easily verify our results.&lt;/p&gt;
&lt;h2 id=&#34;a-new-class&#34;&gt;A new class&lt;/h2&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;The template&lt;/a&gt; already includes webpack, so we can just create a new file (&lt;em&gt;ContactData.ts&lt;/em&gt;) with a class that we will include in our &lt;code&gt;MyDataSource&lt;/code&gt; with an import statement.&lt;/p&gt;
&lt;p&gt;First, we need to retrieve the contacts from Dataverse. We can use &lt;code&gt;Xrm.WebApi.retrieveMultiple&lt;/code&gt; for that or use the context given to us in the &lt;code&gt;init&lt;/code&gt; method, it also includes the WebApi interface. I will use the context here to showcase its usage and I assume it&amp;rsquo;s the preferred option by MS (but this is pure speculation because, at the time of writing, there is no best practice communicated).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getData&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecord&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;[]&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;accountId&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;page&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`$filter=_parentcustomerid_value eq &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;accountId&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`$select=contactid,fullname,modifiedon,emailaddress1`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;contacts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;webAPI&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;retrieveMultipleRecords&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;`?&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;select&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;contacts&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entities&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;mapContact&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we want to map the returned contact to our &lt;code&gt;IRecord&lt;/code&gt; interface.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// In Record.ts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecord&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;email&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;date&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// In ContactData.ts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapContact&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;contact&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecord&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;contact.contactid&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;contact.fullname&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;email&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;contact.emailaddress1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;date&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;contact.modifiedon&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, you could name the properties more similar to the original fields of contact and if you are using earlybounds, you may skip &lt;code&gt;IRecord&lt;/code&gt;, since you will have a generated type that you can use. However, in the next code snippet, the data property is a string that we will fill with &lt;code&gt;JSON.stringify&lt;/code&gt;. We will need to deserialize this JSON again in other parts of the code, so it&amp;rsquo;s a good idea to agree on a common ground on what is in the data property and that&amp;rsquo;s exactly what &lt;code&gt;IRecord&lt;/code&gt; achieves.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordsData&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecordsDataRequest&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordData&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;[]&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getData&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;mapRecord&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecordData&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;recordData&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;JSON.stringify&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;record&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;record.id&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;sortDateValue&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;record.date&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;recordData&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can find the full class &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/DataverseContactData/src/ContactData.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;changes-to-mydatasource&#34;&gt;Changes to MyDataSource&lt;/h2&gt;
&lt;p&gt;In the original class, the &lt;code&gt;getRecordsData&lt;/code&gt; method changes, it will now call our new class and return its results. Notice here, how the results are cached in &lt;code&gt;this.records&lt;/code&gt;, &lt;code&gt;getRecordsData&lt;/code&gt; is called with every filtering and search action as well and it just makes sense to filter in memory as it will be a smoother experience for the user. I also want to mention that I had trouble with reloading data when filtering, however, the issues of just receiving a never-ending loading icon were not reproducible anymore when preparing this article.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;ContactData&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./ContactData&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordsData&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecordsDataRequest&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;filter?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IFilterRequest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordsDataResponse&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;contactData&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordsData&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;requestId&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;request.requestId&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.records&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some other parts will also change, for example, I changed the icon to the matching &lt;code&gt;IconOption.ContactEntityIcon&lt;/code&gt;, the header of the card will show the name and the body will show the email.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;createHeader&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;factory&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Label&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordSourceInfo&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_header`&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;createBody&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecord&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;factory&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Label&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordSourceInfo&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;recordId&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;_body`&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;email&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can find the full class &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/DataverseContactData/src/MyRecordSource.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The result&lt;/h2&gt;
&lt;p&gt;If we open an account now, with the form selected where our custom connector is configured for the timeline, the timeline now displays the contacts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/data/records.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;As teased in the beginning, we can nicely see that the results are correct by just comparing them to the &lt;em&gt;related contacts&lt;/em&gt; grid on the right.&lt;/p&gt;
&lt;p&gt;If you want to take a look at the full code of this sample, check out &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/tree/DataverseContactData&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the DataverseContactData branch&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Since we don&amp;rsquo;t need to handle any secrets, showing data from Dataverse in the timeline is straightforward. This can enable you to fulfill requirements to show data in the timeline which usually would not be a fit for example because it&amp;rsquo;s not an activity entity. You should of course question the requirement, is it really needed? Because we need to invest time coding the connector and will have future costs for maintaining a custom component. Right now, this simple sample is already 150 lines of code, and we haven&amp;rsquo;t handled any filtering yet. However, sometimes there are strong business reasons to not use a standard grid and then we have a great tool in our toolbox.&lt;/p&gt;
&lt;p&gt;For showing custom data, add a class/method to &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the template&lt;/a&gt; to retrieve the data using &lt;code&gt;WebApi.retrieveMultiple&lt;/code&gt;. Next, map the data to an interface like &lt;code&gt;IRecord&lt;/code&gt; and serialize the records to the &lt;code&gt;IRecordData&lt;/code&gt; interface. Return these serialized records from the &lt;code&gt;getRecordsData&lt;/code&gt;. You will receive them back in &lt;code&gt;getRecordUX&lt;/code&gt; where you may deserialize your records again and build a card form showing the data, comprising of header, body and footer.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Set up the Google Identity provider in Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/identity-google/</link>
        <pubDate>Fri, 02 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/identity-google/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/identity-google/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Set up the Google Identity provider in Power Pages?" /&gt;&lt;p&gt;Power Pages has multiple Identity providers for handling Authentication and Google accounts can be used as such. Learn here how to set it up to enable login with a Google account to for example allow your customers to login to your Power Page.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/dgjgPMHuGVk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/dgjgPMHuGVk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/identity-google/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Highlights from Wave2 2024</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-07/</link>
        <pubDate>Mon, 29 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-07/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-07/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Highlights from Wave2 2024" /&gt;&lt;p&gt;New wave, new Power Platform Learners. Everyone picked their favorite feature(s) of the wave update. And because we are so diverse those features are distributed across Sales, Service, Copilot and Power Pages, so a colorful bouquet for you to choose from.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/Eskqu0sXaRA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/Eskqu0sXaRA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-07/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Disable anonymous access on Power Page</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/anonymous-access/</link>
        <pubDate>Thu, 25 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/anonymous-access/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/anonymous-access/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Disable anonymous access on Power Page" /&gt;&lt;p&gt;&amp;ldquo;Disable anonymous access&amp;rdquo;, a feature easily misunderstood by reading the title. After you enable this, anonymous users will still be able to access the site, but they won&amp;rsquo;t be able to read data from Dataverse. Learn more in this Power Pages quick tip.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/-Ud-pd3fS_8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/-Ud-pd3fS_8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/anonymous-access/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Timeline Custom Connectors 1: Basics</title>
        <link>https://www.marius-wodtke.de/post/timeline/custom/basics/</link>
        <pubDate>Sun, 21 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/timeline/custom/basics/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/basics/cover.jpg" alt="Featured image of post Timeline Custom Connectors 1: Basics" /&gt;&lt;p&gt;Did you know you can &lt;em&gt;bring your own data&lt;/em&gt; to the Dynamics timeline component? In general, the timeline is quite powerful with &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/set-up-timeline-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;its configuration options&lt;/a&gt; in a no-code scenario already. But it&amp;rsquo;s also limited to the standard entities of the timeline: Notes, Posts and Activities. With these, we can work around, e.g. inserting posts for the creation of another entity or just defining an entity as an activity in the first place.&lt;/p&gt;
&lt;p&gt;But sometimes this is not feasible, e.g. due to the security setup with activities or you are working with external data that you prefer to not persist. A good example is news articles. They just have less footprint in the system if they are queried the moment the seller opens the Account and compared to a nightly creation of post records, the news will always be up to date.&lt;/p&gt;
&lt;h2 id=&#34;the-feature&#34;&gt;The Feature&lt;/h2&gt;
&lt;p&gt;But getting there will be quite a journey. First of all, I&amp;rsquo;m talking about a feature Microsoft calls &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/custom-connectors-timeline-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;em&gt;Custom Connectors with the timeline control&lt;/em&gt;&lt;/a&gt;. This is a high-code extension that you will find in the &lt;em&gt;Advanced&lt;/em&gt; section of the Timeline control settings. You enter the unique name of a webresource and the name of the primary class.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/basics/register1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The section for custom connectors is found in Advanced&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/basics/register2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The unique name of the webresource and the (full) name of the entrypoint is needed&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;This class you are specifying needs to implement a certain interface called &lt;code&gt;IRecordSource&lt;/code&gt; which is found in &lt;a class=&#34;link&#34; href=&#34;https://github.com/microsoft/PowerApps-Samples/blob/master/model-driven-apps/custom-connectors/SecondaryRecordSource/solution/Interfaces.d.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Interfaces.d.ts&lt;/a&gt; in the sample linked at the bottom of the article. This requires you to implement 4 (+ 2 optional) methods.&lt;/p&gt;
&lt;h2 id=&#34;init&#34;&gt;init&lt;/h2&gt;
&lt;p&gt;This is the first bummer. The sample (at the time of writing) does not include a definition for the given context since the referenced &lt;code&gt;mscrm.d.ts&lt;/code&gt; is missing. We will try to fix that later, but you could type it into &lt;code&gt;any&lt;/code&gt; here and just save it to a class property/field.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;config?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;JSON&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;void&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The context contains a lot of functions, way too many to list here. But check the template repository linked at the end, the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/main/src/types/Interfaces.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Interfaces.ts&lt;/a&gt; contains a partially defined &lt;code&gt;IControlData&lt;/code&gt; interface, showing a lot of the capabilities.&lt;/p&gt;
&lt;p&gt;Apart from that you have static namespaces like Xrm.WebApi or Xrm.Utility available.&lt;/p&gt;
&lt;h2 id=&#34;getrecordsourceinfo&#34;&gt;getRecordSourceInfo&lt;/h2&gt;
&lt;p&gt;This is a very simple function, it just returns an object with a name property, done. No special requirements for the name, but it should correspond to the module aka the connector you are implementing. I&amp;rsquo;ve used the class name as the default.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordSourceInfo&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IRecordSourceInfo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;MyRecordSource.name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;getrecordsdata&#34;&gt;getRecordsData&lt;/h2&gt;
&lt;p&gt;This function is called onLoad, on refresh and whenever the filters or search is used. It shall return the data based on the filters (if present). Here is the absolute minimal extract.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-TS&#34; data-lang=&#34;TS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getRecordsData&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IRecordsDataRequest&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;filter?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;IFilterRequest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;IRecordsDataResponse&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; [{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;JSON.stringify&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Record 1&amp;#34;&lt;/span&gt; }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;sortDateValue&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Date()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }]; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;requestId&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;request.requestId&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;records&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;this.records&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will cover an actual sample in the next article about displaying data.&lt;/p&gt;
&lt;h2 id=&#34;getrecordux&#34;&gt;getRecordUX&lt;/h2&gt;
&lt;p&gt;This function is called for every record you return from &lt;code&gt;getRecordsData&lt;/code&gt;. Here you transform the data into something displayable in the timeline. Important parts are Header, Body and Footer, composing the information in the card, an icon and the &lt;code&gt;sortDateValue&lt;/code&gt; for ordering.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/basics/record.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;No code sample here, we cover this in the next article about displaying data. But the template contains the necessary parts for this &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector/blob/main/src/MyRecordSource.ts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;in this class&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, you can add commands to each record, which is probably another topic in itself, so expect this series to grow quite a bit.&lt;/p&gt;
&lt;h2 id=&#34;getfilterdetails&#34;&gt;getFilterDetails&lt;/h2&gt;
&lt;p&gt;This function gets passed in the filters and shall return the (possibly manipulated) filters. Manipulations might for example be altering an existing filter by adding an option or creating a new filter that fits your extension.&lt;/p&gt;
&lt;p&gt;Implementing this is quite complex since you need to consider the existing filters, need to prevent illegal combinations and calculate the count of records matching a filter in combination with all already selected filters. We will have to cover this in a dedicated article, but at least this is one of the optional functions, so we can just ignore it for now.&lt;/p&gt;
&lt;h2 id=&#34;getrecordcreate&#34;&gt;getRecordCreate&lt;/h2&gt;
&lt;p&gt;This function controls what is being shown at the + for creating new records. The objects you return contain an onClick function that is executed as soon as the added option is clicked. You only need to implement the buttons you want to add, options available by the regular configuration options of the timeline are shown regardless. So if you do not intend to add a button, you may skip the implementation of this function.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/timeline/custom/basics/create.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;There is no code sample here, we will have another dedicated article for it.&lt;/p&gt;
&lt;h2 id=&#34;the-template&#34;&gt;The Template&lt;/h2&gt;
&lt;p&gt;An obvious problem with this feature (at the time of writing) is the lack of samples. &lt;a class=&#34;link&#34; href=&#34;https://github.com/microsoft/PowerApps-Samples/tree/master/model-driven-apps/custom-connectors/SecondaryRecordSource/solution&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;The provided samples&lt;/a&gt; are compiled already, the definitions are not complete and &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/custom-connectors-timeline-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the docs article&lt;/a&gt; lacks explanations on the required functions. I try to fix this on this blog by providing more context around the functions but we will also have samples.&lt;/p&gt;
&lt;p&gt;These samples will be built on a template, which is found in &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this repository&lt;/a&gt;. The plan is that the main branch holds the template for easy cloning/forking/copying and each sample receives a separate branch.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Timeline Custom Connectors are a high-code extension configurable for the Dynamics timeline, shown on forms. It allows for a custom experience of records being shown in the timeline, with no constraint on what is shown, even custom commands are implementable. However, a considerable amount of code is required to leverage these capabilities!&lt;/p&gt;
&lt;p&gt;The template in &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this repository&lt;/a&gt; tries to improve your time to &amp;ldquo;getting started&amp;rdquo; and you should check &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/timeline/custom/&#34; &gt;the series&lt;/a&gt; for more samples. If certain functionality is still missing on this blog you may be able to reverse engineer them from the &lt;a class=&#34;link&#34; href=&#34;https://github.com/microsoft/PowerApps-Samples/tree/master/model-driven-apps/custom-connectors/SecondaryRecordSource/solution&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MS samples&lt;/a&gt;.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Power Pages Authentication Providers</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/auth/</link>
        <pubDate>Wed, 17 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/auth/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/auth/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Power Pages Authentication Providers" /&gt;&lt;p&gt;We need to talk about authentication and before we do so, we need to settle on the basics and the authentication providers enabled by default. In this tip, Ambesh takes us through fundamental terms and options.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ZZZGBEX_T1I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ZZZGBEX_T1I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/auth/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Create Power Pages content with Copilot</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/copilot-edit/</link>
        <pubDate>Wed, 10 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/copilot-edit/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/copilot-edit/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Create Power Pages content with Copilot" /&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/website-create-copilot/&#34; &gt;In another tip&lt;/a&gt;, the Power Pages quick tips series already covered the creation of a website with copilot. But you can also create and edit pages inside the website.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/AtyCuMeZS4Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/AtyCuMeZS4Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/copilot-edit/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Service Bus 6: Processing Events with a Function</title>
        <link>https://www.marius-wodtke.de/post/servicebus/function/</link>
        <pubDate>Sun, 07 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/servicebus/function/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/cover.jpg" alt="Featured image of post Service Bus 6: Processing Events with a Function" /&gt;&lt;p&gt;We already covered &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/&#34; &gt;a webjob&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/console/&#34; &gt;a plain console app&lt;/a&gt; as processors. This time we will take a closer look at &lt;em&gt;Azure Functions&lt;/em&gt;. These are interesting as a consumption-based processing option, apart from that you will recognize many of the patterns applied in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/&#34; &gt;a webjob&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;general-preparations&#34;&gt;General preparations&lt;/h2&gt;
&lt;p&gt;You can find the full project code &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365ServiceBusProcessors/tree/main/DataverseEventProcessorFunction&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Something else often forgotten in this kind of article is the description of the used dependencies (here NuGets):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt; =&amp;gt; Definitions of&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Azure.Functions.Worker&lt;/code&gt; =&amp;gt; Bindings to Service Bus&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Logging&lt;/code&gt; =&amp;gt; Used for logging output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.PowerPlatform.Dataverse.Client&lt;/code&gt; =&amp;gt; RemoteExecutionContext class for Deserialization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And of course, check out &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/exporting-events/&#34; &gt;the first post&lt;/a&gt; of this series where we set up the service bus and our possibility of sending events to this service bus. So by now every time you create an account, a message should pop up in your subscription.&lt;/p&gt;
&lt;h2 id=&#34;the-project&#34;&gt;The project&lt;/h2&gt;
&lt;p&gt;This time, the setup of the project is a little more involved, we have a wizard. The first stages of the project type &lt;em&gt;Azure Functions&lt;/em&gt; are as usual, but then we define your first trigger and here we will select &lt;em&gt;Service Bus Topic Trigger&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Setup1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Setup2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Again we get started with 2 classes, Program.cs and Function1.cs, I&amp;rsquo;ll start with Program.cs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cs&#34; data-lang=&#34;cs&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Azure.Core;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Azure.Identity;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Azure.Functions.Worker;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Extensions.Configuration;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Extensions.DependencyInjection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Extensions.Hosting;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.PowerPlatform.Dataverse.Client;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Xrm.Sdk;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; host = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HostBuilder()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .ConfigureFunctionsWebApplication()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .ConfigureServices(services =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        services.AddApplicationInsightsTelemetryWorkerService();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        services.ConfigureFunctionsApplicationInsights();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        services.AddTransient&amp;lt;IOrganizationService&amp;gt;(_ =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            IConfiguration configuration = _.GetService&amp;lt;IConfiguration&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; dataverseUrl = configuration[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DataverseUrl&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ServiceClient(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Uri(dataverseUrl),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; _ =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#if&lt;/span&gt; DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cred = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; VisualStudioCredential(); &lt;span style=&#34;color:#75715e&#34;&gt;// DefaultAzureCredential is painfully slow in local debugging.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cred = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; DefaultAzureCredential();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; token = &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; cred.GetTokenAsync(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; TokenRequestContext([&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{dataverseUrl}/.default&amp;#34;&lt;/span&gt;]), CancellationToken.None);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; token.Token;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Build();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;host.Run();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice here how I&amp;rsquo;ve sneaked an authentication for a managed identity, &lt;a class=&#34;link&#34; href=&#34;https://community.dynamics.com/blogs/post/?postid=09f639ba-5134-4bd1-8812-04e019b7b920&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;read more about that here&lt;/a&gt;.
Apart from adding the &lt;code&gt;IOrganizationService&lt;/code&gt; the class stayed as it was set up by the project template.&lt;/p&gt;
&lt;p&gt;Next up is Function1.cs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cs&#34; data-lang=&#34;cs&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Azure.Functions.Worker;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Extensions.Logging;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Xrm.Sdk;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; DataverseEventProcessorFunction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Function1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;Function1&amp;gt; _logger;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Function1(ILogger&amp;lt;Function1&amp;gt; logger)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _logger = logger;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;        [Function(nameof(Function1))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; Task Run(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;            [ServiceBusTrigger(&amp;#34;dataverse&amp;#34;, &amp;#34;account-export&amp;#34;, Connection = &amp;#34;AzureServiceBus&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            RemoteExecutionContext message)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _logger.LogInformation(message.MessageName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This looks pretty similar to what we had with the webjob, the &lt;code&gt;ServiceBusTrigger&lt;/code&gt; attribute is registered on startup and connects the function to the service bus. Of course, you may rename the function!&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget to check the authentication setup for the service bus, it is identical to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/#handling-authentication&#34; &gt;the webjob&lt;/a&gt;. All the other configurations may also be taken from that post, but be aware, that Functions do not support nested configurations, this means we will need to specify our service bus configuration like this &lt;code&gt;  &amp;quot;AzureServiceBus__fullyQualifiedNamespace&amp;quot;: &amp;quot;mariuswodtke-dev.servicebus.windows.net&amp;quot;&lt;/code&gt; in the local.settings.json.&lt;/p&gt;
&lt;h2 id=&#34;setting-up-the-function-app&#34;&gt;Setting up the Function App&lt;/h2&gt;
&lt;p&gt;Much easier in this case with the Azure Portal, but surely we will follow up someday with a bicep automation. A special thing here is that the wizard will auto-create 3 more resources, an Application Insights, Storage Account and App Service Plan. The latter might surprise, given that we will host on a consumption basis, but inspecting the App Service Plan reveals, that its SKU Y1 is for consumption purposes. I encourage the consumption here unless there are special requirements because scaling happens based on the number of events, which is perfect for such a service bus use case, where the Function will scale based on the messages ready to process.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Azure1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Azure2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;deploying&#34;&gt;Deploying&lt;/h2&gt;
&lt;p&gt;This works pretty much like &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/#deploying&#34; &gt;the webjob&lt;/a&gt;, so I will just run with the pictures for some guidance here.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Publish1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Publish2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Publish3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Publish4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/PublishProfile.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;result&#34;&gt;Result&lt;/h2&gt;
&lt;p&gt;You can look at the logs via the &lt;em&gt;Overview&lt;/em&gt; page of the Function. Besides your function, there is a button &lt;em&gt;Invocations and More&lt;/em&gt;, which will take you to the most recent executions. Keep in mind that these are provided through the Application Insights, which means they will be lagging by a few seconds to minutes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/Functions.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;But right now we should not see anything here, even if there are already messages in the service bus. This is because we first need to configure the environment variables.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/function/EnvironmentVariables.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And from here on out, we will see the same output as &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/#result&#34; &gt;the webjob&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Azure Functions are a great choice for our service bus message processor due to the free quotas, event-based scaling and the good Visual Studio template, which allows for a really short time-to-deployed-prototype. If you are already familiar webjobs, the triggers in functions will be very familiar to you and also setup and deployment is very similar. The big disadvantage historically was that functions only supported .Net Core. But with the general availability of &lt;code&gt;Microsoft.PowerPlatform.Dataverse.Client&lt;/code&gt;, that is no longer a problem!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Using Power Fx in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/powerfx/</link>
        <pubDate>Thu, 04 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/powerfx/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/powerfx/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Using Power Fx in Power Pages" /&gt;&lt;p&gt;Today Ambesh shows us how to use Power Fx in Power Pages, showing dynamic content on a page.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/Nf1IpkZPnLA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/Nf1IpkZPnLA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/powerfx/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Calling Bound APIs with the Power Pages Actions</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api-bound/</link>
        <pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api-bound/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-bound/cover.jpg" alt="Featured image of post [VIDEO] Calling Bound APIs with the Power Pages Actions" /&gt;&lt;p&gt;Custom APIs offer a lot of flexibility, but cannot be called directly from PowerPages. With this video and the accompanying repository/solution, this is going to change! We are going to call custom functionality with an interface that is similar to what you know from the JavaScript of Model-Driven Apps.&lt;/p&gt;
&lt;p&gt;This time we are covering how to call bound APIs.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/2Q7ox1fyci0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/2Q7ox1fyci0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-bound/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Copilot, Copilot Studio and C# 13 latest updates</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-05/</link>
        <pubDate>Tue, 25 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-05/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-05/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Copilot, Copilot Studio and C# 13 latest updates" /&gt;&lt;p&gt;The May edition of the Power Platform Learners Group brings some of our favorite features and announcements from MS Build. Lots of it was of course dedicated to Copilot and building our Copilots with Copilot Studio, but for example, I grabbed some of the new features in C# to show and Jörgen had something from Canvas Apps.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/eAl2DGkAV28&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/eAl2DGkAV28&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-05/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Triggering a Flow from the Ribbon (multiple records)</title>
        <link>https://www.marius-wodtke.de/post/pattern/ribbon-flow-multi/</link>
        <pubDate>Sun, 23 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/ribbon-flow-multi/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/cover.jpg" alt="Featured image of post Patterns: Triggering a Flow from the Ribbon (multiple records)" /&gt;&lt;p&gt;Of course, we can easily boost the productivity of users by letting them apply logic to multiple records at once! Think about the requirement &amp;ldquo;I need to confirm close to 100 orders per day, it is sufficient to check plausibility in the View, I don&amp;rsquo;t want to navigate every single order confirming them on the form!&amp;rdquo;&lt;br&gt;
This sounds like a good fit for our pattern here, the user clicks a button and the Flow connected to it then executes the logic, like firing a &lt;em&gt;FulfillOrderRequest&lt;/em&gt; (confused here? Check &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/pattern/ribbon-flow/&#34; &gt;the previous article&lt;/a&gt;). Just that the user would still need to confirm the records one by one from the grid&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;button-visibility&#34;&gt;Button Visibility&lt;/h2&gt;
&lt;p&gt;Before we go further with the button functionality I first want to take a little side step that I should have already explained in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/pattern/ribbon-flow/&#34; &gt;the last article&lt;/a&gt;. You can control when a button is shown based on the amount of records being selected in a grid. This makes sense for the Home Grid button that we implemented last time to just show it if exactly one record is selected. For this article of course we will require at least one selected record. This can be enforced by the Enable Rule &lt;em&gt;SelectionCountRule&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/SelectionCountRule.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;For some buttons that work on selected records, some records should be selected for the button to be shown!&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;multiple-selected-records&#34;&gt;Multiple Selected Records&lt;/h2&gt;
&lt;p&gt;For selecting multiple records, we first need to make a little change to our JS. It is now possible that the records to update is an array and we need to update for each. I will simply paste the full code here because quite some changes revolve about the handling of the success message, which will only be given once per execution, while errors will be handled one by one.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TriggerFlow&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;successMessageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessageForUser&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;successMessageForUser&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;successMessageForUser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Operation successfully started!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessageForUser&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessageForUser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Starting Operation ...&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;showProgressIndicator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessageForUser&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArray&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;UpdateRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;UpdateRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityIds&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Promise.&lt;span style=&#34;color:#a6e22e&#34;&gt;all&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closeProgressIndicator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;successMessageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .&lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closeProgressIndicator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// No output here, it is done per error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;UpdateRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;updateEntity&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;updateEntity&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Date();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WebApi&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;updateRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;updateEntity&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// No output here, it is done in total
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also, we have a new optional parameter for the message being displayed in the progress indicator (aka loading spinner), considering that the user might select quite a lot of records, which could lead to a wait time until the success message is visible.&lt;/p&gt;
&lt;p&gt;In the Command in Ribbon Workbench, instead of passing &lt;em&gt;FirstSelectedItemId&lt;/em&gt;, we can now simply pass &lt;em&gt;SelectedControlSelectedItemIds&lt;/em&gt;, the rest can stay identical to the values in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/pattern/ribbon-flow/&#34; &gt;the first article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/Selected.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The second parameter for the record id(s) changed.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;all-visible-records&#34;&gt;All (Visible) Records&lt;/h2&gt;
&lt;p&gt;We can also use the parameter &lt;em&gt;SelectedControlAllItemIds&lt;/em&gt; to get all records shown. Notice that in this situation we would configure the button visibility to have a selection of 0 records.&lt;/p&gt;
&lt;p&gt;This is of limited use in my opinion because it has rather limited use for the user. He cannot easily exclude records and on the other hand it will also only include the records on the current page. He could achieve the same by using the &amp;ldquo;Select All&amp;rdquo; checkbox at the grid and then using the button with &lt;em&gt;SelectedControlSelectedItemIds&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/All.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The second parameter for the record id(s) changed again.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;In testing, I&amp;rsquo;ve noticed that the &amp;ldquo;New Look&amp;rdquo; ignores the &lt;em&gt;Records Per Page&lt;/em&gt; setting and dynamically renders 50 new records as you scroll down. &lt;em&gt;SelectedControlAllItemIds&lt;/em&gt; then returns the first 50 items no matter the &lt;em&gt;Records Per Page&lt;/em&gt; setting until you start scrolling, again a very uncontrolled scenario.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/PersonalOptions.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;all-records&#34;&gt;All Records&lt;/h2&gt;
&lt;p&gt;So, let&amp;rsquo;s discuss a real &amp;ldquo;All Records&amp;rdquo; option.&lt;/p&gt;
&lt;p&gt;For this, we will leverage the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/grids/gridcontrol/getfetchxml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;em&gt;getFetchXml&lt;/em&gt;&lt;/a&gt; function from the grid client API. We will then have to iterate through all pages and update all records found.&lt;/p&gt;
&lt;p&gt;Some more changes:&lt;br&gt;
The optional parameters were consolidated into a single JSON, we just need too many parameters at this point.&lt;br&gt;
This is partly because I also added a confirmation dialog. This is because this potentially enables the user to trigger a Flow on millions of records in a single click, which will likely lead to the flow hitting service limits and starting to throttle, significantly slowing the functionality for all other users as well. &amp;ldquo;With great power comes great responsibility&amp;rdquo;. The confirmation shall prevent accidentally missed filters and similar mistakes.
And lastly, the &lt;em&gt;retrieveAllRecords&lt;/em&gt; is grabbed &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/xrm-webapi/retrievemultiplerecords#fetchxml-example-online-scenario&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;from MS here&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TriggerFlowQuery&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;gridContext&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;optionsString&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;optionsString&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;successMessage&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;successMessage&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Operation successfully started!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessage&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessage&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Starting Operation ...&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmMessage&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmMessage&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Do you want to start the Operation on {count} records?&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmTitle&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmTitle&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Continue?&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gridContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getEntityName&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchXml&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gridContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getFetchXml&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ProcessAllRecords&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchXml&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ProcessAllRecords&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchXml&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;showProgressIndicator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessage&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;retrieveAllRecords&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchXml&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entities&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closeProgressIndicator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmStrings&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmMessage&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{count}&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entities&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmTitle&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;openConfirmDialog&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmStrings&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmed&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmed&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;showProgressIndicator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;workingMessage&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;entities&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;entity&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;UpdateRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entity&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Promise.&lt;span style=&#34;color:#a6e22e&#34;&gt;all&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;promises&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        .&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closeProgressIndicator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;successMessage&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        .&lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closeProgressIndicator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#75715e&#34;&gt;// No output here, it is done per error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;                        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }).&lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Utility&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;closeProgressIndicator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// No output here, it is done per error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now the last thing left is to define the command, where we will define &lt;em&gt;SelectedControl&lt;/em&gt; this time to provide the grid context.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/Query.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The messages shown can be customized optionally by providing a JSON with the properties confirmTitle, confirmMessage, workingMessage, successMessage&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow-multi/Dialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A dialog is displayed before the updates are being executed. The texts shown here are the defaults.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;powerfx&#34;&gt;PowerFX&lt;/h2&gt;
&lt;p&gt;Can you do something like this with PowerFX as well? At least partly, yes!&lt;/p&gt;
&lt;p&gt;So first let&amp;rsquo;s control the button visibility with&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;IsEmpty&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Selected&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AllItems&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then we can Patch the &lt;strong&gt;selected&lt;/strong&gt; records with&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ForAll&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Selected&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AllItems&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Patch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Test Table 2S&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ThisRecord&lt;/span&gt;, { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Trigger Field&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;() })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Notify&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Operation successfully started!&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;NotificationType&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Success&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, do not forget to replace the entity (&amp;lsquo;Test Table 2S&amp;rsquo;) and field (&amp;lsquo;Trigger Field&amp;rsquo;) with your values.&lt;/p&gt;
&lt;p&gt;This only works with the selected records, getting all records or the query of &amp;ldquo;Self&amp;rdquo; is not possible to my knowledge.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;From a few to thousands of records, the pattern can scale with some adaptions. Remember that you can use &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the helper solution&lt;/a&gt; to not have to fiddle with code! So the power of this pattern is just one solution installation away. After that, you can start configuring your ribbon command from the Ribbon Workbench.&lt;/p&gt;
&lt;p&gt;This pattern can also be used from Modern Commanding, but as usual, this is sadly more limited than the Workbench. Yet still, there are good chances the options of Modern Commanding might be a fit for your requirements.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to create Power Pages website using Copilot?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/website-create-copilot/</link>
        <pubDate>Sat, 22 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/website-create-copilot/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/website-create-copilot/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to create Power Pages website using Copilot?" /&gt;&lt;p&gt;Sweta and Ambesh show you some of the new capabilities of Copilot for Power Pages, creating a starter website with the chat.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/PagsJYlvYhQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/PagsJYlvYhQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/website-create-copilot/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to Customize appearances of AI-driven chatbot in the Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/copilot-appearance/</link>
        <pubDate>Mon, 17 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/copilot-appearance/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/copilot-appearance/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to Customize appearances of AI-driven chatbot in the Power Pages?" /&gt;&lt;p&gt;In this one Ambesh shows the integration of a chatbot from Copilot Studio and how you can change its looks.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/lp7gDX-Igms&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/lp7gDX-Igms&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/copilot-appearance/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Using Control Styles in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/control-styles/</link>
        <pubDate>Sun, 09 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/control-styles/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/control-styles/cover.jpg" alt="Featured image of post [VIDEO] Using Control Styles in Power Pages" /&gt;&lt;p&gt;Control Styles can alter the behavior of form fields in Power Pages. Some of them are tailored to gathering customer feedback, but others have a wide range of use cases like &amp;ldquo;Render Lookup as Dropdown&amp;rdquo; or &amp;ldquo;Code Component&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/9ERQ6xbIV3A&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/9ERQ6xbIV3A&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/control-styles/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Avoid the Creation Limit</title>
        <link>https://www.marius-wodtke.de/post/teams/creation-limit/</link>
        <pubDate>Sun, 02 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/creation-limit/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/creation-limit/cover.jpg" alt="Featured image of post Teams Integration: Avoid the Creation Limit" /&gt;&lt;p&gt;It might be you are here because your users are reporting that they cannot create any new Teams anymore or you followed &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/custom/&#34; &gt;the post about creating connected Teams with Power Automate&lt;/a&gt; and your technical user for the Connection Reference went on strike. Anyhow, here is the solution if there are reports about &amp;ldquo;AadGroupCreationLimitExceeded&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;symptom&#34;&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The Teams integration worked fine, but from one moment to the next, it stopped working, no matter the constellation of data, the user can&amp;rsquo;t make it work anymore. It even extends to creating a team in Teams itself.&lt;/p&gt;
&lt;p&gt;The error messages will contain some reference to &amp;ldquo;AadGroupCreationLimitExceeded&amp;rdquo;, for a flow, it might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BadRequest&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to execute Templates backend request CreateTeamFromTemplateRequest. Request Url: https://teams.microsoft.com/fabric/emea/templates/api/team, Request Method: POST, Response Status Code: BadRequest, ErrorMessage : {\&amp;#34;errors\&amp;#34;:[{\&amp;#34;message\&amp;#34;:\&amp;#34;Error when calling Middle Tier. Message: &amp;#39;&amp;#39;. Error code: &amp;#39;AadGroupCreationLimitExceeded&amp;#39;. Status code: BadRequest.\&amp;#34;,\&amp;#34;errorCode\&amp;#34;:\&amp;#34;Unknown\&amp;#34;}],\&amp;#34;operationId\&amp;#34;:\&amp;#34;abcd\&amp;#34;}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;innerError&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BadRequest&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to execute Templates backend request CreateTeamFromTemplateRequest. Request Url: https://teams.microsoft.com/fabric/emea/templates/api/team, Request Method: POST, Response Status Code: BadRequest, ErrorMessage : {\&amp;#34;errors\&amp;#34;:[{\&amp;#34;message\&amp;#34;:\&amp;#34;Error when calling Middle Tier. Message: &amp;#39;&amp;#39;. Error code: &amp;#39;AadGroupCreationLimitExceeded&amp;#39;. Status code: BadRequest.\&amp;#34;,\&amp;#34;errorCode\&amp;#34;:\&amp;#34;Unknown\&amp;#34;}],\&amp;#34;operationId\&amp;#34;:\&amp;#34;abcd\&amp;#34;}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;details&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error when calling Middle Tier. Message: &amp;#39;&amp;#39;. Error code: &amp;#39;AadGroupCreationLimitExceeded&amp;#39;. Status code: BadRequest.&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;target&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Templates&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2024-05-14TXX:XX:XX&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;request-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00000000-0000-0000-0000-000000000000&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;client-request-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00000000-0000-0000-0000-000000000000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cause&#34;&gt;Cause&lt;/h2&gt;
&lt;p&gt;There is &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/entra/identity/users/directory-service-limits-restrictions#:~:text=A%20non%2Dadmin%20user%20can%20create%20no%20more%20than%20250%20Microsoft%20Entra%20resources&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;a limit of 250 Entra Id objects&lt;/a&gt; that can be created by a single user. Every Teams Team creates a group in Entra, which is counted as an object. So as soon as you create 250 (or less if you also created other objects) Teams, you cannot create any more.&lt;/p&gt;
&lt;h2 id=&#34;resolution&#34;&gt;Resolution&lt;/h2&gt;
&lt;p&gt;You could get off this limit by deleting some Teams, but since they are soft deleted, they are still counted to your quota until they are finally gone after 30 days. Quite some time to wait if you hit the limit&amp;hellip;&lt;/p&gt;
&lt;p&gt;And especially with a technical user for a Power Automate Flow, deletion will simply not be possible, since there might be more than 250 Teams needed, so we need something different.&lt;br&gt;
One option could be the &lt;em&gt;Teams Service Administrator&lt;/em&gt; role that you could assign to the technical user as [described here](&lt;a class=&#34;link&#34; href=&#34;https://medium.com/365uc/non-admin-users-cannot-create-more-than-250-teams-8ce20f0d9bb2#:~:text=new%20roles%2C%20the-,Teams%20Service%20Administrator%20role,-includes%20%28amongst%20other%29&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://medium.com/365uc/non-admin-users-cannot-create-more-than-250-teams-8ce20f0d9bb2#:~:text=new%20roles%2C%20the-,Teams%20Service%20Administrator%20role,-includes%20(amongst%20other)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If this is too many privileges for an enterprise scenario there is also a specific privilege for this 250 objects limit. &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/quickstart-app-registration-limits&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Microsoft describes this&lt;/a&gt; for users hitting this limit with creating AppRegistrations, so I won&amp;rsquo;t reiterate the process here. You will just need to replace the mentioned privilege (&lt;code&gt;microsoft.directory/applications/create&lt;/code&gt;) with the &lt;code&gt;microsoft.directory/groups.unified/create&lt;/code&gt; privilege.&lt;br&gt;
This custom role is also more suited if regular users hit this limit in their daily jobs, as the &lt;em&gt;Teams Service Administrator&lt;/em&gt; is overkill in this situation.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Depending on how many Teams you create, some users or a technical user might be hitting a limit once they created 250 Teams. This is not a Teams limitation but an Entra Id limitation for non-administrative users and shows by the error &amp;ldquo;AadGroupCreationLimitExceeded&amp;rdquo;. It can be resolved with administrative privileges, e.g. &lt;em&gt;Teams Service Administrator&lt;/em&gt; or with a custom role containing the privilege &lt;code&gt;microsoft.directory/groups.unified/create&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I hope this helped you, but before you go, just think about one more thing, &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/entra/identity/users/directory-service-limits-restrictions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;there are more limits&lt;/a&gt;! In total Entra Id has a limit of 300.000 objects (if you have a domain), will you hit that once you remove the create limitation on the users? Likely not, but it&amp;rsquo;s not a bad idea to reason and make some estimates to avoid this greater limit. Maybe you need to think about a deletion strategy.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Standard) 5: Viewing Parents Documents</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/standard/quickview/</link>
        <pubDate>Sun, 26 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/standard/quickview/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/cover.jpg" alt="Featured image of post Sharepoint Integration (Standard) 5: Viewing Parents Documents" /&gt;&lt;p&gt;This article might sound like a very particular requirement, but I&amp;rsquo;ve heard it for the second time now: &amp;ldquo;The user should not need to navigate to the parent to work with its documents&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;There are usually more questions involved like &amp;ldquo;Should this work like a hierarchy?&amp;rdquo; or &amp;ldquo;What does he need to do? Does he need to see the same documents at the parent?&amp;rdquo; but if it&amp;rsquo;s really the flat and the same documents, there is an easy solution.&lt;/p&gt;
&lt;h2 id=&#34;options&#34;&gt;Options&lt;/h2&gt;
&lt;p&gt;The obvious choice might be a Quick View form. If you were not aware: Quick View forms can show Subgrids! So we can include a Subgrid to the &lt;em&gt;Sharepoint Documents&lt;/em&gt; table and define this as the only component of a &amp;ldquo;Files Quick View&amp;rdquo; form that we will include in the child records form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/AddGridToForm.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/AddQV.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/ResultQV.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;But here we have the problem that the &lt;em&gt;New&lt;/em&gt; and &lt;em&gt;Upload&lt;/em&gt; Buttons are missing as with other Subgrids included in a Quick View form.&lt;/p&gt;
&lt;p&gt;The better option here is the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/form-component-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;em&gt;Form Control Component&lt;/em&gt;&lt;/a&gt; which is a PCF by Microsoft that works similarly to a Quick View but allows editing the record shown.&lt;/p&gt;
&lt;p&gt;There is also a disadvantage: The form shown needs to be a &lt;em&gt;Main Form&lt;/em&gt; and it needs to be part of the app and be available to the user by security. This means our &amp;ldquo;Files&amp;rdquo; form will be selectable in the form picker of the parent table.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/AddFormComponent.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/ResultMain.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;This way we can have the full experience of files, with upload and document location actions etc. Great!&lt;/p&gt;
&lt;h2 id=&#34;one-more-trick&#34;&gt;One More Trick&lt;/h2&gt;
&lt;p&gt;Have you noticed that the screenshots of the result included the folder breadcrumbs? That&amp;rsquo;s a little trick I&amp;rsquo;ve found by looking at what Microsoft does with the &amp;ldquo;Files&amp;rdquo; Tab for example at the Lead. I was not able to recreate the same experience on a custom entity and found that they used the &lt;em&gt;Document Associated View&lt;/em&gt;. Up above you see that this view can not be selected for a Subgrid, but apparently, some of the integration experience is implemented into that view.&lt;/p&gt;
&lt;p&gt;To fix this for our custom forms and entities we can manipulate the XML.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Put the form with your Files Subgrid to a new solution.&lt;/li&gt;
&lt;li&gt;Export the solution.&lt;/li&gt;
&lt;li&gt;Unzip the solution to a local folder.&lt;/li&gt;
&lt;li&gt;In the Customization.xml, find the place where the Subgrid is defined.&lt;/li&gt;
&lt;li&gt;Replace the ViewId with &lt;em&gt;0016f9f3-41cc-4276-9d11-04308d15858d&lt;/em&gt; (or determine it with the screenshot seen below)&lt;/li&gt;
&lt;li&gt;Save the file.&lt;/li&gt;
&lt;li&gt;Rezip the (3) files &lt;strong&gt;inside&lt;/strong&gt; the local folder.&lt;/li&gt;
&lt;li&gt;Import the resulting zipped solution.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/BeforeExport.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The View Id of the Document Associated View can be determined via the URL when editing.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/ExportEdit.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The subgrid can be easily found via its unique name.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/QuickView/AfterExport.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Maker Portal will show an empty default view but the form keeps working.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;While you can certainly use the knowledge from &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/innerworkings/&#34; &gt;the inner workings article&lt;/a&gt; to replicate document locations to child records, sometimes it&amp;rsquo;s just easier to directly display the documents of the parent.&lt;/p&gt;
&lt;p&gt;To use this, first create a main form on the parent table that only shows a grid to the &lt;em&gt;Sharepoint Documents&lt;/em&gt;. Then you bring a second lookup to the parent to your child tables form and use the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/form-component-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;em&gt;Form Control Component&lt;/em&gt;&lt;/a&gt; Custom Control on the field. Next, we publish and are ready to even upload documents from the child entity using the subgrid on the form.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Authorization with the Power Pages Actions Solution</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api-authorization/</link>
        <pubDate>Sun, 19 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api-authorization/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-authorization/cover.jpg" alt="Featured image of post [VIDEO] Authorization with the Power Pages Actions Solution" /&gt;&lt;p&gt;Custom APIs offer a lot of flexibility, but cannot be called directly from PowerPages. With this video and the accompanying repository/solution, this is going to change! We are going to call custom functionality with an interface that is similar to what you know from the JavaScript of Model-Driven Apps.&lt;/p&gt;
&lt;p&gt;This time we are covering authorization.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/oTJVEFKjM1Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/oTJVEFKjM1Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api-authorization/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Powerpages Omnichannel</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/omnichannel/</link>
        <pubDate>Mon, 13 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/omnichannel/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/omnichannel/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Powerpages Omnichannel" /&gt;&lt;p&gt;This time it&amp;rsquo;s Sweta, bringing in a demo for Omnichannel integration with Power Pages.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/lrxcgtTq4R0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/lrxcgtTq4R0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/omnichannel/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Triggering a Flow from the Ribbon (single record)</title>
        <link>https://www.marius-wodtke.de/post/pattern/ribbon-flow/</link>
        <pubDate>Sun, 12 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/ribbon-flow/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/cover.jpg" alt="Featured image of post Patterns: Triggering a Flow from the Ribbon (single record)" /&gt;&lt;p&gt;Just like &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/pattern/date-flow/&#34; &gt;the first pattern&lt;/a&gt; we discovered, the core of this pattern includes a Flow that is triggered by a DateTime field being set. The requirement sounds like this &amp;ldquo;When the user clicks the button X, logic Y should run&amp;rdquo; and Y here is a logic that either requires the Server due to privileges etc. or is just complex enough that you wouldn&amp;rsquo;t want to implement it in the frontend, e.g. as PowerFX directly to the button. Considering the debuggability of PowerFX in the ribbon (aka Command Bar), that point for me is reached quite quickly. I might then resort to JS, but in the low code era, it might be much easier to do it in a Flow.&lt;/p&gt;
&lt;p&gt;To have a concrete sample you would implement like that: &amp;ldquo;After adding the necessary information, the user needs to send the record for approval&amp;rdquo;. The Power Automate Approvals are best used from Power Automate, so we would like to trigger our flow from a ribbon button &amp;ldquo;Send to Approval&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;backend-setup&#34;&gt;Backend Setup&lt;/h2&gt;
&lt;p&gt;For that, we first create a DateTime field, user local, on our table. Then we define a Flow with the Dataverse trigger, (Added or) Modified targeting the table and choosing the newly created field in the &lt;em&gt;Select Columns&lt;/em&gt; option.&lt;/p&gt;
&lt;p&gt;Anything aside the trigger is now tuned to your logic X.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/Trigger.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;It is vital to filter the trigger with Select Columns to the trigger field.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;why-datetime&#34;&gt;Why DateTime?&lt;/h2&gt;
&lt;p&gt;For the trigger field, we are using a DateTime because it is the most logical for a generalization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Boolean will be set to yes on the first execution, and on the second? Even if you force another Update to yes, this will not show up in Audit History.&lt;/li&gt;
&lt;li&gt;Choices have a similar problem.&lt;/li&gt;
&lt;li&gt;Lookups are harder to set, and there is no natural value to generalize, only in certain cases this might make sense, but still has the problem of the second execution.&lt;/li&gt;
&lt;li&gt;String fields work better with changing strings since this will at least show up in Audit, but for generalization, only random strings would make a good fit.&lt;/li&gt;
&lt;li&gt;Numbers are more interesting since you can simply count how often the button was clicked! And this makes sense if you need that reporting and don&amp;rsquo;t want to use the Audit History to count manually. They are just a little more complicated to implement because you first need to retrieve the current value of the field, coalesce it with a 0 and then set it. If the field is not on the form, we will even need nested deferreds for that.&lt;/li&gt;
&lt;li&gt;DateTimes provides a timing of the last execution even without Audit History and there is a natural way of setting it via the native functions of JS (&lt;code&gt;new Date()&lt;/code&gt;) or PowerFX (&lt;code&gt;Now()&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;frontend-setup&#34;&gt;Frontend Setup&lt;/h2&gt;
&lt;p&gt;Here we have 3 possibilities. And cover each one of them with a short code sample.&lt;/p&gt;
&lt;p&gt;We can bring the field to the form and the button sets it and optionally directly saves the record (we could also leave this up to the user, but that could lead to confusion).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TriggerFlowFormOnly&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Operation successfully started!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getAttribute&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;setValue&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Date());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;notification&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;showCloseButton&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addGlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;notification&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            window.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ClearGlobalNotification&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) =&amp;gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;); });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ClearGlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;clearGlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To call this, pass the CRM Parameters for the formContext (primaryControl), the fields logical name and optionally parameters for the message text and display duration. The values in the screenshot are taken as default if you skip them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/CommandForm.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;We can also update the field through the OData Web API. This does not require the field to be on the form and it&amp;rsquo;s also possible to use on a View! However, since it needs an Id to update the record, it will only work after the first save of the record!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/DisplayRule.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TriggerFlow&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;updateEntity&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;updateEntity&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;fieldLogicalName&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Date();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Operation successfully started!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WebApi&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;updateRecord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;entityName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;entityId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;updateEntity&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;messageForUser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;displayDuration&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Find the GlobalNotification function in the code block above
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This time we will need to pass the logical name and record id. The other parameters for the field logical name and the optional message and duration stay the same.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/Command.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The form will pass the FirstPrimaryItemId&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/CommandView.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The view will pass the FirstSelectedItemId&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And the third way is of course leveraging the modern commanding with PowerFX. This will internally work very similarly to &lt;code&gt;Xrm.WebApi.updateRecord&lt;/code&gt;, meaning it works from views and on forms without the field present.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-JS&#34; data-lang=&#34;JS&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Patch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Demos&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Selected&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Item&lt;/span&gt;, { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Trigger Field&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;() });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Notify&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Operation successfully started!&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;NotificationType&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Success&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/PowerFX.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Enter the formula from above, with the adapted Table (Demos) and Field (Trigger Field).&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;helper-solution&#34;&gt;Helper Solution&lt;/h2&gt;
&lt;p&gt;And of course, we can also trigger the JS functions from the modern commanding as well since it can simply call WebResource functions. And this is where &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the helper solution&lt;/a&gt; for this article comes in! With this, you can call a flow from the ribbon without writing a single line of code (be that JS or PowerFX) and without leveraging third-party tools like RibbonWorkbench. Simply install the solution, edit the command bar of your table, set the button&amp;rsquo;s action as shown below, publish and you are ready to go.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/ribbon-flow/CommandJS.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The optional parameters are omitted in this screenshot.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Users like ribbon functionality and low code developers like Power Automate Flows. To bring both together we will filter our flow trigger to a DateTime field and then define a ribbon button that has exactly one purpose: Setting that field.&lt;/p&gt;
&lt;p&gt;With the help of &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the RibbonHelpers solution&lt;/a&gt;, this can be done without writing a single line of code. Or you could also do it with one to two lines of PowerFX!&lt;/p&gt;
&lt;p&gt;The low complexity and wide possibilities in Power Automate are why this has become my favorite way of implementing ribbon functionality.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Patterns: Deferring Flows with the Trigger</title>
        <link>https://www.marius-wodtke.de/post/pattern/date-flow/</link>
        <pubDate>Sun, 05 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/pattern/date-flow/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/cover.jpg" alt="Featured image of post Patterns: Deferring Flows with the Trigger" /&gt;&lt;p&gt;Have you tried the &lt;em&gt;Delay Until&lt;/em&gt; option of the Dataverse trigger in Power Automate? If not, I will show you some useful knowledge about that field, variations of usage and a workaround.&lt;/p&gt;
&lt;p&gt;For the story: We want to send out a notification when the &lt;em&gt;Due Date&lt;/em&gt; (scheduledend) of a task approaches. Hitting the correct timing is possible in many ways, but &lt;em&gt;Delay Until&lt;/em&gt; can be very elegant!&lt;/p&gt;
&lt;h2 id=&#34;using-delay-until-trigger&#34;&gt;Using Delay Until Trigger&lt;/h2&gt;
&lt;p&gt;If you just have a simple field that marks the execution time, it&amp;rsquo;s simple, just enter the logical name. If it&amp;rsquo;s more complicated, you are out of luck at first, since you cannot use any outputs of the trigger for calculating the delay in an expression. We will try to cover that in the next sections.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/DataverseTrigger.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The trigger only reacts to changing the Due Date and waits for the Due Date before the execution starts.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/DataverseFields.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The change was made at 5:46, target time is 5:50.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/DataverseRun.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The flow run appears at 5:50.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Oh, and a bonus: If &lt;em&gt;Delay Until&lt;/em&gt; is in the past, it will be evaluated as an immediate execution, so we do not need to worry about that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/ImmediateFields.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The change was made at 5:59, target time is 5:45.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/ImmediateRun.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The flow run appears at 5:59, right after the change&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;business-rule-for-calculation&#34;&gt;Business Rule for calculation&lt;/h2&gt;
&lt;p&gt;But for the story, it would be nice if the reminder is not right at the deadline but sometime before! So we need to deal with that and that will be calculating the correct time for the reminder in a separate field. Our first attempt with Business Rules shows a weakness in the Formulas of Business Rules: They can only add/subtract days. This will work here if the requirement is &amp;ldquo;send the reminder 1 day before due&amp;rdquo;, but not if it is &amp;ldquo;send the reminder 12 hours before due&amp;rdquo; because the formula for adding days only allows whole numbers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/BusinessRule.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Set Field with Formula only allows full days to be added/subtracted.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/BusinessRuleFields.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The change was made at 8:12, target time is 8:15.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/BusinessRuleRun.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The flow run appears at 8:15.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Of course, any other method of writing a field with the correct value would work, e.g. Flow, Plugin and JavaScript will work as well, but will add complexity.&lt;/p&gt;
&lt;h2 id=&#34;powerfx-and-calculated-columns&#34;&gt;PowerFX and calculated columns&lt;/h2&gt;
&lt;p&gt;Our next attempt will be with calculated columns, with the modern PowerFX ones and a classic field. Both have a problem: While they are more flexible than the Business Rule formula, the resulting field does not work with &lt;em&gt;Delay Until&lt;/em&gt;! But we can still work around it.&lt;/p&gt;
&lt;p&gt;So first of all, the idea is we subtract one hour from the &lt;em&gt;Due Date&lt;/em&gt; and trigger on &lt;em&gt;Due Date&lt;/em&gt; since you cannot trigger on calculated fields.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/PowerFX.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/PowerFXTrigger.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Selected Columns cannot be a calculated field! Usually, you are good with taking the source fields of the calculated you are caring for as a trigger.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/PowerFXFields.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The change was made at 6:08, target time is 6:15.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/PowerFXRun.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The flow run appears at 6:08, right after the change.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Pretty much the same happens with a classic calculated column.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/CalculatedField.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/CalculatedFields.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The change was made at 8:07, target time is 8:10.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/CalculatedRun.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The flow run appears at 8:07, right after the change.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;delay-until-action&#34;&gt;Delay Until Action&lt;/h2&gt;
&lt;p&gt;How can we still make it work? There is also a Delay Until action in Power Automate! And since the fields are in the trigger output we can directly feed them to this action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/DelayAction.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The flow will now wait for the given timestamp and then will resume execution. But of course, since this is in the flow execution, we might as well just calculate an expression, skipping the PowerFX/calculated field after all.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/DelayResult.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;trigger-vs-action&#34;&gt;Trigger vs. Action&lt;/h2&gt;
&lt;p&gt;If the action is more flexible here, why should I bother with the trigger option at all? Well, there are three considerations why I think the trigger option is better.&lt;/p&gt;
&lt;p&gt;Number one, the trigger outputs are calculated upon start. In the sample of the due notification, you want to check if the task is not already closed by the time the flow is about to send it. With the trigger option, you have the correct values, if you used the actions to wait for days probably, you will want to re-retrieve the task to check for its state, introducing an inefficiency.&lt;/p&gt;
&lt;p&gt;Second is the long runs. If you are using the action, run duration is now dominated by the Delay Action, making it hard to determine how long the runs take in terms of real operating time. Also, you might have a long list of runs that are all running, while with the trigger option, the whole monitoring looks exactly like a flow that has no waiting conditions at all.&lt;/p&gt;
&lt;p&gt;And third, you might run into the 30-day flow timeout. In our story, if the &lt;em&gt;Due Date&lt;/em&gt; is in 45 days, no one will get a notification if you used the action, since the flow will have timed out. With the trigger option, it will run right on time (if the flow was turned on when the change event happened as well as on the target time).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/pattern/date-flow/DelayRun.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A run with Delay Until action starts immediately and then posts a longer execution time.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s summarize.&lt;/p&gt;
&lt;p&gt;Prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have a clear idea of when the Flow actions shall execute.&lt;/li&gt;
&lt;li&gt;You can PERSIST that DateTime to a Dataverse field.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Action:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enter the logical name of the field that holds the target execution time to the &lt;em&gt;Delay Until&lt;/em&gt; option of the Dataverse Trigger.&lt;/li&gt;
&lt;li&gt;(Optional) Add a condition that the field should not be empty into the &lt;em&gt;Filter Rows&lt;/em&gt; option to prevent immediate execution.&lt;/li&gt;
&lt;li&gt;(Optional) Add the logical name of the field to the &lt;em&gt;Selected Columns&lt;/em&gt; option to limit the pileup of executions due to other changes in the meanwhile.&lt;/li&gt;
&lt;li&gt;Add an internal condition to check the deferral time to prevent early execution when the target time was moved while the Trigger delayed the execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these in place, you can get around ugly solutions like polling (Scheduled Trigger) and busy waiting (Delay Until Action). It will result in short flow run durations, will not introduce inefficiencies due to re-retrieving and even can break the 30-day barrier of flows.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to integrate SharePoint in the Power Pages?</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/sharepoint/</link>
        <pubDate>Fri, 03 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/sharepoint/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/sharepoint/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to integrate SharePoint in the Power Pages?" /&gt;&lt;p&gt;Following up on the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/hierarchical-permissions/&#34; &gt;video about hierarchical permissions&lt;/a&gt;, this time we will integrate Sharepoint for storing files from a Power Page.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6pAoUThL96Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6pAoUThL96Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/sharepoint/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Business Applications launch event highlights</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-04/</link>
        <pubDate>Thu, 02 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-04/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-04/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Business Applications launch event highlights" /&gt;&lt;p&gt;In the April edition of the Power Platform Learners Group, we got some updates from the Business Applications launch event which was all about Copilot. I&amp;rsquo;ll admit, I had not attended and jumped in with a really quick tip from my blog.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/CQiNtZtLfE0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/CQiNtZtLfE0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-04/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] A solution for calling Custom APIs from Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/custom-api/</link>
        <pubDate>Sun, 28 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/custom-api/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api/cover.jpg" alt="Featured image of post [VIDEO] A solution for calling Custom APIs from Power Pages" /&gt;&lt;p&gt;Custom APIs offer a lot of flexibility, but cannot be called directly from PowerPages. With this video and the accompanying repository/solution, this is going to change! We are going to call custom functionality with an interface that is similar to what you know from the JavaScript of Model-Driven Apps.&lt;/p&gt;
&lt;p&gt;Repository Link: &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/Kunter-Bunt/PowerPagesActions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/oTJVEFKjM1Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/oTJVEFKjM1Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/custom-api/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Help, my Duplicate Detection isn&#39;t firing!</title>
        <link>https://www.marius-wodtke.de/post/other/duplicate-detection-trigger/</link>
        <pubDate>Sun, 21 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/duplicate-detection-trigger/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/cover.jpg" alt="Featured image of post Help, my Duplicate Detection isn&#39;t firing!" /&gt;&lt;p&gt;Your duplicate detection (DD) isn&amp;rsquo;t working, now what? As with every problem, stay calm and we&amp;rsquo;ll work our way through all the reasons why it might not be working. Well, at least all the reasons I know.&lt;/p&gt;
&lt;h2 id=&#34;duplicate-detection-is-disabled&#34;&gt;Duplicate detection is disabled&lt;/h2&gt;
&lt;p&gt;You will find this setting in &lt;em&gt;Advanced Settings&lt;/em&gt; &amp;gt; &lt;em&gt;Data Management&lt;/em&gt; &amp;gt; &lt;em&gt;Duplicate Detection Settings&lt;/em&gt;. You will want to make sure that the whole feature is enabled and your respective suboperation where you expect DD to be performed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/DDSettings.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;rule-not-published&#34;&gt;Rule not published&lt;/h2&gt;
&lt;p&gt;Your rules are found in &lt;em&gt;Advanced Settings&lt;/em&gt; &amp;gt; &lt;em&gt;Data Management&lt;/em&gt; &amp;gt; &lt;em&gt;Duplicate Detection Rules&lt;/em&gt;. Check that the rules that you want to apply are published and any other rule is unpublished. Notice that DD rules are unpublished upon deployment if they are included in the imported solution! That might be the reason why you have regular problems with unpublished rules.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/RuleUnpublished.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;rule-can-not-be-published&#34;&gt;Rule can not be published&lt;/h2&gt;
&lt;p&gt;There is a limit of 5 rules per entity on duplicate detection rules. So if you hit this limit, the publish will fail and you will need to deactivate another rule on the same entity.&lt;/p&gt;
&lt;h2 id=&#34;rule-options-not-correct&#34;&gt;Rule options not correct&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Case Sensitivity&lt;/em&gt; and &lt;em&gt;Exclude Inactive Records&lt;/em&gt; can severely limit the results. Think about the lead here, qualified or disqualified leads will not be taken into account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/RuleOptions.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;dd-does-not-work-on-activation&#34;&gt;DD does not work on activation&lt;/h2&gt;
&lt;p&gt;Especially if you use &lt;em&gt;Exclude Inactive Records&lt;/em&gt; you might expect the records to be checked upon activation. But sadly I have to tell you that there is another limitation, DD does not work on activate/deactivate.&lt;/p&gt;
&lt;h2 id=&#34;wrong-field-display-name&#34;&gt;Wrong field Display Name&lt;/h2&gt;
&lt;p&gt;Harder to spot is this one: You might have selected the wrong field because you have renamed the field on the form. So the Display Name of the field on the form might be &lt;em&gt;City&lt;/em&gt; in a section called &lt;em&gt;Billing Address&lt;/em&gt;, you select it in the DD rule criteria and it does not yield the expected result. To check this, I would suggest going to the customizations, opening the form, then selecting the field and opening the column behind it, to check its display name.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/WrongName.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;duplicate-display-name&#34;&gt;Duplicate Display Name&lt;/h2&gt;
&lt;p&gt;It gets nasty if in the situation above you have actually changed the display name of both &lt;em&gt;Address 1: City&lt;/em&gt; and &lt;em&gt;Address 2: City&lt;/em&gt; to &lt;em&gt;City&lt;/em&gt;. They will be differentiated on the form through their respective section, but in Advanced Find, Power Automate and other places you won&amp;rsquo;t be able to do so. That&amp;rsquo;s why it&amp;rsquo;s generally not a good idea to name 2 fields the same outside of forms, but it&amp;rsquo;s manageable through trial and error still.&lt;/p&gt;
&lt;p&gt;However, DD takes that up a notch: In that situation, the display names in the selection get cleared of duplicates (ironic, I know) and thus, only one of the 2 City fields is shown. And what you select then is pure luck. So simple rule: If you want to use a field with DD, you can&amp;rsquo;t have a second field with the same display name!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/DuplicateName1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;City is only shown once&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/duplicate-detection-trigger/DuplicateName2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;But it’s also not listed under Address X&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I hope one of these causes hit your problem, if not, I can only wish you good luck with your further search. Remember that you also can open a ticket with Microsoft support, they are often very helpful with their insights from the backend.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Hierarchical Table Permissions in the Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/hierarchical-permissions/</link>
        <pubDate>Thu, 18 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/hierarchical-permissions/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/hierarchical-permissions/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Hierarchical Table Permissions in the Power Pages" /&gt;&lt;p&gt;This time we have a little demo for you on how File Attachments can work in Power Pages, for that we will need hierarchical site permissions.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/EKHJjrV4Bn0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/EKHJjrV4Bn0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/hierarchical-permissions/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Showing Notifications from Frontend and their UX</title>
        <link>https://www.marius-wodtke.de/post/other/frontend-notifications/</link>
        <pubDate>Sun, 14 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/frontend-notifications/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/cover.jpg" alt="Featured image of post Showing Notifications from Frontend and their UX" /&gt;&lt;p&gt;We have several options for notifying the user of something from the frontend. However, &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Client API documentation&lt;/a&gt; does not picture many of these ways. So this post shall work as a reference for discussions, since it takes time to prepare a demo, especially if the business has no previous experience and needs all the options. Please note that &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/send-in-app-notifications?tabs=clientapi&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;In-App Notifications&lt;/a&gt; are missing! This is because I don&amp;rsquo;t think this should be a tool for the frontend but rather for the backend.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;To demonstrate the methods, I&amp;rsquo;ve set up ribbon buttons on a custom entity in a custom app. Then I recorded a short gif of each method. The following sections will consist of a short explanation, a code sample and those videos. A short tip for the gifs: If they look a little bit grainy on your browser, you can click on them to enlarge them to the native resolution.&lt;/p&gt;
&lt;p&gt;If you want to have a closer look, check &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365JSWarningOptions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the accompanying repository&lt;/a&gt;. For a quick look and feel I recommend the managed solution as it can be uninstalled cleanly after your experiments.&lt;/p&gt;
&lt;h2 id=&#34;form-notifications&#34;&gt;Form Notifications&lt;/h2&gt;
&lt;p&gt;The name suggests it, these are for the form. Therefore to show this, a formContext is needed. Alongside the message to show and a level that determines the color (INFO, WARNING or ERROR), there is also an identifier that can be used to remove the message again. In the code below this is done after 5 seconds.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// level: INFO | WARNING | ERROR
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FormNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ui&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setFormNotification&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`Hello from &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; Form Notification`&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;formNotification&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    window.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ClearFormNotification&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ClearFormNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ui&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;clearFormNotification&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;formNotification&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/FormInfo.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A form notification with level INFO is gray.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/FormWarning.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A form notification with level WARNING is yellow.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/FormError.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A form notification with level ERROR is red.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Be careful, if you are planning multiple notifications on the same form, as soon as you have two, they will be folded. And while the top level keeps the color of the highest level, the unfolded notifications are gray and the level is only shown in the icon, making it harder to differentiate them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/FormStacking.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Two or more notifications are folded.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;global-notifications&#34;&gt;Global Notifications&lt;/h2&gt;
&lt;p&gt;Compared to a form notification, a global notification is shown on the whole app, it will span wider across the navigation and above the ribbon. It also has more options! We can include an action button and a close button and also have an additional notification level of SUCCESS.&lt;/p&gt;
&lt;p&gt;You will notice that the code is more involved, simply because there are more options and the identifier of the notification is not passed by us but is returned in the deferred. Again, I&amp;rsquo;ve included a sample to auto-remove the notification after 5 seconds.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// level: 1: SUCCESS | 2: ERROR | 3: WARNING | 4: INFO
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;notification&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;action&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;actionLabel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visit Google&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;eventHandler&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;openUrl&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.google.com/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello from Global Notification&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;showCloseButton&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addGlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;notification&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;LogNotificationResult&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Message set: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            window.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ClearGlobalNotification&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;LogNotificationResult&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Message error&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ClearGlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;clearGlobalNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;LogNotificationResult&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Message cleared&amp;#34;&lt;/span&gt;), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;LogNotificationResult&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Message clear error&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;LogNotificationResult&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another important differentiation is the fact, that global notifications will persist a navigation, so if the form is closed, the notification stays.&lt;/p&gt;
&lt;p&gt;Also, no formContext is needed, making this suitable for views and &amp;ldquo;global&amp;rdquo; scripts. In combination with the action button, you could for example guide a user from a view to a certain record. Or, to be quirky: You create an enable rule on a button in the application ribbon that monitors the database (via Xrm.WebApi) and if a certain state is detected, suggests the user an action. E.g. &amp;ldquo;Your weekly report is ready! [Go to Report]&amp;rdquo;. But before you consider that, also look for the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/send-in-app-notifications?tabs=clientapi&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;In-App Notifications&lt;/a&gt;, which could be triggered from the backend as soon as the report is ready. The only disadvantage with those is the fact that they can take some seconds to show up, so they are not real-time, more like the same minute.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/GlobalSuccess.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A global notification with level SUCCESS is green.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/GlobalInfo.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A global notification with level INFO is gray.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/GlobalWarning.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A global notification with level WARNING is yellow.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/GlobalError.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A global notification with level ERROR is red.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/GlobalSwitch.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A global notification persists through navigation and may have an action.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Here, the same thing regarding folding applies as with the form notifications, as soon as there are two global notifications are shown, they will fold. Note, that it is possible to show one form and one global notification without a folding happening.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/GlobalStacking.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Two or more notifications are folded.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;field-notifications&#34;&gt;Field Notifications&lt;/h2&gt;
&lt;p&gt;Next up are notifications for specific fields. Consider something like &amp;ldquo;The combination of values for these 2 fields is not allowed&amp;rdquo;. There are 2 types, one for the control, and one for the attribute. Both will block saving (in contrast to the other errors we showed before). I do prefer the notification on the control since it provides immediate feedback to the user. But maybe you want to prevent exactly that and defer the error to saving, assuming the user will correct the error on his own anyway&amp;hellip;&lt;/p&gt;
&lt;p&gt;In terms of coding, the control notification is pretty similar to the form notifications we&amp;rsquo;ve already seen, there is just no level, it&amp;rsquo;s always ERROR. The attributes method setIsValid is different in that there is no id for the notification, there can only be one per attribute, and it&amp;rsquo;s either valid or not.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AttributeValidation&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;valid&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getAttribute&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_name&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;setIsValid&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valid&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello from Attribute Validation&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    window.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;AttributeValidation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ControlNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getControl&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_name&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;setNotification&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello from Control Validation&amp;#34;&lt;/span&gt; , &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;controlNotification&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    window.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ClearControlNotification&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;5000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ClearControlNotification&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;formContext&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getControl&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_name&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;clearNotification&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;controlNotification&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/Control.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A control notification shows on the attribute and blocks saving. On save it will also show as a form notification.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/Attribute.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;An invalid attribute will not immediately show on the form, but on save it is evaluated.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;dialogs&#34;&gt;Dialogs&lt;/h2&gt;
&lt;p&gt;Dialogs are very &amp;ldquo;in your face&amp;rdquo;. Which can be good or bad of course. Alert Dialogs have exactly one button to close them, Confirm Dialogs have two options and we can determine which one the user clicked. Error dialogs have 2 variants, one with an error code, and one with a custom message. The error codes receive their text from a &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/web-service-error-codes&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;predefined list&lt;/a&gt; of MS, so this is especially useful if you want to pass on an error you received from the backend. For all other types of errors, you can use a custom message. You can also provide details, which will be part of the downloadable log.&lt;/p&gt;
&lt;p&gt;Note that dialogs can&amp;rsquo;t be closed by code, they are designed to be dismissed by the user.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AlertDialog&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;alertStrings&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmButtonLabel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OK&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello from Alert Dialog&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Dialog Title&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;alertOptions&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;120&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;260&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;openAlertDialog&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;alertStrings&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;alertOptions&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Alert dialog closed&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ConfirmDialog&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmStrings&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;cancelButtonLabel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Skip&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmButtonLabel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OK&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello from Confirm Dialog&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Dialog Title&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmOptions&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;450&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;openConfirmDialog&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmStrings&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;confirmOptions&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;) {    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confirmed&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Dialog closed using OK button.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Dialog closed using Skip button or X.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ErrorDialogCode&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;errorOptions&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;errorCode&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2147135487&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;details&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Details from Error Dialog&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;openErrorDialog&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;errorOptions&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;);        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ErrorDialogMessage&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;errorOptions&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello from Error Dialog&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;details&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Details from Error Dialog&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Xrm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Navigation&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;openErrorDialog&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;errorOptions&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;);        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/DialogConfirm.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A Confirm Dialog has two buttons to choose from.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/DialogAlert.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;An Alert Dialog only has one button.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/DialogErrorCode.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;An error code dialog retrieves its message from the MS error code list.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/DialogErrorMessage.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;An error dialog can also have a custom message.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If you download the logs, it will contain the error details you passed to the function.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Exception Message: -2147135487

ErrorCode: -2147135487
HexErrorCode: 0x80055001
Error Details: Details from Error Dialog

Activity Id: f42dc166-c52b-4841-a01e-298afff445a1
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;powerfx-modern-commanding&#34;&gt;PowerFX (Modern Commanding)&lt;/h2&gt;
&lt;p&gt;And finally PowerFX. This became relevant with modern commanding, where you can create ribbon buttons with a Low Code approach. This has only one option, Notify. This is optically translated to a global notification but does not have all its parameters. It only has a message, level and a timeout can be passed.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// NotificationTypes: Success, Error, Warning, Information
Notify(&amp;#34;Hello from PowerFX&amp;#34;, NotificationType.Information, 5000)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/frontend-notifications/PowerFX.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;PowerFX can only show global notifications without actions.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;While our options with JavaScript are numerous, PowerFX is rather limited in terms of notifying the users. However, it&amp;rsquo;s only available in the ribbon right now and not for form scripting, so this is not that big of a deal. If you need more flexibility, use JavaScript, here you can not only show notifications on the app level but also the form and field level, additionally, dialogs can be used as a hard prompt to the user.&lt;/p&gt;
&lt;p&gt;I hope this helps you imagine the user experience you want to provide, have a nice refinement!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Virtual Tables in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/virtual-tables/</link>
        <pubDate>Mon, 08 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/virtual-tables/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/virtual-tables/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Virtual Tables in Power Pages" /&gt;&lt;p&gt;What hasn&amp;rsquo;t worked on the first try in the group presentation is picked up by Sweta in the Power Pages Quick Tips series, showcasing the integration of external data with Virtual Tables.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/efVxG94-UCo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/efVxG94-UCo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/virtual-tables/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Creating Authentication Tokens with Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/token/</link>
        <pubDate>Sun, 07 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/token/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/token/cover.jpg" alt="Featured image of post [VIDEO] Creating Authentication Tokens with Power Pages" /&gt;&lt;p&gt;JSON Web Token allows for Authentication and Authorization with WebServices. For example, the Dataverse Web API can leverage such a tokes (with the Dataverse audience) to determine what you can read. With Power Pages, we can also create such tokens as well to secure our custom services. We only need a trusted certificate and an App Registration.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ce8H_Twx0kE&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ce8H_Twx0kE&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/token/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Know more about Power Platform Administration and Power FX in Desktop Flows</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-03/03/</link>
        <pubDate>Tue, 02 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-03/03/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-03/03/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Know more about Power Platform Administration and Power FX in Desktop Flows" /&gt;&lt;p&gt;The March edition of the Power Platform Learners group! My contribution this time was around an admin center setting that will influence the behavior on the creation of Customer Addresses, preventing the creation of empty ones.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/DWMZzm9WNw8?t=345&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/DWMZzm9WNw8?t=345&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-03/03/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My First Shot at PCFs with React</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/pcf-react/</link>
        <pubDate>Sun, 31 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/pcf-react/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/pcf-react/cover.jpg" alt="Featured image of post My First Shot at PCFs with React" /&gt;&lt;p&gt;Did you know there is &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/component-framework/react-controls-platform-libraries&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;a preview for using React in Code Components&lt;/a&gt; (aka Power Apps Component Framework, PCF)? Well, I recently received training on using React natively so I thought this is a great opportunity to consolidate my knowledge.&lt;/p&gt;
&lt;p&gt;I plan to broaden the topic around &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/tags/vies-vat/&#34; &gt;VIES VAT&lt;/a&gt;, so this experiment will contain a new PCF that shall do the same thing as the original &lt;a class=&#34;link&#34; href=&#34;https://pcf.gallery/vat-number-validator/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;VAT Number Validator&lt;/a&gt; just for Power Pages.&lt;/p&gt;
&lt;h2 id=&#34;full-example&#34;&gt;Full example&lt;/h2&gt;
&lt;p&gt;Please note that this is a first shot, the actual validation part is mocked at this stage, we will worry about CORS and calling VIES VAT in another article.&lt;/p&gt;
&lt;p&gt;Therefore the code at this stage has received a separate branch, so make sure to view the branch &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesVATNumberValidator/tree/my-first-shot/react-pcf&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;my-first-shot/react-pcf&lt;/a&gt; when comparing your solution.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;As this is a preview feature, keep a close eye on &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/component-framework/react-controls-platform-libraries&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official documentation&lt;/a&gt;, the parameters might change!&lt;/p&gt;
&lt;p&gt;To create my control I&amp;rsquo;ve used
&lt;code&gt;pac pcf init --name PowerPagesVatNumberValidator --namespace PowerPagesVATNumberValidatorNameSpace --template field --framework react --run-npm-install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also shorten the parameter names to
&lt;code&gt;pac pcf init -n PowerPagesVATNumberValidator -ns PowerPagesVATNumberValidator -t field -fw react -npm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The main difference here is the &lt;code&gt;--framework&lt;/code&gt; or &lt;code&gt;-fw&lt;/code&gt; parameter, making it a React component.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You cannot have a .gitignore in the folder where you are executing the &lt;code&gt;pac pcf init&lt;/code&gt; command, if you have, you will receive an IO error.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Other commands you might need are &lt;code&gt;npm run refreshTypes&lt;/code&gt; after you changed the &lt;em&gt;ControlManifest.Input.xml&lt;/em&gt;, &lt;code&gt;pac solution init --publisher-name MariusWodtke --publisher-prefix mwo&lt;/code&gt; to create a new Solution locally and &lt;code&gt;pac solution add-reference --path ..\&lt;/code&gt; to add the &lt;em&gt;.pcfproj&lt;/em&gt; file as a reference to your solution. If you now &lt;code&gt;dotnet build&lt;/code&gt; your solution, the PCF will also be built and included in the solution. To test locally, &lt;code&gt;npm start watch&lt;/code&gt; launches a sandbox in the browser to quickly test changes. Please note that not all features of PCF are supported in the sandbox, for example, I noticed that &lt;code&gt;getResource&lt;/code&gt; does somewhat work, but hands out undefined as the requested content. Also, remember that when deploying the built solution to Dataverse you will need to increase the version of the control node in the &lt;em&gt;ControlManifest.Input.xml&lt;/em&gt;, otherwise Dataverse won&amp;rsquo;t pick up the changes.&lt;/p&gt;
&lt;h2 id=&#34;the-component&#34;&gt;The component&lt;/h2&gt;
&lt;p&gt;A lot of the magic of the &lt;em&gt;VAT Number Validator&lt;/em&gt; happened in index.ts, this will be much different even in the easiest React controls because you want to work in .tsx files instead. I named my entry-point &lt;em&gt;Control(.tsx)&lt;/em&gt; in correlation to the convention for &lt;em&gt;App(.tsx)&lt;/em&gt; as the entry-point for native apps. The index.ts will call this entry-point and pass along some needed objects, like the input parameters and a function that will ultimately call &lt;code&gt;notifyOutputChanged&lt;/code&gt; to persist the calculated results to the actual form field. In contrast to classic PCFs this happens in the &lt;code&gt;updateView&lt;/code&gt; method which looks like this in my case:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;updateView&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ComponentFramework&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;IInputs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;React&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ReactElement&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IControlProps&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;outputChanged&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;outputChanged&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;React&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Control&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputChanged&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IOutputs&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;notifyOutputChanged&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getOutputs&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IOutputs&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, updateView creates the root React element and passes the context with the inputs. The React element passes back the outputs, should the user input something into the field this is persisted in the form via &lt;code&gt;notifyOutputChanged&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now I can use my &lt;em&gt;Control.tsx&lt;/em&gt; to define the elements that are rendered inside. I want to mention the concept of &lt;code&gt;useState&lt;/code&gt;: It returns a getter and a setter. And when a component uses the getter it will be rerendered every time the setter is called. So in the following sample, when vatStates.img is set asynchronously after the correct image is determined and loaded, the img element will be refreshed automatically.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-react&#34; data-lang=&#34;react&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;vatStates&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt;} 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pcfvatimage&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The variable vatStates is already a complex object here, as I prefer to extract the handling of multiple states to a separate class with hooks. The hooks in the sample are stored in the &lt;em&gt;/services&lt;/em&gt; folder and have the &lt;em&gt;use&lt;/em&gt;-prefix as a convention for the public methods. If you take a closer look at the &lt;em&gt;vathooks.ts&lt;/em&gt; you will notice that the actual validation only looks for the user input starting with &amp;ldquo;AT&amp;rdquo; for marking it as valid. That&amp;rsquo;s of course not correct, but allows us to observe the loading of the sample image and persisting the bound fields.&lt;/p&gt;
&lt;p&gt;This preview also includes Fluent UI and using it should give you a look that is consistent with the Microsoft designs. You can &lt;a class=&#34;link&#34; href=&#34;https://developer.microsoft.com/en-us/fluentui#/controls/web&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;find the component here&lt;/a&gt;, I&amp;rsquo;ve used a TextField as input.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-react&#34; data-lang=&#34;react&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TextField&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pcfvatinput&amp;#39;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;vatStates&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;} 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{(&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;vatStates&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;onInput&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;styles&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fieldGroup&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;border&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }} 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is not much to elaborate on this component, you can work here as explained in regular React tutorials. The only difference is the onChange will have to call &lt;code&gt;notifyOutputChanged&lt;/code&gt; method directly or indirectly. Here this happens through &lt;code&gt;vatStates.onInput&lt;/code&gt;, &lt;code&gt;internalOnInput&lt;/code&gt; and &lt;code&gt;props.outputChanged&lt;/code&gt;. If you skip this, your inputs will not persist in the form and therefore will not be saved with the form.&lt;/p&gt;
&lt;h2 id=&#34;problems-i-faced&#34;&gt;Problems I faced&lt;/h2&gt;
&lt;p&gt;I did not want to inline styling and wanted to work with a .css module, however I couldn&amp;rsquo;t make the sandbox pick it up and instead used a regular .css. A .css just has to be referenced in the &lt;em&gt;ControlManifest.Input.xml&lt;/em&gt; and then you can use the defined classes in your components.&lt;/p&gt;
&lt;p&gt;As already mentioned, the sandbox does not work correctly with &lt;code&gt;getResource&lt;/code&gt;, it loads the resource when observing the network, but passes undefined to the callback. For debugging purposes, I solved this with a ternary, rendering a fixed image in the sandbox so that styling can be checked.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;resources&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getResource&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;img/at.png&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setImg&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data:image/.png;base64,&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;// -&amp;gt; rendered in dataverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setImg&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data:image/.png;base64,iVBOR...ggg==&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#75715e&#34;&gt;// -&amp;gt; rendered in sandbox
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to get img&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, the &lt;em&gt;New Look&lt;/em&gt;. Here the look of the Fluent UI component is not in line with the other fields, making it very apparent, that this field is self-made. This might change if we were using Fluent UI v9 (we can&amp;rsquo;t right now, the library versions in the &lt;em&gt;ControlManifest.Input.xml&lt;/em&gt; are fixed) and maybe applying additional styling, but the current transition highlights how even well-fit PCFs can be a break to the user experience with future wave updates and do require maintenance. Here are some screenshots of the new look and in the next section is a short video of the &amp;ldquo;old&amp;rdquo; look, where the integration (apart from the font) is much better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/pcf-react/NewLook.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/pcf-react/NewLookFocus.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/pcf-react/Result.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;React makes it easy to work with asynchronous resources and render the results in a way that is not the ultimate Christmas tree. Together with Fluent UI, the addition of this framework capability is a great fit for slick PCFs that behave well for the user while also having clean code behind them.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: AI driven Chatbot in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/chatbot/</link>
        <pubDate>Sat, 30 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/chatbot/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/chatbot/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: AI driven Chatbot in Power Pages" /&gt;&lt;p&gt;What hasn&amp;rsquo;t worked on the first try in the group presentation is picked up by Sweta in the Power Pages Quick Tips series, showcasing the integration of external data with Virtual Tables.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/MCnHLWmUO2k&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/MCnHLWmUO2k&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/chatbot/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: How to use External Data Source in Power Pages and Delegation of Service Principles in ALM</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-03/02/</link>
        <pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-03/02/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-03/02/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: How to use External Data Source in Power Pages and Delegation of Service Principles in ALM" /&gt;&lt;p&gt;Our new members Sweta and Milad show us some cool Power Platform Features for their introduction. Milad has something for Service Principles with Power Platform Pipelines and Sweta brings Virtual Tables for Power Pages to the table.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/T4JqlSXlhJA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/T4JqlSXlhJA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-03/02/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Identify Bootstrap version</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/bootstrap-version/</link>
        <pubDate>Mon, 25 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/bootstrap-version/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/bootstrap-version/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Identify Bootstrap version" /&gt;&lt;p&gt;Ambesh already showed us how to enable Bootstrap 5 for new Power Pages, but how do you know which version is active on an already existing Power Page? This video holds the answer!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ik2xb5PqARQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ik2xb5PqARQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/bootstrap-version/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Business Rule and Autonumber timing</title>
        <link>https://www.marius-wodtke.de/post/other/br-timing/</link>
        <pubDate>Sun, 24 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/br-timing/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-timing/cover.jpg" alt="Featured image of post Business Rule and Autonumber timing" /&gt;&lt;p&gt;This week just some observations regarding timing that might be helpful in your solution designs. We are going to find out when Business Rules are executed within the execution pipeline and when Autonumber fields are filled by Dynamics.&lt;/p&gt;
&lt;h2 id=&#34;execution-pipeline&#34;&gt;Execution Pipeline&lt;/h2&gt;
&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/event-framework#event-execution-pipeline&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official documentation&lt;/a&gt; Microsoft only lists the following Stages of the Event Pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PreValidation&lt;/li&gt;
&lt;li&gt;PreOperation&lt;/li&gt;
&lt;li&gt;MainOperation&lt;/li&gt;
&lt;li&gt;PostOperation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, this implies that there is also something like a MainValidation and PostOperation splits into two parts. One synchronous part is still within the transaction and one asynchronous part will be executed outside of the transaction by &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/asynchronous-service?tabs=webapi&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Asynchronous Service&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So the real Event Pipeline will look something like&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PreValidation&lt;/li&gt;
&lt;li&gt;MainValidation&lt;/li&gt;
&lt;li&gt;Database Transaction
&lt;ul&gt;
&lt;li&gt;PreOperation&lt;/li&gt;
&lt;li&gt;MainOperation&lt;/li&gt;
&lt;li&gt;PostOperation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Asynchronous&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I would assume that especially Autonumber would be executed somewhere in the MainOperation and from the Audit History I know that Business Rules, unlike synchronous workflows, are not executed PostOperation. But let&amp;rsquo;s find out!&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;To check my theory, I wrote a simple plugin that uses the tracing service to print all attributes present in the target.&lt;/p&gt;
&lt;p&gt;This Plugin is deployed to my Dataverse environment and registered with the Plugin Registration Tool (PRT) for PreValidation, PreOperation and PostOperation of Create of the Account entity. MainOperation is not available in this case, that would only be possible for Custom APIs or Virtual Table implementations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-timing/Registration.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For the Business Rule, I set up a Business Rule with Entity Scope and made it fill &lt;code&gt;emailaddress2&lt;/code&gt; with a fixed value if it is empty. &lt;strong&gt;Important&lt;/strong&gt;: &lt;code&gt;emailaddress2&lt;/code&gt; is not on the form in this experiment because otherwise the field would already be filled when the form is submitted, meaning the value would be present before PreValidation.&lt;/p&gt;
&lt;p&gt;For the Autonumber field, I chose the &lt;code&gt;accountnumber&lt;/code&gt;, the presence of this field is the reason why I&amp;rsquo;ve used the account table. This field is on the form but is not filled in the tests.&lt;/p&gt;
&lt;h2 id=&#34;testing&#34;&gt;Testing&lt;/h2&gt;
&lt;p&gt;For testing, I now create accounts and check the trace logs for the plugin executions where the attributes are logged. Let&amp;rsquo;s run through the results, I&amp;rsquo;ve marked the important lines below.&lt;/p&gt;
&lt;h2 id=&#34;prevalidation&#34;&gt;PreValidation&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-as&#34; data-lang=&#34;as&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;account&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1390&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bc28&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;44&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ee11&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9079&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0022489&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ef7a6&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Test 3&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_travelchargetype&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;690970003&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_taxexempt&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;creditonhold&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotpostalmail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotfax&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotphone&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotbulkemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;preferredcontactmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;statuscode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_gdproptout&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;processid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;00000000-0000-0000-0000-000000000000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;transactioncurrencyid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;transactioncurrency(6f02758b-6fc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ownerid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotsendmm&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotbulkpostalmail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountratingcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountclassificationcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;isprivate&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;territorycode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;businesstypecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;shippingmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;marketingonly&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;followemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;True&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;merged&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_addresstypecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;customersizecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;participatesinworkflow&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_shippingmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_freighttermscode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountnumber&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;preoperation&#34;&gt;PreOperation&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-as&#34; data-lang=&#34;as&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;account&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1390&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bc28&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;44&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ee11&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9079&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0022489&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ef7a6&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;territorycode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_travelchargetype&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;690970003&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_shippingmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1390bc28-44cc-ee11-9079-0022489ef7a6&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;followemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;True&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotbulkemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotsendmm&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;createdon&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2/15/2024 8:52:48 PM&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;statecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;isprivate&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;businesstypecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotpostalmail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;owningbusinessunit&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;businessunit(5eb3f8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ownerid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotbulkpostalmail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Test 3&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountnumber&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_addresstypecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotphone&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;transactioncurrencyid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;transactioncurrency(6f02758b-6fc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;modifiedby&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;statuscode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;modifiedonbehalfby&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;preferredcontactmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_gdproptout&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountratingcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;createdby&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotfax&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;merged&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;customersizecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;marketingonly&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;processid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;00000000-0000-0000-0000-000000000000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;shippingmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_taxexempt&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;creditonhold&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;modifiedon&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2/15/2024 8:52:48 PM&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;participatesinworkflow&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountclassificationcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_freighttermscode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;emailaddress2&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;test@marius-wodtke.de&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;postoperation&#34;&gt;PostOperation&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-as&#34; data-lang=&#34;as&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;account&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1390&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bc28&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;44&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ee11&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9079&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0022489&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ef7a6&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;territorycode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_travelchargetype&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;690970003&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_shippingmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1390bc28-44cc-ee11-9079-0022489ef7a6&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;followemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;True&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotbulkemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotsendmm&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;createdon&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2/15/2024 8:52:48 PM&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;statecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;isprivate&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;businesstypecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotpostalmail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;owningbusinessunit&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;businessunit(5eb3f8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ownerid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotbulkpostalmail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Test 3&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountnumber&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_addresstypecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotphone&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;transactioncurrencyid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;transactioncurrency(6f02758b-6fc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;modifiedby&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotemail&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;statuscode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;modifiedonbehalfby&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;preferredcontactmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_gdproptout&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountratingcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;createdby&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;systemuser(50baf8f5-0cc2-ee11-907a-0022489bee1d)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;donotfax&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;merged&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;customersizecode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;marketingonly&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;processid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;00000000-0000-0000-0000-000000000000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;shippingmethodcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_taxexempt&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;creditonhold&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;modifiedon&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2/15/2024 8:52:48 PM&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;participatesinworkflow&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;False&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountclassificationcode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address2_freighttermscode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;emailaddress2&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;test@marius-wodtke.de&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_taxexemptnumber&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_travelcharge&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;exchangerate&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1.000000000000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;msdyn_travelcharge_base&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;results--implications&#34;&gt;Results &amp;amp; Implications&lt;/h2&gt;
&lt;p&gt;This is quite far from what I expected. The Autonumber received its value before the PreValidation! In the form, Autonumber fields are empty before the first save, but they are filled before any plugin can run.&lt;/p&gt;
&lt;p&gt;The business rule ran somewhere between the PreValidation and PreOperation stages. So you can expect that they have been executed before your PreOperation plugins run, but not for your validation logic that you push to the PreValidation Stage.&lt;/p&gt;
&lt;p&gt;The PostOperation stage then offers no new insights, some properties were added, like the exchange rate, but the subjects of our test are already done.&lt;/p&gt;
&lt;p&gt;Because the Autonumber is executed before the Business Rule, this means the Business Rule can use the result in its conditions or actions. This is in contrast to Calculated fields which will be available earliest in the PostOperation stage since they are calculated upon retrieval, which is not possible PreCreate and might deliver wrong results PreUpdate.
Also, all custom Plugins can assume that the Autonumber has been executed, but PreValidation Plugins will perform their actions before the Business Rule.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This was a short reverse engineering session and we have clarity on timing for these standard features now. Autonumber executes before the PreValidation stage and Business Rules with &lt;em&gt;Entity&lt;/em&gt; scope (aka Table scope) are executed between PreValidation and PreOperation if they were not executed on the form in the frontend anyway.&lt;/p&gt;
&lt;p&gt;I hope this helps you in your mission to the perfect solution with the Dynamics standard functionality. My experiment here ended with the conclusion: I can use both the results of an Autonumber and a Business Rule action (&lt;em&gt;Set Value&lt;/em&gt;) to feed a Calculated field which will display correctly right after saving. I just can&amp;rsquo;t use another Calculated field for the Business Rule, in that case, I would have had to resort to a second Calculated field and a dependency between those two.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Pages Web API with N:N Relationships</title>
        <link>https://www.marius-wodtke.de/post/power-pages/webapi/</link>
        <pubDate>Sun, 17 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/webapi/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/webapi/cover.jpg" alt="Featured image of post [VIDEO] Power Pages Web API with N:N Relationships" /&gt;&lt;p&gt;This time we got the feedback that the standard experience for N:N Relationships is not ideal. The customer wants checkboxes instead. This is where the Web API of Power Pages can help us create a custom user experience for the users!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6hpz1GXMemY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6hpz1GXMemY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/webapi/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: PAC support for Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/pacportal/</link>
        <pubDate>Sun, 10 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/pacportal/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/pacportal/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: PAC support for Power Pages" /&gt;&lt;p&gt;This episode of the Power Pages Quick Tips is about the Power Apps CLI (aka PAC or Power Platform CLI) and how it can be used for a more ProDev experience with Power Pages. It enables us to download and upload a given Portal, for example, to move between stages or build up version control.&lt;/p&gt;
&lt;p&gt;Stay tuned and subscribe to the channel, the rest of the crew will join us soon for more tips &amp;amp; tricks around the Power Platform.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/KkaupFk_GX8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/KkaupFk_GX8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/pacportal/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Join a chat</title>
        <link>https://www.marius-wodtke.de/post/teams/self-join/</link>
        <pubDate>Sun, 10 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/self-join/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/cover.jpg" alt="Featured image of post Teams Integration: Join a chat" /&gt;&lt;p&gt;One problem with &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/chat/&#34; &gt;the connected chats&lt;/a&gt; in Dynamics is: How do you get access if you are not already part of the group? Dataverse does such a good job of breaking silos in organizations willing to do so, but if Teams chats are widely used, they may become mini-silos where relevant information is only accessible to a very selected group of people.&lt;/p&gt;
&lt;p&gt;But this shall not draw you away from using the Teams integration! Instead, consider the &lt;em&gt;Self-Join&lt;/em&gt; option. This enables anyone with the appropriate permissions in Dataverse to join existing connected chats.&lt;/p&gt;
&lt;h2 id=&#34;how-to-enable-self-join&#34;&gt;How to enable Self-Join?&lt;/h2&gt;
&lt;p&gt;The Teams options are found at &lt;em&gt;Sales Hub&lt;/em&gt; -&amp;gt; &lt;em&gt;App Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Chat and Collaborate&lt;/em&gt;. Here you want to scroll down to the entities, select the given entity, activate the switch for &lt;em&gt;Join chat&lt;/em&gt; and save.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/Enable.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-feel&#34;&gt;How does it feel?&lt;/h2&gt;
&lt;p&gt;After enabling it, it&amp;rsquo;s pretty straightforward to join an existing conversation. First, we will take a look at what happens if you have the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/timeline/&#34; &gt;timeline integration&lt;/a&gt; enabled as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/TimelineJoin.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;This picture has important information inside: All chat history is shared upon joining. This is good for getting the most out of this functionality, otherwise, you would be starting into the chat with no context of what has been already discussed. But it also means these chats are not as private as a chat you just have with a colleague. Even if you had to add someone in Teams, you could still choose not to share the chat history. This means that you will have to train the users so they understand, that the connected chat is more of an official communication around the record.&lt;/p&gt;
&lt;p&gt;If you did not activate the timeline integration, there is of course another way to join via the chat pane.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/ChatPaneJoin.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;when-does-it-not-work&#34;&gt;When does it not work?&lt;/h2&gt;
&lt;p&gt;Well firstly if you do not have write or append permission on the record. The chat pane will show the chat as locked and if you try to click the button at the timeline entry, you will receive an error about missing permissions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/NoPermission.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The second reason it might not work is when you did not enable the entity for self-joining. Remember that this setting is per entity, so while it might work for one type of record, it might not for another. The chat pane will look the same as if you had no permissions, clicking the button at the timeline entry reveals a different error.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/OptionNotEnabled.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Lastly, it will also not work if you have used the pinned tab in Teams as &amp;ldquo;connection&amp;rdquo; as this is not counted as connected chat. This chat won&amp;rsquo;t be listed in the connected chats section and therefore there will be no join button. So you can safely use the Dynamics 365 tab of Teams in chats that you want to keep private, only chats created/manually connected from Dynamics are subject to self-joining.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/self-join/FromTeams.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Enabling self-join is done in the collaboration options in a per-entity fashion.
Once you enable self-join for an entity, users with either write or append privileges to a record may join the connected chat(s). They can do so either via the timeline entry (&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/timeline/&#34; &gt;if this feature is enabled&lt;/a&gt;) or the chat pane. This does only work for chats that were connected from Dynamics, chats are not connected to Dynamics by adding the Dynamics 365 tab to Teams.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Using the Timeline</title>
        <link>https://www.marius-wodtke.de/post/teams/timeline/</link>
        <pubDate>Sun, 03 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/timeline/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/cover.jpg" alt="Featured image of post Teams Integration: Using the Timeline" /&gt;&lt;p&gt;We&amp;rsquo;ve already &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/chat/&#34; &gt;talked about chat&lt;/a&gt; as the lighter integration of Teams. And there is a preview to make it even better: &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/release-plan/2023wave1/service/dynamics365-customer-service/view-teams-chat-connected-records-timeline&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Timeline Integration&lt;/a&gt;. Even though this is marked as a customer service feature, it will still work nicely in the sales app context I&amp;rsquo;ve set in this series.&lt;/p&gt;
&lt;h2 id=&#34;how-to-enable-timeline-integration&#34;&gt;How to enable Timeline Integration?&lt;/h2&gt;
&lt;p&gt;Luckily, the Teams Integration settings are a one-stop job. So we are again at &lt;em&gt;Sales Hub&lt;/em&gt; -&amp;gt; &lt;em&gt;App Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Chat and Collaborate&lt;/em&gt;. Here it&amp;rsquo;s the &lt;em&gt;Show Teams chats on the connected record&amp;rsquo;s timeline (Preview)&lt;/em&gt; Flag we are looking for. Also note the fact, that this needs a Global Administrator to enable it because changes in Teams will be captured by an Application and synched back to Dynamics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/Enable.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-feel&#34;&gt;How does it feel?&lt;/h2&gt;
&lt;p&gt;Well, when you now connect a new chat to a record, here I&amp;rsquo;ve used an Opportunity, after some time and manually refreshing the timeline, a new activity will appear. By clicking on the button at the activity, the chat will open.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/PostClick.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The activity will display who connected the chat and who did the most recent change. I  did not see that much value in the most recent change. In the examples below, Alan made the change by simply being added to the chat when it was created and in the second example, Jeff self-joined, resulting in an unknown app to perform the most recent change.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/Details1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/Details2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;why-is-it-useful&#34;&gt;Why is it useful?&lt;/h2&gt;
&lt;p&gt;Those last sentences may sound like I wouldn&amp;rsquo;t like this feature, but I think it&amp;rsquo;s a wonderful feature for discovering chats! Let&amp;rsquo;s take a look at what was shown before I clicked the button.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/PreClick.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Nothing is telling me about the connected chat apart from the Timeline entry. I would need to open the chat drawer with the button at the top right to find the chat. Therefore the chat is easily overlooked and this will limit the adoption of the integration. Therefore I think you should enable this to help the users after the first connection of the chat.&lt;/p&gt;
&lt;h2 id=&#34;activities&#34;&gt;Activities&lt;/h2&gt;
&lt;p&gt;The timeline shows activities, which means there are also activity records. When clicking these in Activities, the chat will also open. This might be useful when searching for a certain chat, but you could as well just search in Teams and find the initial note to the record in Dataverse there. So I think this will mostly be used by the administrator to measure adoption&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/Activities.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;when-does-it-not-apply&#34;&gt;When does it not apply?&lt;/h2&gt;
&lt;p&gt;This only works when you connect the chat from Dynamics as detailed in the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/chat/&#34; &gt;post about chat&lt;/a&gt;. That specifically means that it will not make a timeline entry if you pin the Dynamics 365 Tab in a Teams chat, and this is a good decision by MS! Because there are other features like &lt;em&gt;Self Join&lt;/em&gt; that would make this a serious issue in regards to privacy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/timeline/FromTeams.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So if you want a timeline post to find the chat at a later point in time, start the chat from Dynamics.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;If you can get a Global Administrator on board to enable the Timeline Integration, do so! It&amp;rsquo;s as simple as flipping a switch and provides the users with a visible hint to existing Teams chats in Dynamics. Once the user identifies the chat in the timeline, he is just one click away from the chat window. Also if there are too many activities and users struggle to find the chat, this can be solved by &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/user/add-activities#pin-or-unpin-records-in-timeline&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;pinning the activity&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;If you choose not to enable this, for example, because it&amp;rsquo;s a preview feature, the chats can still be found via the chat pane, which is opened at the top right once you enabled chats. This has less visibility, but if the users have the chat pane always open, they will likely not bother about the timeline and will notice the chats there.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: My Highlight from Wave1 2024</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-03/01/</link>
        <pubDate>Sat, 02 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-03/01/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-03/01/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: My Highlight from Wave1 2024" /&gt;&lt;p&gt;The Power Platform Learners group is growing! In this episode, we picked our favorites from the wave update. I chose a lot of security enhancements for Plugins, Views and Apps. Also, I completely missed that &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/&#34; &gt;Plugin Packages&lt;/a&gt; are now generally available!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/BvqvHNhZpWA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/BvqvHNhZpWA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-03/01/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Pages Form Validation and the benefits of Webfiles</title>
        <link>https://www.marius-wodtke.de/post/power-pages/webfiles/</link>
        <pubDate>Sun, 25 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/webfiles/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/webfiles/cover.jpg" alt="Featured image of post [VIDEO] Power Pages Form Validation and the benefits of Webfiles" /&gt;&lt;p&gt;For the Lead form, we only want to allow entries for persons older than 18 years. A great fit for Client Site Validation, but there is a bug in the code, and the debugging tools are not working correctly! With the usage of Webfiles, we can get a better debugging experience to quickly find the error. And with using Fiddler we can even test some fixes without deploying a change to the server to avoid the time this takes.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/DkggPTxvn3o&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/DkggPTxvn3o&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/webfiles/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>PowerBI: Filtering Embedded Reports</title>
        <link>https://www.marius-wodtke.de/post/powerbi/filter/</link>
        <pubDate>Sun, 18 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/powerbi/filter/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/cover.jpg" alt="Featured image of post PowerBI: Filtering Embedded Reports" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/embedded-report/&#34; &gt;this post&lt;/a&gt; of the PowerBI series, we embedded a report in a Dynamics form. But I think you also noticed that this was somewhat pointless because the report showed the same data on every record, it lacked context. This time we are going to fix this with a filter!&lt;/p&gt;
&lt;h2 id=&#34;simple-filter&#34;&gt;Simple filter&lt;/h2&gt;
&lt;p&gt;To set a filter first, we need to find out the table and column name of the target column in the PowerBI report. You can find these in the &lt;em&gt;Table View&lt;/em&gt; when opening the report in the Desktop App.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/Fields.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;From here, fill the following Template by replacing the &amp;lt;tags&amp;gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{
    &amp;#34;Filter&amp;#34;: &amp;#34;[{\&amp;#34;$schema\&amp;#34;:\&amp;#34;basic\&amp;#34;,\&amp;#34;target\&amp;#34;:{\&amp;#34;table\&amp;#34;:\&amp;#34;&amp;lt;PowerBI Table Name&amp;gt;\&amp;#34;,\&amp;#34;column\&amp;#34;:\&amp;#34;&amp;lt;PowerBI Column Name&amp;gt;\&amp;#34;},\&amp;#34;operator\&amp;#34;:\&amp;#34;In\&amp;#34;,\&amp;#34;values\&amp;#34;:[$a],\&amp;#34;filterType\&amp;#34;:1}]&amp;#34;,
    &amp;#34;Alias&amp;#34;: {
        &amp;#34;$a&amp;#34;: &amp;#34;&amp;lt;CE Column Logicalname&amp;gt;&amp;#34;,
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The ugly formatting is on purpose! When I prepared this article I had to follow the advice &lt;a class=&#34;link&#34; href=&#34;https://powerusers.microsoft.com/t5/Building-Power-Apps/How-does-the-table-binding-or-JSON-filter-string-work-in-the-new/td-p/1834916&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt; to remove all line breaks in the &lt;em&gt;Filter&lt;/em&gt; property of the JSON as this is an encoded JSON.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The result for my sample looks like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{
    &amp;#34;Filter&amp;#34;: &amp;#34;[{\&amp;#34;$schema\&amp;#34;:\&amp;#34;basic\&amp;#34;,\&amp;#34;target\&amp;#34;:{\&amp;#34;table\&amp;#34;:\&amp;#34;account\&amp;#34;,\&amp;#34;column\&amp;#34;:\&amp;#34;Account Name\&amp;#34;},\&amp;#34;operator\&amp;#34;:\&amp;#34;In\&amp;#34;,\&amp;#34;values\&amp;#34;:[$a],\&amp;#34;filterType\&amp;#34;:1}]&amp;#34;,
    &amp;#34;Alias&amp;#34;: {
        &amp;#34;$a&amp;#34;: &amp;#34;name&amp;#34;,
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/Configuration.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now the report looks much different! Of course, my report for this series is not well designed for this scenario, the only thing that makes sense is the filtered contact grid.
Notice how the &lt;em&gt;Pie Chart&lt;/em&gt; for the country changed: When we selected an account manually in the Dashboard/Embedded Report, it got a &lt;em&gt;cross highlighting&lt;/em&gt;, but the filter applies to the whole page, so the visual is simply filtered.
I have also enabled the &lt;em&gt;Filter Pane&lt;/em&gt; in the PCF settings to show you the effects on the displayed report.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/FilteredReport.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/Filter.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;or-filter&#34;&gt;Or filter&lt;/h2&gt;
&lt;p&gt;You can also filter one PowerBI column to 2 different CE fields. This is also &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/embed-powerbi-report-in-system-form&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the sample Microsoft provides&lt;/a&gt;. With the operator &amp;ldquo;In&amp;rdquo; we can just define a second &lt;em&gt;Alias&lt;/em&gt; (aka Parameter) for the Filter like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{
    &amp;#34;Filter&amp;#34;: &amp;#34;[{\&amp;#34;$schema\&amp;#34;:\&amp;#34;basic\&amp;#34;,\&amp;#34;target\&amp;#34;:{\&amp;#34;table\&amp;#34;:\&amp;#34;account\&amp;#34;,\&amp;#34;column\&amp;#34;:\&amp;#34;Account Name\&amp;#34;},\&amp;#34;operator\&amp;#34;:\&amp;#34;In\&amp;#34;,\&amp;#34;values\&amp;#34;:[$a,$b],\&amp;#34;filterType\&amp;#34;:1}]&amp;#34;,
    &amp;#34;Alias&amp;#34;: {
        &amp;#34;$a&amp;#34;: &amp;#34;name&amp;#34;,
        &amp;#34;$b&amp;#34;: &amp;#34;accountnumber&amp;#34;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, I&amp;rsquo;m not good with these samples, but notice here how 2 Accounts are listed now, one with an empty (&lt;em&gt;Blank&lt;/em&gt;) name for the empty accountnumber of the CE record and one with name of the CE record.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/OrFilter.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;and-filter&#34;&gt;And filter&lt;/h2&gt;
&lt;p&gt;And finally, you can filter on multiple fields matching with AND. For that, you simply define 2 filters like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{
    &amp;#34;Filter&amp;#34;: &amp;#34;[{\&amp;#34;$schema\&amp;#34;:\&amp;#34;basic\&amp;#34;,\&amp;#34;target\&amp;#34;:{\&amp;#34;table\&amp;#34;:\&amp;#34;account\&amp;#34;,\&amp;#34;column\&amp;#34;:\&amp;#34;Account Name\&amp;#34;},\&amp;#34;operator\&amp;#34;:\&amp;#34;In\&amp;#34;,\&amp;#34;values\&amp;#34;:[$a],\&amp;#34;filterType\&amp;#34;:1},{\&amp;#34;$schema\&amp;#34;:\&amp;#34;basic\&amp;#34;,\&amp;#34;target\&amp;#34;:{\&amp;#34;table\&amp;#34;:\&amp;#34;account\&amp;#34;,\&amp;#34;column\&amp;#34;:\&amp;#34;Account Number\&amp;#34;},\&amp;#34;operator\&amp;#34;:\&amp;#34;In\&amp;#34;,\&amp;#34;values\&amp;#34;:[$b],\&amp;#34;filterType\&amp;#34;:1}]&amp;#34;,
    &amp;#34;Alias&amp;#34;: {
        &amp;#34;$a&amp;#34;: &amp;#34;name&amp;#34;,
        &amp;#34;$b&amp;#34;: &amp;#34;accountnumber&amp;#34;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sorry for that long line, but if I put a linebreak there, you can bet someone will copy it blindly and be frustrated for it not working.&lt;/p&gt;
&lt;p&gt;The result looks pretty much the same as in the first picture, the name and number match up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/filter/AndFilter.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;considerations&#34;&gt;Considerations&lt;/h2&gt;
&lt;p&gt;I don&amp;rsquo;t have any knowledge of a way to connect two filters in the JSON as an or and that poses an interesting architectural requirement: While I can match two or more CE columns to one PowerBI Column I can&amp;rsquo;t match one CE column to multiple PowerBI columns.&lt;/p&gt;
&lt;p&gt;How could I run into this? Just an example: You have migrated the accounts of 2 legacy systems into CE and put the respective account number into the &lt;code&gt;accountnumber&lt;/code&gt; field. Now the designer of the PowerBI Dataset aggregates from both systems as well and tries to deduplicate the accounts, he puts in 2 columns for &lt;code&gt;accountnumberA&lt;/code&gt; and &lt;code&gt;accountnumberB&lt;/code&gt; to be able to fill both of them.
Now you will have trouble solving this. If the dataset also contained an &lt;code&gt;accountnumber&lt;/code&gt;, which one would it be in case of a deduplication? One of the CE records would show an empty report. Splitting it in CE won&amp;rsquo;t help either, it still would not match in case of a deduplication where both numbers are filled.
The dataset designer can&amp;rsquo;t even solve this on his own if he would match the CE accounts on his side as he still has to put 2 &lt;code&gt;accountnumber&lt;/code&gt;s into one field, which will not work unless you can afford a &lt;code&gt;contains&lt;/code&gt; filter-operator.
A deduplication on CE side will likely not be possible on CE side, because if it had, you would have done it in the first place.&lt;/p&gt;
&lt;p&gt;One thing you could do is to feed back a new number back to CE based on the dataset, lets call it &lt;code&gt;reportnumber&lt;/code&gt;. This could be the deduplicated &lt;code&gt;accountnumber&lt;/code&gt; in the dataset, pushed to CE via a &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/tags/dataflows&#34; &gt;Dataflow&lt;/a&gt;. Now you can filter CEs &lt;code&gt;reportnumber&lt;/code&gt; against PowerBIs &lt;code&gt;accountnumber&lt;/code&gt; and will always get the correct result.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Filters make embedded PowerBI reports much more useful! To apply a filter, you enter a JSON in the configuration of the PowerBI PCF in the Dataverse form and reference the correct PowerBI table and column names as well as the logicalname(s) of the CE field(s) on the form. The result is a prefiltered report which can for example show all orders for the customer from an ERP system without needing a full integration via &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/tags/virtual-entities/&#34; &gt;Virtual Entities&lt;/a&gt; or similar technologies. You can filter one PowerBI column by multiple CE fields or multiple PowerBI columns with an AND. An OR filtering on multiple PowerBI columns is not possible, you can only filter a single PowerBI column with an OR by using the IN operator.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Connecting a Team via Flow</title>
        <link>https://www.marius-wodtke.de/post/teams/custom/</link>
        <pubDate>Sun, 11 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/custom/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/cover.jpg" alt="Featured image of post Teams Integration: Connecting a Team via Flow" /&gt;&lt;p&gt;We have covered the standard approach of connecting a Teams team with Dataverse for &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-basic/&#34; &gt;the basic&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-enhanced/&#34; &gt;enhanced experience&lt;/a&gt;. But this time we will discuss automation around that. The standard wizard has a lot of steps that you might not want your users to go through every time. They also have a lot of flexibility in where they want to attach the record but that might not be what the IT-department wants as well.&lt;/p&gt;
&lt;p&gt;Our premise for this post will therefore be &amp;ldquo;Users do need a Team for collaborating with others when an Opportunity reaches a certain stage and this should be a new Team named like the Opportunity&amp;rdquo;. To automate the creation, I have chosen Power Automate as seems to be a perfect fit for its wide variety of connectors.&lt;/p&gt;
&lt;h2 id=&#34;creating-the-team&#34;&gt;Creating the Team&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s the easiest of this whole post since there is a Teams connector and that&amp;rsquo;s one of its actions. Note however that under advanced, the &lt;em&gt;Visibility&lt;/em&gt; has a default value of public, which might be unwanted.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/CreateTeam.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;reading-all-necessary-information&#34;&gt;Reading all necessary information&lt;/h2&gt;
&lt;p&gt;Now the team is already there, only one more thing to do, connect it to Dataverse, right? Yeah, kind of, but to create a _&amp;quot;&lt;em&gt;Microsoft Teams Collaboration entity&lt;/em&gt; we need a lot more information than just the ID that was returned from the &lt;em&gt;Create a Team&lt;/em&gt; action.&lt;/p&gt;
&lt;p&gt;First, we will be getting the Team and its default channel called &amp;ldquo;General&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/GetTeam.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then we will also want the URL of the Sharepoint Site, which is not returned by any action of the Teams connector. Instead, we will use the &lt;em&gt;Office 365 Groups&lt;/em&gt; Connector, which has an action to send a generic HTTP request to the &lt;em&gt;Graph API&lt;/em&gt; where we can get it in the property &lt;em&gt;webUrl&lt;/em&gt;. Please note the wait period of a few seconds here. It drove me insane when the Flow always said &amp;ldquo;404 not found&amp;rdquo; and copying the URL in the browser returned the expected result. The Graph API needs a second to catch up, probably due to indexing, anyway after a few seconds it will work, 5 to 10 seconds should be enough.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/GetSharepoint.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For copying: &lt;code&gt;https://graph.microsoft.com/v1.0/groups/@{outputs(&#39;Create_a_team&#39;)?[&#39;body/newTeamId&#39;]}/sites/root?$select=webUrl&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We will need the full URL as well as a relative one, I have extracted these to two variables.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/Var1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/Var2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s everything we will need to create a Microsoft Teams Collaboration entity.&lt;/p&gt;
&lt;h2 id=&#34;adding-a-team-member&#34;&gt;Adding a Team member&lt;/h2&gt;
&lt;p&gt;But before we do that, let&amp;rsquo;s quickly take a side step and add someone to the team. Because if only our Power Automate integration user is in there, that won&amp;rsquo;t yield any value. There need to be users, and probably the person working the opportunity is the right one. For simplicity, I have chosen the &lt;em&gt;Created by&lt;/em&gt; here because while I think the &lt;em&gt;Owner&lt;/em&gt; makes more sense, I would have needed to catch the fact, that the owner can also be a team.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/AddOwner.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connecting-the-record&#34;&gt;Connecting the record&lt;/h2&gt;
&lt;p&gt;Now, we go on to create the Microsoft Teams Collaboration entity. Let&amp;rsquo;s go with the picture first:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/CreateCollab.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For reference, as code view:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;parameters&amp;#34;: {
    &amp;#34;entityName&amp;#34;: &amp;#34;msdyn_teamscollaborations&amp;#34;,
    &amp;#34;item/msdyn_groupid&amp;#34;: &amp;#34;@outputs(&amp;#39;Get_a_team&amp;#39;)?[&amp;#39;body/id&amp;#39;]&amp;#34;,
    &amp;#34;item/msdyn_tenantid&amp;#34;: &amp;#34;eba86d05-d196-4bd8-8793-b2cae91984dd&amp;#34;,
    &amp;#34;item/msdyn_channelid&amp;#34;: &amp;#34;@outputs(&amp;#39;List_channels&amp;#39;)?[&amp;#39;body/value&amp;#39;][0][&amp;#39;id&amp;#39;]&amp;#34;,
    &amp;#34;item/msdyn_channelfolderrelativeurl&amp;#34;: &amp;#34;@{variables(&amp;#39;SharepointRelative&amp;#39;)}/Shared Documents/General&amp;#34;,
    &amp;#34;item/msdyn_teamid&amp;#34;: &amp;#34;@outputs(&amp;#39;Get_a_team&amp;#39;)?[&amp;#39;body/internalId&amp;#39;]&amp;#34;,
    &amp;#34;item/regardingobjectid&amp;#34;: &amp;#34;@triggerOutputs()?[&amp;#39;body/opportunityid&amp;#39;]&amp;#34;,
    &amp;#34;item/regardingobjecttypename&amp;#34;: &amp;#34;opportunity&amp;#34;,
    &amp;#34;item/msdyn_teamsiteurl&amp;#34;: &amp;#34;@variables(&amp;#39;SharepointFull&amp;#39;)&amp;#34;,
    &amp;#34;item/msdyn_channelname&amp;#34;: &amp;#34;General&amp;#34;,
    &amp;#34;item/msdyn_channeltype&amp;#34;: &amp;#34;Standard&amp;#34;,
    &amp;#34;item/msdyn_teamname&amp;#34;: &amp;#34;@triggerOutputs()?[&amp;#39;body/name&amp;#39;]&amp;#34;,
    &amp;#34;item/regardingobjecttypecode&amp;#34;: 3
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Most of these should be clear, &lt;code&gt;Team ID&lt;/code&gt; and &lt;code&gt;Internal Id&lt;/code&gt; are from the &lt;em&gt;Get a Team&lt;/em&gt; action and the &lt;code&gt;Collaboration Channel ID&lt;/code&gt; value assumes that the first channel shall be connected (for a new team this is the default channel &amp;ldquo;General&amp;rdquo;). This assumption around the &amp;ldquo;General&amp;rdquo; channel is also applied in the &lt;code&gt;Collaboration Channel Name&lt;/code&gt; and &lt;code&gt;CollaborationChannel Relative Folder URL&lt;/code&gt;. For &lt;code&gt;Collaboration Channel Type&lt;/code&gt; may be &lt;em&gt;Private&lt;/em&gt; or &lt;em&gt;Standard&lt;/em&gt;, so Standard for the default channel and the &lt;code&gt;Team Site URL&lt;/code&gt; is the &lt;code&gt;webUrl&lt;/code&gt; we saved to a variable earlier.&lt;/p&gt;
&lt;p&gt;An easy way to get the &lt;code&gt;Collaboration Tenant Identifier&lt;/code&gt; is to look it up in the overview of the Entra ID (aka Azure AD). This, as well as the Sharepoint base URL that I removed to get a relative URL, will likely be fixed because often we deploy between different Environments of the same tenant. If however, you are planning to transport the solution to a different tenant, consider using &lt;em&gt;Environment Variables&lt;/em&gt; to be able to adapt the value to the target tenant.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/TenantId.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The other values are related to the record in Dataverse. If you are not using Opportunity, of course, the values for &lt;code&gt;Related Dynamics 365 Record ...&lt;/code&gt; will change and your naming scheme for the Team might be different. An interesting note: I try to avoid the &lt;em&gt;Entity Code&lt;/em&gt; whenever possible because it changes for custom entities with deployments. But here you need to set it, otherwise, you will get the error pictured below. This should not be a bigger problem as the integration only works for a limited set of standard entities.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: There are hints, that the limitation to the entities that can be integrated with teams might be lifted, check out this recent change to the Entity.xml of a custom entity:&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/Teaser.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/Error.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The Result&lt;/h2&gt;
&lt;p&gt;So first let&amp;rsquo;s picture the full flow that we just picked apart:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/FlowFull.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After the team is created and connected, the Opportunity will notice and the Teams button will show the existing team (if you have not hidden this button, which is likely in the described scenario from the beginning). Also, the Documents Tab now shows the /Shared Documents/General folder of the Sharepoint site connected to the team.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/Result1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/custom/Result2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;pros-and-cons&#34;&gt;Pros and Cons&lt;/h2&gt;
&lt;p&gt;So there is a catch. We never pinned the Dynamics 365 tab to the Teams channel. The users will have to do that on their own if they like this feature. I am not aware of how this could work with flows, hit me up if you have an idea. Resorting to a classic plugin has the problem that we would need to do the authentication without a good way to store the secret&amp;hellip;&lt;/p&gt;
&lt;p&gt;But there are many pros as well! For once, in an optimal case does not require any user interaction at all and also makes sure all Teams are created the same way. The fact that the Teams are &amp;ldquo;managed&amp;rdquo; by the user creating them (the user is an owner) allows us to implement further logic. You can for example add members to the Team from Dataverse actions, like connecting another user in the &lt;em&gt;Sales Team&lt;/em&gt; Connection subgrid on the right. The flow user can also make posts in the channel and I bet you can think of more stuff you want out of the Teams integration. With the standard you might always run into permission issues, either the flow user has very liberal privileges in the corporate Teams, or it will simply not be possible unless the users are trained to include that user in the team.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;With Power Automate we can certainly mock a connected Teams Team! We will have to leverage a third connector apart from Teams and Dataverse in the Office 365 Groups connector, but the integration will behave very similar to the OOTB experience without the need for the user to select as many options correctly in a multi-step wizard. The Teams channel won&amp;rsquo;t have the Dynamics 365 tab pinned by default, that&amp;rsquo;s something a user will have to add manually. But this approach offers repeatability and allows for further automated activities in Teams down the line.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Enable Bootstrap5 in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/bootstrap5/</link>
        <pubDate>Fri, 09 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/bootstrap5/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/bootstrap5/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Enable Bootstrap5 in Power Pages" /&gt;&lt;p&gt;In this episode of the Power Pages Quick Tips, we talked about Bootstrap 5, the newest version of the framework which claims to be more lightweight and faster, which should of course also translate to quicker Power Pages as well.&lt;/p&gt;
&lt;p&gt;Stay tuned and subscribe to the channel, the rest of the crew will join us soon for more tips &amp;amp; tricks around the Power Platform.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/KkaupFk_GX8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/KkaupFk_GX8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/bootstrap5/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Using FetchXml in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/fetchxml/</link>
        <pubDate>Sun, 04 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/fetchxml/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/fetchxml/cover.jpg" alt="Featured image of post [VIDEO] Using FetchXml in Power Pages" /&gt;&lt;p&gt;We already took a look at &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/list&#34; &gt;displaying a list to the user&lt;/a&gt;. This used the products of Dataverse which allowed the Dataverse Users to control what visitors will see on the Power Page.
But that list might not fill all requirements in terms of looks. With FetchXml in Power Pages, we can build a more flexible list to show the data exactly as we like to!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/V7SRHXqKnw0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/V7SRHXqKnw0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/fetchxml/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Dataflow Deployment Considerations</title>
        <link>https://www.marius-wodtke.de/post/ppl/2024-02/</link>
        <pubDate>Sat, 03 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2024-02/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-02/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Dataflow Deployment Considerations" /&gt;&lt;p&gt;The group of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/ppl/2023-advent/&#34; &gt;the Advents Calendar&lt;/a&gt; is coming back to the Power Platform Learners channel. To get the new year started, the first episode had a pretty open pool of topics and I chose to showcase something from &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/dataflows/deployment/&#34; &gt;a recent post&lt;/a&gt;. In the next episode, which should be in February, we will talk about the new release wave announcements.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/3cDlfMaQV0Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/3cDlfMaQV0Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2024-02/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Power Platform Learners: Enhanced Data Model</title>
        <link>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/enhanced-datamodel/</link>
        <pubDate>Mon, 29 Jan 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/power-pages-quick-tips/enhanced-datamodel/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/enhanced-datamodel/cover.jpg" alt="Featured image of post [VIDEO] Power Platform Learners: Enhanced Data Model" /&gt;&lt;p&gt;The Power Platform Learners channel is not dead after the  Power  Platform Advent Calendar! Together with Ambesh Singh, we will talk more about Power Pages, THE tool for your customers and partners to effectively interact with your company through Dataverse.&lt;/p&gt;
&lt;p&gt;In this episode of the Power Pages Quick Tips, we talked about the Enhanced Data Model, a big step after so many years of Powerapps Portal!&lt;/p&gt;
&lt;p&gt;Stay tuned and subscribe to the channel, the rest of the crew will join us soon for more tips &amp;amp; tricks around the Power Platform.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/KthV8DhJ1AQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/KthV8DhJ1AQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/power-pages-quick-tips/enhanced-datamodel/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>PowerBI: Deploying an Embedded Report with Dataverse</title>
        <link>https://www.marius-wodtke.de/post/powerbi/deploy/</link>
        <pubDate>Sun, 28 Jan 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/powerbi/deploy/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/cover.jpg" alt="Featured image of post PowerBI: Deploying an Embedded Report with Dataverse" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/embedded-report/&#34; &gt;the post about Embedded Reports&lt;/a&gt;, we did talk about the customizations needed to show a PowerBI Report on a form in Dynamics, but not about general ALM praxis: Customize on a Development environment and then deploy a solution.
We will catch up here and explore the &lt;em&gt;Solution Awareness&lt;/em&gt; of Reports.&lt;/p&gt;
&lt;h2 id=&#34;preparations&#34;&gt;Preparations&lt;/h2&gt;
&lt;p&gt;For the report to be selectable for embedding, it needs to be included in a solution anyway, so if you followed &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/embedded-report/&#34; &gt;the post about Embedded Reports&lt;/a&gt;, your solution includes a Dataset, a Report and probably an Entity with a form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/SolutionComponents.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;plain-import&#34;&gt;Plain Import&lt;/h2&gt;
&lt;p&gt;If we take this solution now, export and import to the target environment, the form will show the report, but no data. This is because the Dataset does not yet include data. We can fix this in the &lt;a class=&#34;link&#34; href=&#34;https://app.powerbi.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;PowerBI Service&lt;/a&gt; by selecting the managed Workspace for the Dataverse environment, which was automatically created as soon as we imported the solution with the Report/Dataset inside. Here we can manually refresh the Dataset and if needed authorize the connection in the &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/ReportBroken.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Before the report can be used in the target environment a refresh needs to be executed.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/Workspace.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;For Solution Aware Reports, a Workspace is automatically created.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/Settings.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Scheduled Refresh Settings are not deployed with a solution. But the Connection-URL is!&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-connection-problem&#34;&gt;The Connection Problem&lt;/h2&gt;
&lt;p&gt;But as marked in the last screenshot, the connection from the original environment was kept in the Dataset and the editing capabilities in the &lt;a class=&#34;link&#34; href=&#34;https://app.powerbi.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;PowerBI Service&lt;/a&gt; cannot simply change it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As of writing this article, editing semantic models in the PowerBI Service is a preview feature. Your milage may vary.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Therefore I have to edit the query in the Desktop App and then publish it again to the Workspace of the target environment. And this change will be overwritten with a new deployment! So we need a different solution. Fortunately, there is one for Dataverse: When you click edit on the Dataset in the Solution, there is an option to use the current environment as the connection. If this is set to yes, the Connection-URL will always point to the environment where the solution is deployed! On the first deployment, you will still need to set the refresh schedule and probably do a manual refresh to make the embedded report work and check that the connection is authorized, but after that, there should not be any problems.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/EditDataset.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/EditDatasetDialog.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;non-dataverse-connections&#34;&gt;Non-Dataverse Connections&lt;/h2&gt;
&lt;p&gt;For Datasets that do not connect to Dataverse, this won&amp;rsquo;t be as easy, because there is no &amp;ldquo;current environment&amp;rdquo; to connect to. In this situation, I would like to inspire you to look into the Pipelines of the PowerBI Service.&lt;/p&gt;
&lt;p&gt;First, we include the components in a solution on the source environment, then we deploy this solution once to the target environment(s). Now the requirements for embedding are fulfilled. Any updates to the report/model are now deployed via the PowerBI Pipeline which moves the components between the connected workspaces and sets the correct connection values.&lt;/p&gt;
&lt;p&gt;Just be aware that you will have problems importing a new version of the solution, you will encounter an error like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Import failed: Unsynced customizations detected. 
Please sync changes with Power BI and try again. 
Datasets with customizations: [&amp;lt;ExternalReferenceId=5a4c6949-b787-4920-b608-effd9baeafb3, HasCustomizations=True&amp;gt;]. 
Reports with customizations: [&amp;lt;ExternalReferenceId=9bf7ca28-3b28-4fff-8f5e-6bb1b3847589, HasCustomizations=True&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can be corrected by using the &lt;em&gt;Sync changes&lt;/em&gt; button on the components in the solution in the target environment. This does work on managed solutions as well. But an import of this solution after the PowerBI Pipeline is set up is not advisable anyway, it will reset the connection until the PowerBI Pipeline runs again.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;At least with a Dataverse connection, the trick of using &amp;ldquo;an existing connection in the target environment&amp;rdquo; makes deploying embedded reports easy and only requires manual setup after the first deployment of a dataset. The result contains my changed &amp;ldquo;&lt;strong&gt;AAA&lt;/strong&gt;dventure Works&amp;rdquo; Account, only being present in the target environment.&lt;/p&gt;
&lt;p&gt;Stay tuned for the next episode in this series where we will look into filters to make these reports more useful by applying filters based on values on a form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/deploy/Report.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Deploying Dataflows</title>
        <link>https://www.marius-wodtke.de/post/dataflows/deployment/</link>
        <pubDate>Sun, 21 Jan 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/dataflows/deployment/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/cover.jpg" alt="Featured image of post Deploying Dataflows" /&gt;&lt;p&gt;In the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/dataflows/&#34; &gt;first shot of Dataflows&lt;/a&gt;, we&amp;rsquo;ve explored the feature and imported products from an Azure SQL database. But a feature without deployment is merely a PoC.&lt;/p&gt;
&lt;h2 id=&#34;dataflows-in-solutions&#34;&gt;Dataflows in Solutions&lt;/h2&gt;
&lt;p&gt;Luckily, we can just add dataflows to solutions! We can not create them inside a solution, or edit them, this has to happen in the dataflows section of the Maker Portal. But after preparing the dataflow there, we can just select &lt;em&gt;Add Existing&lt;/em&gt;-&amp;gt;&lt;em&gt;Automation&lt;/em&gt;-&amp;gt;&lt;em&gt;Dataflow&lt;/em&gt; in our solution.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/AddExisting.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Dataflows are found under Automation.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;what-is-not-transported&#34;&gt;What is not transported?&lt;/h2&gt;
&lt;p&gt;So that&amp;rsquo;s it, add it, deploy it and it will run? Sadly not. The connection to the database is not transported with the solution. That&amp;rsquo;s of course good news as the opposite behavior would mean a secret like a password is included in the solution. But that means we will have to do some manual work.&lt;/p&gt;
&lt;p&gt;First, you find the deployed dataflow in the dataflows section of the maker portal. If another user imported the solution you can reassign the dataflow by finding the importing user and then selecting &lt;em&gt;Change Owner&lt;/em&gt; (you need to be a System Administrator for that). And if you don&amp;rsquo;t know who first imported the dataflow, you can check the owner column in the solution that contains the dataflow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/ChangeOwner.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/ChangeOwner2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Next, you need to edit the dataflow. The &lt;em&gt;Transform&lt;/em&gt; screen will welcome you with a warning to connect to the data source. Here, the secret values need to be reentered.
Now step through the rest of Wizard. If you also deployed the target entity, the mappings should be already set.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/PostDeployment.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After the first deployment, the dataflow will need to be published.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/SetConnection.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When selecting Edit, a warning guides you to enter the connection.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/PostDeployment2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Once the wizard is completed, the dataflow will be published.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Another thing that is not included in the solution is the incremental refresh settings. That&amp;rsquo;s less intuitive but needs to be considered if the defaults were changed. These values are at least unlikely to change, so most probably you only need to set these up with the first deployment alongside the connection.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/dataflows/deployment/IncrementalRefresh.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;While the Refresh Settings are transported, Incremental Refresh is not.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;watch-out&#34;&gt;Watch out!&lt;/h2&gt;
&lt;p&gt;A dataflow can only be edited by its owner. And we can change the owner if we are admin. But guest users and application users (a.k.a. App Registrations or S2S apps) are not found by the dataflows view &amp;ldquo;All Dataflows&amp;rdquo;! That means if the dataflow was first imported by a guest user, it can only be edited to set the connection by this user. If it was imported by an application user - usually via an automated pipeline - it&amp;rsquo;s even worse, since there is no UI Login for that application, the connection cannot be set at all.&lt;/p&gt;
&lt;p&gt;Even upgrading the solution to a version without the dataflow will not resolve this. For me, this still left a trace in the system, although it was not visible in the Default Solution anymore. Another import with a regular user made the dataflow show up with the old owner again. The only solution that worked was to create a copy of the dataflow in the source environment and import that to the target environment.&lt;/p&gt;
&lt;p&gt;For this new import, I&amp;rsquo;ve used a separate solution that is not included in the automated pipeline. Instead, it is transported manually now.&lt;/p&gt;
&lt;p&gt;If this is not an option, for example, due to company policies, a Dataflow Template might be an option to include in the solution. After the deployment, an administrator would create the dataflow from that template, which I can not see much benefit to handing them the solution and configuring the connection after the import.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;You can include dataflows to solutions. The connection to the data source will not be included and will have to be set after the first import as a post-deployment step. Make sure you are not importing the solution with an application user and I also strongly advise against a guest user for the import, you might lose control because you can&amp;rsquo;t reassign the dataflow. If necessary, create a separate &amp;ldquo;manual&amp;rdquo; solution for dataflows if you are at risk of these import errors.&lt;/p&gt;
&lt;p&gt;Check out &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-query/dataflows/dataflow-solution-awareness&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official article of Microsoft on solution awareness&lt;/a&gt; to make sure you have the latest version of &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-query/dataflows/dataflow-solution-awareness#known-limitations&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the limitations&lt;/a&gt;.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] First Steps of Code Editing in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/code/</link>
        <pubDate>Sun, 14 Jan 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/code/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/code/cover.jpg" alt="Featured image of post [VIDEO] First Steps of Code Editing in Power Pages" /&gt;&lt;p&gt;We have used the Design Studio to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/editor/&#34; &gt;create static content&lt;/a&gt;, &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/form/&#34; &gt;show forms to enter data&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/list/&#34; &gt;lists to let the users view dynamic data&lt;/a&gt;. This time we will broaden our capabilities again by editing the code of a page, allowing for full web development flexibility.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/19pSEQNAFHA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/19pSEQNAFHA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/code/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>VAT Number Validator PCF: CORS Tricks with Dataverse</title>
        <link>https://www.marius-wodtke.de/post/other/vat-pcf/</link>
        <pubDate>Sun, 07 Jan 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/vat-pcf/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/vat-pcf/cover.jpg" alt="Featured image of post VAT Number Validator PCF: CORS Tricks with Dataverse" /&gt;&lt;p&gt;I hate CORS! It has cost me so many nerves throughout my developer career and still while I have some patterns now for myself, you can be sure that colleagues will find a way to run into these problems and approach me with wild errors.&lt;/p&gt;
&lt;p&gt;And CORS is a topic of PCFs as well. Recently I discovered the &lt;a class=&#34;link&#34; href=&#34;https://pcf.gallery/vat-number-validator/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;VAT Number Validator&lt;/a&gt; in the PCF Gallery and immediately thought &amp;ldquo;How did the author solve the CORS problem here?&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;the-original-solution&#34;&gt;The original solution&lt;/h2&gt;
&lt;p&gt;While researching that in the linked &lt;a class=&#34;link&#34; href=&#34;https://www.blog.allandecastro.com/deep-dive-into-power-apps-component-framework-part-4-walkthrough-to-create-your-first-pcf-based-on-a-field/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;blog article&lt;/a&gt; I noticed that the author, Allan De Castro, is a colleague from France, best regards from this point.&lt;/p&gt;
&lt;p&gt;Allan mentions the usage of &lt;a class=&#34;link&#34; href=&#34;https://cors-anywhere.herokuapp.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://cors-anywhere.herokuapp.com/&lt;/a&gt; which as a third party may fail and is therefore not a preferred solution for enterprise use cases. The actual solution contained &lt;a class=&#34;link&#34; href=&#34;https://cors.bridged.cc&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://cors.bridged.cc&lt;/a&gt; instead which is a commercial alternative, selling the service of passing through requests with a CORS wildcard. Having a contracted party is of course better, but it still introduces the risk of a third party not being available.&lt;/p&gt;
&lt;h2 id=&#34;the-short-cors-explanation&#34;&gt;The (short) CORS explanation&lt;/h2&gt;
&lt;p&gt;CORS stands for Cross-Origin Resource Sharing. It requires that if a Website implements a request to a different domain, the server accepting the request must explicitly allow the request.&lt;/p&gt;
&lt;p&gt;It is an important detail that this is enforced and validated by the browsers, not the server handling the request! When JavaScript performs a request in code (POST for example), the browser first sends an OPTIONS request to the server requested and evaluates the response. Is the current domain and the HTTP method allowed? Are all headers required for the actual request valid?&lt;/p&gt;
&lt;p&gt;All of these can be set with a wildcard at the destination, so a server can allow all operations from any domain with any headers. However, remember the first sentence &amp;ldquo;request to a different domain&amp;rdquo;, so all of this is not required if your page is requesting a server on the same domain that also delivers the page itself.&lt;/p&gt;
&lt;h2 id=&#34;my-solution-in-the-past&#34;&gt;My solution in the past&lt;/h2&gt;
&lt;p&gt;I had to implement the VAT Number Validator twice in my life already, once as a backend solution and once in the frontend. At the time we were not on the PCF train yet and used plain JavaScript, but the problem remains the same, CORS does not allow you to directly call &lt;a class=&#34;link&#34; href=&#34;http://ec.europa.eu/taxation_customs/vies/services/checkVatService&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http://ec.europa.eu/taxation_customs/vies/services/checkVatService&lt;/a&gt; from the browser.&lt;/p&gt;
&lt;p&gt;Our solution there was to use the existing Azure API Management (APIM) as a proxy. You define an API on the WSDL of the European VAT Service (VIES) and add an &lt;em&gt;inbound policy&lt;/em&gt; to allow CORS from your Dynamics Environment URLs (XXX.crmY.dynamics.com). Additionally, when defining the API you can tell the APIM to perform a SOAP/REST conversion, allowing you to pass a JSON body as input which is much easier to define in JavaScript.&lt;/p&gt;
&lt;p&gt;SOAP body:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;soadpRequest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;?xml version=&amp;#39;1.0&amp;#39; encoding=&amp;#39;UTF-8&amp;#39;?&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;SOAP-ENV:Envelope xmlns:ns0=&amp;#39;urn:ec.europa.eu:taxud:vies:services:checkVat:types&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; xmlns:ns1=&amp;#39;http://schemas.xmlsoap.org/soap/envelope/&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; xmlns:xsi=&amp;#39;http://www.w3.org/2001/XMLSchema-instance&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; xmlns:SOAP-ENV=&amp;#39;http://schemas.xmlsoap.org/soap/envelope/&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;SOAP-ENV:Header/&amp;gt;&amp;lt;ns1:Body&amp;gt;&amp;lt;ns0:checkVat&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;ns0:countryCode&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_vatNumberElement&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toUpperCase&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;/ns0:countryCode&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;ns0:vatNumber&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_vatNumberElement&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;/ns0:vatNumber&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;/ns0:checkVat&amp;gt;&amp;lt;/ns1:Body&amp;gt;&amp;lt;/SOAP-ENV:Envelope&amp;gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;REST body:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;soadpRequest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;countryCode&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_vatNumberElement&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toUpperCase&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;vatNumber&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_vatNumberElement&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If there is a provisioned APIM already, this solution will be free. If you need or have a consumption-based APIM, either the 1 million free requests or 3.8 EUROCENTS(!) per 10.000 requests are quite cheap. Of course, keep in mind that these conditions and prices are a snapshot and Microsoft could change them.&lt;/p&gt;
&lt;p&gt;But this whole solution requires extra resources, the APIM, and thus works well in a project situation with the customers Azure subscription but not in a public PCF, so we need something different.&lt;/p&gt;
&lt;h2 id=&#34;defeating-cors-in-dataverse-contexts&#34;&gt;Defeating CORS in Dataverse Contexts&lt;/h2&gt;
&lt;p&gt;But by now we have an easy and completely free method of defeating CORS problems in Dataverse Contexts: Custom APIs.&lt;/p&gt;
&lt;p&gt;This works because CORS is browser-only and allows communication to the server that is delivering the website. So the browser is calling XXX.crmY.dynamics.com which is allowed and the server code of Dynamics calls ec.europa.eu which is also allowed.&lt;/p&gt;
&lt;p&gt;Here is a quick rundown of how this looks.&lt;/p&gt;
&lt;p&gt;First, you need to tell the PCF Framework that you need the webAPI in the ControlManifest.Input.xml. This by the way limits the PCF to Model Driven Apps because Portals and Canvas Apps don&amp;rsquo;t have that API available in the same way.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;feature-usage&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;uses-feature&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WebAPI&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;required=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/feature-usage&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you define a request class matching the inputs of your Custom API. This is very easy here, there is just one string input &amp;ldquo;VAT&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ValidateVATRequest&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;VAT&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;constructor&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;vat&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;VAT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;vat&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;getMetadata&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;metadata&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;boundParameter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unknown&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;parameterTypes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;VAT&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;typeName&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Edm.String&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;structuralProperty&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;operationName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mwo_ValidateVAT&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;operationType&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;metadata&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then we can instantiate that class and call the webAPIs &lt;code&gt;execute&lt;/code&gt; method to invoke the API. After converting the response body to JSON we can use all outputs. I&amp;rsquo;ve used the original outputs of the VIES service, so we have &lt;code&gt;Valid&lt;/code&gt;, &lt;code&gt;Name&lt;/code&gt; and &lt;code&gt;Address&lt;/code&gt; available.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ValidateVATRequest&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_vatNumberElement&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;webAPI&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;any&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;execute&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Valid&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally of course we need the Custom API. Apart from the records in CRM, this is a plugin class that extracts the VAT from the inputs, feeds it to the connected SOAP Service and extracts the response properties back to the output.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;protected&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;override&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; ExecuteDataversePlugin(ILocalPluginContext localPluginContext)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; context = localPluginContext.PluginExecutionContext;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; input = context.InputParameters[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;VAT&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;input: {input}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    input = input.Replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).ToUpper(); &lt;span style=&#34;color:#75715e&#34;&gt;// general cleaning of input&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; country = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;(input.Take(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;).ToArray());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; vat = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;(input.Skip(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;).ToArray());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;country: {country}, vat: {vat}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; service = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; checkVatPortTypeClient();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    service.checkVat(&lt;span style=&#34;color:#66d9ef&#34;&gt;ref&lt;/span&gt; country, &lt;span style=&#34;color:#66d9ef&#34;&gt;ref&lt;/span&gt; vat, &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; valid, &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; name, &lt;span style=&#34;color:#66d9ef&#34;&gt;out&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; address);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;valid: {valid}, name: {name}, address: {address}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context.OutputParameters[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Valid&amp;#34;&lt;/span&gt;] = valid;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context.OutputParameters[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;] = name;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context.OutputParameters[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Address&amp;#34;&lt;/span&gt;] = address;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/vat-pcf/ConnectedService.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;To have the simple new checkVatPortTypeClient() constructor available, add the WSDL to the project&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;The decision now was, is this an improvement for the existing control or should it be a fork? This is because the Custom API ties this down to Model Driven Apps which might not be wanted. But I&amp;rsquo;m in contact with Allan and it seems the Control was always aimed at Model Driven Apps anyway so the changes will be merged. Until the changes are available in &lt;a class=&#34;link&#34; href=&#34;https://pcf.gallery/vat-number-validator/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official control&lt;/a&gt;, you can download the control or take a look at the source code in &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/VATNumberValidator&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;my fork&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; The Fork was merged to &lt;a class=&#34;link&#34; href=&#34;https://pcf.gallery/vat-number-validator/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official control&lt;/a&gt; and is available for download.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;demo-time&#34;&gt;Demo time!&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/vat-pcf/Demo.gif&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Simple and effective, you enter the VAT Number and it gets validated as well as Name and Address are resolved.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I still hate CORS :D But when you have Dataverse as the platform, it&amp;rsquo;s easy to circumvent with Custom APIs. Other frontends might require different solutions, like Flows for Canvas Apps or simply Azure API Management as a Swiss army knife, but as long as you have a server that you can program, you can get around it!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Connect Chats</title>
        <link>https://www.marius-wodtke.de/post/teams/chat/</link>
        <pubDate>Sun, 31 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/chat/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/cover.jpg" alt="Featured image of post Teams Integration: Connect Chats" /&gt;&lt;p&gt;Let&amp;rsquo;s chat! Sometimes a full team is just too much for a Dynamics record. A chat would be more lightweight and posts might be more visible to the participants, especially if the chat is not used that frequently.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;Enabling chatting is again at &lt;em&gt;Sales Hub&lt;/em&gt; -&amp;gt; &lt;em&gt;App Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Chat and Collaborate&lt;/em&gt;. We can choose in which apps users can chat (by default the service apps are enabled here) and to which entities we can connect chats. Also take a close look at the second screenshot, if you select the ellipses of an entity in the configuration, you can customize the experience for the user. Most importantly some of those settings might be considered security-relevant, like self-joining chats.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In my environment it took around 15 minutes to show the features correctly after successfully saving the settings.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Setup1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;You can select apps and entities for chats.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Setup2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;With the ellipses, additional settings per entity appear.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;co-presence-chatting&#34;&gt;Co-presence chatting&lt;/h2&gt;
&lt;p&gt;When someone is on the same record as you, you can start a chat with them. When Teams opens, the message box is prefilled with a link to the record. At first glance this seems not that helpful, the other side is already there! But adding a space (and thus converting the text to a link) reveals a nice feature of the app in Teams: The link is converted to a card and the open button behaves much like if you had added the tab.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/CoChat1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Co-presence is found at the top right.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/CoChat2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When clicked, Teams opens with a prefilled message box.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/CoChat3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Adding a space transforms the text into a link, revealing a card.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/CoChat4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Using the open button, the Dynamics app in Teams opens.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connecting-a-chat&#34;&gt;Connecting a chat&lt;/h2&gt;
&lt;p&gt;But the whole operation we performed before was only in teams. From Dataverse, there is no link from the record to the chat, so once it moves down the list of conversations, it will be hard to find. Also, it was just a message in the regular chat with me and my colleague, nothing dedicated to the record itself, making it hard to integrate others later on in the same matter because the chat history might contain other things. So, a really &amp;ldquo;new&amp;rdquo; chat tied to the record is better for storing the information shared!&lt;/p&gt;
&lt;p&gt;This is done with the Chat buttons in the global ribbon or the drawer at the side. Here you can select an existing chat from your recent chats or create a new one. Since I think it makes sense to make a chat per record, I created a new chat here. And that works with only one additional participant! That way I can still have my private chat Alan and a dedicated &amp;ldquo;official&amp;rdquo; chat for that (Key-)Account.&lt;/p&gt;
&lt;p&gt;Once connected, the chat shows in the timeline and a drawer at the bottom displays. And of course, opening the chat pane via the buttons will display it as well. But only if you are a member of the chat! The experience of a user that is not joined yet is something we will explore separately. Depending on the settings, you can also disconnect chats from the record.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Connect1.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The chat pane can be opened from 2 different locations.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Connect2.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Usually, you will need a new chat for a new record.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Connect3.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;If you made a mistake, you can disconnect the chat again (if allowed in the settings).&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Connect4.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The chat appears in a drawer at the bottom, also a timeline post appears.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/chat/Connect5.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The ellipses offer to switch over to Teams.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;At first look, the Chat integration is simple for both sides: The administrator just enables it for apps and entities and the user can just start by creating a new chat for a record. On second thought, configuring this feature might take months of discussions in larger corporations: Are users allowed to self-join? How do we limit the self-joining capabilities with the Write/Append privileges? Do we need to consider the information leak in Teams by the Bot/Note?
Nevertheless, Chats are a lightweight way to interact with colleagues with or without Dynamics access. The integration promotes the documentation of communication, even if it&amp;rsquo;s a quick, unstructured communication.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: 24.12.2023 Farewell</title>
        <link>https://www.marius-wodtke.de/post/ppl/2023-advent/24/</link>
        <pubDate>Sun, 24 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2023-advent/24/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: 24.12.2023 Farewell" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce Copilot in Power Platform and learn something about Azure Communication Services. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ByjwBdHhgs0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/ByjwBdHhgs0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/24/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Service Bus 5: Deploying Service Endpoints</title>
        <link>https://www.marius-wodtke.de/post/servicebus/deployment/</link>
        <pubDate>Sun, 24 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/servicebus/deployment/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/cover.jpg" alt="Featured image of post Service Bus 5: Deploying Service Endpoints" /&gt;&lt;p&gt;After finishing a development it&amp;rsquo;s time to deploy it. In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/biceps/&#34; &gt;the last post&lt;/a&gt; of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/&#34; &gt;this series&lt;/a&gt;, we covered how to deploy the Azure Service Bus. But the Service Endpoint in Dynamics linking it needs to be deployed as well!&lt;/p&gt;
&lt;h2 id=&#34;whats-the-problem&#34;&gt;What&amp;rsquo;s the problem?&lt;/h2&gt;
&lt;p&gt;At first glance, it seems easy since you can put the Service Endpoint into a solution, but inspecting the customization.xml reveals the problem. The Namespace and Path (Queue or Topic) are in the export while the SAS Key is not. The SAS Key not being there is of course positive, but the Namespace and Path being present means deploying the solution will overwrite these properties. That would mean we are using the same Topic for each Environment and then differentiate in the subscriptions. And that&amp;rsquo;s something I suggest against due to security concerns as explained in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/exporting-events/&#34; &gt;the first post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/addToSolution.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Service Endpoints are found under the section Developer.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/solutionContent.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;This is an extract of the customization.xml&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;a-simple-solution&#34;&gt;A simple solution&lt;/h2&gt;
&lt;p&gt;So how to handle it? Well, without making some sort of handstand, you can follow the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a separate solution that only contains the Service Endpoint.&lt;/li&gt;
&lt;li&gt;Add the steps of it to your regular solution.&lt;/li&gt;
&lt;li&gt;Export the Service Endpoint solution and import it to the target environment.&lt;/li&gt;
&lt;li&gt;Connect the Plugin Registration Tool to the target environment.&lt;/li&gt;
&lt;li&gt;Change the settings for Namespace, Path and SAS Key to the correct values and save.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And then you never deploy the Service Endpoint Solution again! That is possible because the flexible parts like the Steps and Images are part of your other solution but it&amp;rsquo;s also a crux since this needs to be known by all other developers and the knowledge of how this is a one-time setup process must never vanish. Risky&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;a-pipeline-solution&#34;&gt;A pipeline solution&lt;/h2&gt;
&lt;p&gt;And also it is a manual effort. One-time per environment only, but there are always reasons why you prefer automation.
For this, we will use the help of the &lt;a class=&#34;link&#34; href=&#34;https://marketplace.visualstudio.com/items?itemName=WaelHamze.xrm-ci-framework-build-tasks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Power DevOps Tools&lt;/a&gt; of Wael Hamze. A tool we don&amp;rsquo;t need that often anymore since Microsoft has published a similar set of tasks, but it includes the perfect task for the job.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/pdtInstall.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Step 1: Install the Tooling&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/pdtDeploy.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Step 2: Deploy the Endpoint&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And the question of course is: What is the content of svcBus.json?
You can find the &lt;a class=&#34;link&#34; href=&#34;https://github.com/WaelHamze/xrm-ci-framework/blob/master/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/PluginRegistration/ServiceEndpt.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;class definition here&lt;/a&gt;, but I will provide you with a sample as well.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;7756786a-229c-ee11-be37-000d3a207f96&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__Name__&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;NamespaceAddress&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sb://__Name__.servicebus.windows.net/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Contract&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Topic&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__Path__&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;MessageFormat&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;JSON&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AuthType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SASKey&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;SASKeyName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Dataverse&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;SASKey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__SASKey__&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;UserClaim&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;UserId&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Description&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AuthValue&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Steps&amp;#34;&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Putting the secret directly into the file is another security concern, so I&amp;rsquo;ve already prepared this sample for the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/file-transform-v1?view=azure-pipelines&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;File Transform Task&lt;/a&gt;. This task will take your variables and replace the properties of the JSON file. Because of the array enclosure, we need to prefix the variables with a &amp;ldquo;0.&amp;rdquo; for the first element, &amp;ldquo;1.&amp;rdquo; for the second, and so on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/filetransform.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The file transformation task substitutes variables in the given JSON file.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/deployment/variables.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Because of the array notation, the variables have a prefix&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d still suggest putting the steps to a regular solution, there is no problem in deploying them regularly. You just have to make sure the Guid of the Service Endpoint is the same as on the Development Environment, but since that was in the exported customizations earlier, it should be no problem to configure properly.&lt;/p&gt;
&lt;p&gt;A quick note on YAML Pipelines: Variables can also be defined here with a dot, so it would look like&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;variables&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;Name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;mariuswodtke-dev&amp;#39;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or you use a variable group to have the traditional mode of entry which is probably preferred for the secret.&lt;/p&gt;
&lt;h2 id=&#34;a-generic-solution&#34;&gt;A generic solution&lt;/h2&gt;
&lt;p&gt;What if you can&amp;rsquo;t use the &lt;em&gt;Power DevOps Tools&lt;/em&gt;? E.g. because you are not using Azure DevOps or you are not allowed to install the Tasks. Well, we can still code this! After all, Service Endpoint is a table and we can update a record there, especially since we can assume that the Guid is constant.&lt;/p&gt;
&lt;p&gt;So with C#, this can look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;= &amp;#34;&lt;/span&gt;sb:&lt;span style=&#34;color:#75715e&#34;&gt;//mariuswodtke-dev.servicebus.windows.net/&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; secret = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;secret&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; svcenpt = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Entity(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;serviceendpoint&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Guid(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;7756786a-229c-ee11-be37-000d3a207f96&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;svcenpt.Attributes.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;namespaceaddress&amp;#34;&lt;/span&gt;, namespace);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;svcenpt.Attributes.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;saskey&amp;#34;&lt;/span&gt;, secret);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OrgService.Update(svcenpt);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or with Powershell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ps1&#34; data-lang=&#34;ps1&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Set-PSRepository PSGallery -InstallationPolicy Trusted
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Install-Module Microsoft.Xrm.Data.Powershell -Confirm:$false
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$conn = Get-CrmConnection -ConnectionString $connection
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$namespace = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sb://mariuswodtke-dev.servicebus.windows.net/&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$secret = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;secret&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Set-CrmRecord -conn $conn -EntityLogicalName serviceendpoint -Fields @{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;namespaceaddress&amp;#34;&lt;/span&gt; = $namespace; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;saskey&amp;#34;&lt;/span&gt; = $secret} -Id &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;7756786a-229c-ee11-be37-000d3a207f96&amp;#34;&lt;/span&gt; -Upsert
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And I think if you use something like Java and have created or updated a record in this language you can transfer the approach.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Deploying Endpoints is a little trickier than wished for but with the right strategy, we can make it work safely. After every deployment of the Service Endpoint, some settings like Namespace, Path and SASKey need to be updated. This can happen manually or be automated.
No matter what approach you choose, I recommend a separate solution that is deployed only once to each new environment e.g. because its Version Number is constant. Nevertheless, the Pipeline Solution/Generic Solution is a safeguard in this because it makes sure that the Service Endpoint is in the correct state after every deployment. They can also help with key rotations and with frequent environment changes.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: 18.12.2023 Power Pages Filtered Lists</title>
        <link>https://www.marius-wodtke.de/post/ppl/2023-advent/18/</link>
        <pubDate>Mon, 18 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2023-advent/18/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: 18.12.2023 Power Pages Filtered Lists" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce Copilot in Power Platform and learn something about Azure Communication Services. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/nwqBRN4edKc&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/nwqBRN4edKc&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/18/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Showing List Data in Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/list/</link>
        <pubDate>Sun, 17 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/list/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/list/cover.jpg" alt="Featured image of post [VIDEO] Showing List Data in Power Pages" /&gt;&lt;p&gt;Not only can we let users enter Data into Power Pages, but we can also show them the data we administer in Power Pages. So in this video, we will publish our product table to the users to browse our catalog.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/L56y4wQAPow&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/L56y4wQAPow&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/list/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: 14.12.2023 Power Pages Prefilling Forms</title>
        <link>https://www.marius-wodtke.de/post/ppl/2023-advent/14/</link>
        <pubDate>Thu, 14 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2023-advent/14/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: 14.12.2023 Power Pages Prefilling Forms" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce Copilot in Power Platform and learn something about Azure Communication Services. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/-brK9SDi43I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/-brK9SDi43I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/14/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: 11.12.2023 Configuration Migration Tool</title>
        <link>https://www.marius-wodtke.de/post/ppl/2023-advent/11/</link>
        <pubDate>Mon, 11 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2023-advent/11/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: 11.12.2023 Configuration Migration Tool" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce Copilot in Power Platform and learn something about Azure Communication Services. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/DssuFR-z1TY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/DssuFR-z1TY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/11/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Enhanced Collaboration Experience</title>
        <link>https://www.marius-wodtke.de/post/teams/collab-enhanced/</link>
        <pubDate>Sun, 10 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/collab-enhanced/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/cover.png" alt="Featured image of post Teams Integration: Enhanced Collaboration Experience" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-basic/&#34; &gt;the previous post&lt;/a&gt;, we explored most of the collaboration features already, but what is better in the &lt;em&gt;Enhanced Experience&lt;/em&gt;? The answer is simple, the creation/connection of Teams Teams (no, this is no mistake, it was not me naming the groups in Teams &amp;ldquo;Teams&amp;rdquo;) can happen from Dynamics.&lt;/p&gt;
&lt;h2 id=&#34;enabling-the-enhanced-experience&#34;&gt;Enabling the Enhanced Experience&lt;/h2&gt;
&lt;p&gt;Just like with the &lt;em&gt;Basic Experience&lt;/em&gt;, the &lt;em&gt;Enhanced Experience&lt;/em&gt; is enabled under &lt;em&gt;Sales Hub&lt;/em&gt; -&amp;gt; &lt;em&gt;App Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Chat and Collaborate&lt;/em&gt;. This time however it&amp;rsquo;s not enough to be an Administrator of Dynamics CE, but be a &amp;ldquo;Tenant Administrator&amp;rdquo;. My Global Admin role in my Tenant is of course enough, but for bigger corporations, this means that one of the few administrative Accounts needs to be enabled as an Administrator for the Dynamics Environments as well to accept the permissions pictured in the second screenshot. Don&amp;rsquo;t forget to save your new settings!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/EnableEnhanced.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/PermissionsEnhanced.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connecting-a-team&#34;&gt;Connecting a Team&lt;/h2&gt;
&lt;p&gt;After enabling the enhanced integration, at first, there seems to be no difference. However, the &lt;em&gt;italic&lt;/em&gt; text at the bottom of the dialog has vanished and when clicking &amp;ldquo;Get Started&amp;rdquo; in the &lt;em&gt;Collaborate&lt;/em&gt; dialog, it will not take you to teams directly but show you some more options.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/Collaborate.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;We can see the list of our Teams directly in the wizard and also have the option to create a new Team which we will explore in the next section. After selecting the team, we also have to select a channel as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/SelectTeam.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/SelectChannel.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then we may select members to be added to the team (and channel if it&amp;rsquo;s private). Here you will not just see anyone but people you as Sales Team (via Connections),&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/SelectMembers.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/Connection.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After the dialog completes, we are redirected to Teams (in a new Tab), directly to the channel&amp;rsquo;s new Tab for the record. As expected the people I selected to be added to the Team are now members.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/Result1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/Result2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;new-team-and-channel&#34;&gt;New Team and Channel&lt;/h2&gt;
&lt;p&gt;In the previous section, we selected existing Teams and Channels. But in both steps, we had the option to create something new as well. Creating a new Team already comes with a General channel, but we also have the option to create new channels, even private ones. No other options are presented (*aside from the Sensitivity Label in the next section), so any special channel options you will have to set in Teams after being redirected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/NewTeam.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/NewChannel.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/ResultNew.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;sensitivity-labels&#34;&gt;Sensitivity Labels&lt;/h2&gt;
&lt;p&gt;If you want to enable Sensitivity Labeling in the Team creation as well (the third switch under &lt;em&gt;Sales Hub&lt;/em&gt; -&amp;gt; &lt;em&gt;App Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Chat and Collaborate&lt;/em&gt;), some more permissions are required for Dynamics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/PermissionLabels.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then, to see the labels, they need to be enabled for Teams in their Scope. If you think about leveraging this option, I assume your organization already uses these labels for Teams and they are already set up. If your tenant looks like mine and is totally blank in this regard, &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/purview/sensitivity-labels-teams-groups-sites#how-to-enable-sensitivity-labels-for-containers-and-synchronize-labels&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;follow the steps listed here&lt;/a&gt; (including the quite extensive sub-articles) to configure. And after that give Entra ID some time to process it to prevent yourself from questioning your sanity!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/PolicyScope.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Once you start seeing the labels in the regular creation process within Teams you should also start seeing them in the dialog in Teams.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/SelectSensitivity.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-enhanced/ResultSensitivity.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;outlook&#34;&gt;Outlook&lt;/h2&gt;
&lt;p&gt;As we saw, the enhancement here is mainly that we get this dialog for the team directly in Dynamics and we don&amp;rsquo;t have to work in 2 applications when we start in Dynamics, which is always great.
If you and your users will love this is mainly a question of what is their primary application, Teams or Dynamics CE. The Dynamics users will probably love this because they never leave their home soil to connect with their colleagues. A user that primarily uses Teams will probably just start the process from Teams as before.&lt;/p&gt;
&lt;p&gt;Details may always change. Stay up to date with &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/sales/teams-integration/teams-collaboration-enhanced-experience&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official documentation&lt;/a&gt; as well!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: 09.12.2023 Power Pages - Anonymous Case</title>
        <link>https://www.marius-wodtke.de/post/ppl/2023-advent/09/</link>
        <pubDate>Sat, 09 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2023-advent/09/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: 09.12.2023 Power Pages - Anonymous Case" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce Copilot in Power Platform and learn something about Azure Communication Services. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6rx1N0e6b1Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/6rx1N0e6b1Y&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/09/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Teams Integration: Basic Collaboration Experience</title>
        <link>https://www.marius-wodtke.de/post/teams/collab-basic/</link>
        <pubDate>Sun, 03 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/teams/collab-basic/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/cover.png" alt="Featured image of post Teams Integration: Basic Collaboration Experience" /&gt;&lt;p&gt;Teams is probably the most important app for almost all business users in companies that work in the Microsoft ecosystem. Therefore we should discuss the Dynamics Teams integration and what better way to do no than first exploring the standard features.&lt;/p&gt;
&lt;h2 id=&#34;basic-vs-enhanced-experience&#34;&gt;Basic vs Enhanced Experience&lt;/h2&gt;
&lt;p&gt;MS quite cleverly named these to promote the fact that the &lt;em&gt;Enhanced Experience&lt;/em&gt; is better. It requires a Tenant Admin to enable while the &lt;em&gt;Basic Experience&lt;/em&gt; does, but getting an Admin on board for such an important integration should be no problem.
Nevertheless, we will only discuss the &lt;em&gt;Basic Experience&lt;/em&gt; in this post and save the &lt;em&gt;Enhanced Experience&lt;/em&gt; for another post. That is because many features stay the same, so we will first set a basis here and look at the improvements that can be achieved later.&lt;/p&gt;
&lt;h2 id=&#34;enabling-teams&#34;&gt;Enabling Teams&lt;/h2&gt;
&lt;p&gt;To enable the Teams integration, go to &lt;em&gt;Sales Hub&lt;/em&gt; -&amp;gt; &lt;em&gt;App Settings&lt;/em&gt; -&amp;gt; &lt;em&gt;Chat and Collaborate&lt;/em&gt;. Here you can flick the switch &amp;ldquo;&lt;em&gt;Turn on the linking of Dynamics 365 records to Microsoft Teams channels&lt;/em&gt;&amp;rdquo;. Saving this will take a while. After that, you will find the &lt;em&gt;Collaborate&lt;/em&gt; button in the ribbon of the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/sales/teams-integration/teams-collaboration#record-types-that-support-microsoft-teams-integration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;supported records&lt;/a&gt; which for example include Lead, Account and Contact. I will be using Opportunity throughout my demonstration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/EnableIntegration.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; Dynamics uses caching for the ribbon, so a simple reload of the record might not be enough for your browser to notice the new button.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;using-collaborate-for-the-first-time&#34;&gt;Using Collaborate for the first time&lt;/h2&gt;
&lt;p&gt;This will show a screen to take you to Teams. I use the web version here. In Teams, the Dynamics App first has to be added to the Tenant if it is not already installed. So while you did not need an Admin to enable the feature, you might need them now to install the app. If already in the tenant, the dialog that is automatically opened offers to open the app.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/CollaborateButton.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/GetStarted.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AppAdd.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Adding the app requires permission to do so in Teams&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AppDialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;dynamics-app-in-teams&#34;&gt;Dynamics App in Teams&lt;/h2&gt;
&lt;p&gt;We are greeted with a Dashboard, but that demands to configure it in the settings. After selecting the environment and app there, it shows that this &amp;ldquo;Dashboard&amp;rdquo; is the whole app. Don&amp;rsquo;t get me wrong, I&amp;rsquo;m not complaining here, I like this much more than just displaying a Dashboard as this is much more powerful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AppNew.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AppSettings.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/App.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connect-to-a-team&#34;&gt;Connect to a Team&lt;/h2&gt;
&lt;p&gt;In a Teams channel, you have the + symbol at the top to add tabs, the Dynamics app will be displayed here.
First, we will add a record to the channel. You can search for a record, but chances are that the record you want to connect is already listed in &lt;em&gt;Recents&lt;/em&gt; because you recently edited it. And then it shows in this tab as if you had it open in the real app. But this time without the navigation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToTeam1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToTeam2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToTeamRecord.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/ConnectedRecord.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;We can also add a view, even to the same channel. This can even work with personal views (if shared with the members of the team), which can be a nice feature to pin the records that are relevant to a sales team. Since you can click records in the view and navigate further through lookups and subgrids, the users might not have the urge to switch to the full app we explored earlier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToTeamView.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToTeam3.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Issues:&lt;/em&gt;&lt;/strong&gt; I had some trouble here at first doing it via the plus. After using the dropdown &amp;ldquo;Add to a team&amp;rdquo; once, it started working for all the teams. Unless members are not allowed to add apps.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/Fix.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/Fix2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;documents&#34;&gt;Documents&lt;/h2&gt;
&lt;p&gt;Did you see that &amp;ldquo;&lt;em&gt;This record is successfully connected to Dynamics 365&lt;/em&gt;&amp;rdquo; with the record we added to the channel? This action was not only manipulating Teams but also Dynamics where a &lt;em&gt;Microsoft Teams Collaboration Entity&lt;/em&gt; is created, linking to the channel. This allows you to visit the &lt;em&gt;Documents&lt;/em&gt; Tab in the Opportunity and see the documents belonging to the Sharepoint Site connected to the Teams channel. For the demo, I created a file in the &lt;em&gt;Files&lt;/em&gt; Tab of the channel and I can see it in Dynamics as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/Documents.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Also, if you connected a &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/&#34; &gt;&amp;ldquo;regular&amp;rdquo; Sharepoint location&lt;/a&gt; before connecting the Teams, both locations can exist side by side.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/Sharepoint.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connect-to-a-chat&#34;&gt;Connect to a chat&lt;/h2&gt;
&lt;p&gt;In my chat with Alan Steiner, I added the record as well. This at first looked very similar to adding a record to a team, but at second glance there are some differences. You don&amp;rsquo;t have the option to link a view and also the connected record does not show the &amp;ldquo;&lt;em&gt;This record is successfully connected to Dynamics 365&lt;/em&gt;&amp;rdquo; message, meaning that the files from this chat are not connected to Dynamics. Still useful when collaborating without a full team!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToChat1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToChat2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToChat3.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connect-to-a-meeting&#34;&gt;Connect to a meeting&lt;/h2&gt;
&lt;p&gt;In a meeting, you can also connect to a record. Similar to the chat, no views and no &amp;ldquo;full connection&amp;rdquo;. Also as a side pane, this looks different and not as powerful as the full forms. But it allows you to create tasks and notes for the Opportunity without leaving the context of the Meeting and should someone need the full form everyone now has the &amp;ldquo;pop-out&amp;rdquo; button at the top right as well. A link in the chat would also do, but certainly, it is more professional if you prepare this ahead of time in the meeting options.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToMeeting1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToMeeting2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/AddToMeeting3.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When opening a meeting from the calendar, you can add the Dynamics app.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;using-collaborate-again&#34;&gt;Using Collaborate again&lt;/h2&gt;
&lt;p&gt;Back when we added our record to a team, it got connected to Dynamics. When we select the &lt;em&gt;Collaborate&lt;/em&gt; button again, the team will be shown. We still can not do much from Dynamics apart from jumping to the correct team. &lt;em&gt;+ Create a new connection&lt;/em&gt; works just like the &lt;em&gt;Get Started&lt;/em&gt; in the beginning and takes us to Teams.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/teams/collab-basic/ExistingCollaborate.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;outlook&#34;&gt;Outlook&lt;/h2&gt;
&lt;p&gt;That was quite a heap of useful features with this integration. But did you notice how everything starts at Teams? The only direct action from Dynamics you can take is to add/edit files once the Teams connection is established. And that will change when we enable the &lt;em&gt;Enhanced Experience&lt;/em&gt; which we will explore in the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/collab-enhanced/&#34; &gt;next post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Details may always change. Stay up to date with &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/sales/teams-integration/teams-collaboration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the official documentation&lt;/a&gt; as well!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Power Platform Advent Calendar: 01.12.2023 Kick-Off</title>
        <link>https://www.marius-wodtke.de/post/ppl/2023-advent/01/</link>
        <pubDate>Fri, 01 Dec 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/ppl/2023-advent/01/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/cover.jpg" alt="Featured image of post Power Platform Advent Calendar: 01.12.2023 Kick-Off" /&gt;&lt;p&gt;Are you ready to unleash the power of Power Platform? Join us for a festive and fun video series that will show you how to create amazing apps, govern your tenant, introduce Copilot in Power Platform and learn something about Azure Communication Services. Each day, you will get a new tip from our experts that will help you learn something new, solve a problem, or improve your skills. Whether you are a beginner or a pro, you will find something useful and interesting in this advent calendar. Don&amp;rsquo;t miss this opportunity to power up your productivity and creativity with Power Platform!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/7-uPmnHs9pk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/7-uPmnHs9pk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/ppl/2023-advent/01/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Adding a Form to Power Pages</title>
        <link>https://www.marius-wodtke.de/post/power-pages/form/</link>
        <pubDate>Sun, 26 Nov 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/form/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/form/cover.jpg" alt="Featured image of post [VIDEO] Adding a Form to Power Pages" /&gt;&lt;p&gt;This time we will add a form to our Power Page. This will allow users of the Website to enter data and submit it directly to Dataverse. Again a simple form can be set up in minutes and will provide you with a Dataverse-like feeling of entering and submitting data.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/IwQ0szTYAb0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/IwQ0szTYAb0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/form/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Service Bus 4: Infrastructure automation with Biceps</title>
        <link>https://www.marius-wodtke.de/post/servicebus/biceps/</link>
        <pubDate>Sun, 19 Nov 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/servicebus/biceps/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/biceps/cover.png" alt="Featured image of post Service Bus 4: Infrastructure automation with Biceps" /&gt;&lt;p&gt;Technically you know everything you need to from the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/&#34; &gt;first 3 articles&lt;/a&gt; of this series. But as you develop more and more functionality and the amount of subscriptions or topics grows, there will be configuration errors happening. The most typical thing: Someone configures a new subscription for dev and test, finishes their feature and then forgets about it. Some time later the listener gets released to production and won&amp;rsquo;t start due to the missing subscription.&lt;/p&gt;
&lt;p&gt;This can be mitigated by using Infrastructure as Code (IaC). This ensures that when the feature is completed and the code for the listener is checked in, the infrastructure for it is checked in alongside, preferably in the same Pull Request.&lt;/p&gt;
&lt;h2 id=&#34;bicep&#34;&gt;Bicep&lt;/h2&gt;
&lt;p&gt;The tool we will be using for this today is Bicep. It was developed by Microsoft for deploying Azure Resources. I won&amp;rsquo;t go too much into the very basics of this tool, if you are stuck with my explanations, MS Learn has a &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/training/paths/fundamentals-bicep/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;full learning path&lt;/a&gt; providing much more help.&lt;/p&gt;
&lt;p&gt;But that much information we need: To deploy (execute, interpret, whatever) the contents of a .bicep file, we will need &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=azure-cli&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the Azure CLI&lt;/a&gt; installed. I also recommend installing the Bicep Extension if you are using VS Code, as this will grant you autocomplete features as well as linting, always welcome features if not having a firm grasp on a new language.&lt;/p&gt;
&lt;h2 id=&#34;servicebusbicep&#34;&gt;servicebus.bicep&lt;/h2&gt;
&lt;p&gt;In a bicep file, you define resources that receive parameters and when executed, the resource will be turned into a real thing in Azure. We can also define modules that group such resources, but let&amp;rsquo;s worry about that later, just be aware, that we are about to define a module in the servicebus.bicep file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resourceGroup&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusResource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;sku&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Standard&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;tier&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Standard&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our Resource here is &lt;code&gt;Microsoft.ServiceBus/namespace&lt;/code&gt;. It has a location like &amp;ldquo;Western Europe&amp;rdquo;, an SKU which I hardcoded here to &lt;em&gt;Standard&lt;/em&gt; and &lt;em&gt;Basic&lt;/em&gt; won&amp;rsquo;t offer the Topic I&amp;rsquo;m about to configure and &lt;em&gt;Premium&lt;/em&gt; is quite expensive and will only be used in heavy load or special security requirements. Notice the &lt;code&gt;param serviceBusName&lt;/code&gt; which is something you need to pass if you want to deploy this.&lt;/p&gt;
&lt;h2 id=&#34;topicbicep&#34;&gt;topic.bicep&lt;/h2&gt;
&lt;p&gt;Next is another module I called topic. Here are some interesting bits as well:
The service bus has an &lt;code&gt;existing&lt;/code&gt; before its definition. That means we don&amp;rsquo;t want to create it, but we want to validate its existence. And we can use it as the parent for our topic after that.
The topic parameter is an object, so you need to pass something more complex. This of course is a parameter you don&amp;rsquo;t want to specify in the CLI, but later we will define a main.bicep where we can code some complex objects.
Lastly, this module calls another module, called subscription.bicep. And it does so with a loop &lt;code&gt;[for subscription in topic.subscriptions: ...]&lt;/code&gt;. The &lt;code&gt;dependsOn&lt;/code&gt; also specifies that this deployment cannot start before the topic is created.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusResource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;existing&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusTopic&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusResource&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;autoDeleteOnIdle&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P10675198DT2H48M5.477S&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Never
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;defaultMessageTimeToLive&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P14D&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subscriptionModule&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;subscription.bicep&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subscription&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;subscriptions&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;${deployment().name}-${substring(subscription.name,0,min(length(subscription.name),25))}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;params&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusResource&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;topicName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;subscription&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subscription&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dependsOn&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusTopic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;subscriptionbicep&#34;&gt;subscription.bicep&lt;/h2&gt;
&lt;p&gt;Of course, this file also needs to be present. And its probably the most important for this article as the subscriptions and rules are the thing that connects this article to the Dynamics Service Endpoint specifics.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topicName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subscription&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusResource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;existing&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusTopic&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;existing&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topicName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusResource&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusSubscription&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusTopic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subscription&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;isClientAffine&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lockDuration&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;PT5M&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;requiresSession&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;defaultMessageTimeToLive&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P14D&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;deadLetteringOnMessageExpiration&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;deadLetteringOnFilterEvaluationExceptions&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;maxDeliveryCount&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Active&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;enableBatchedOperations&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;autoDeleteOnIdle&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P10675198DT2H48M5.477S&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Never
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusRule&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Microsoft.ServiceBus/namespaces/topics/subscriptions/rules@2022-10-01-preview&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subscription&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rules&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusSubscription&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;filterType&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;CorrelationFilter&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;correlationFilter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;union&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;contains&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;entity&amp;#39;&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://schemas.microsoft.com/xrm/2011/Claims/EntityLogicalName&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entity&lt;/span&gt; } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {}, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;contains&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://schemas.microsoft.com/xrm/2011/Claims/RequestName&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt; } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, I&amp;rsquo;ve hardcoded most of the settings for the Subscription here. That&amp;rsquo;s because I don&amp;rsquo;t see any necessity to alter any of these between different subscriptions of the same endpoint. For the rules, this is a different story. Each has its unique filters, for the Dataverse endpoints, these will usually be the &lt;em&gt;EntityLogicalName&lt;/em&gt; and &lt;em&gt;RequestName&lt;/em&gt;. Notice the ternary operators that allow for one of the parameters to be empty. This for example allows for configuration on the message &lt;em&gt;Associate&lt;/em&gt; which simply has no entity.&lt;/p&gt;
&lt;h2 id=&#34;mainbicep&#34;&gt;main.bicep&lt;/h2&gt;
&lt;p&gt;The last file for today: main.bicep. This puts it all together, provides some parameters and is the one we are going to deploy.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resourceGroup&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topics&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;dataverse&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;subscriptions&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;account-export&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;rules&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountUpdate&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;entity&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;account&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Update&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accountCreate&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;entity&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;account&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Create&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusModule&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;servicebus.bicep&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;${deployment().name}-${serviceBusName}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;params&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topicModule&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;topic.bicep&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topics&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;${deployment().name}-${topic.name}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;params&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dependsOn&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceBusModule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve seen object parameters in the modules for topic and subscription and here we see how to fill this parameter. It became a rather deep object with the topic, its subscriptions and the rules of this subscription. A note on that: I expected list objects to be separated by a comma, but the linter quickly flagged that mistake to me.&lt;/p&gt;
&lt;p&gt;This file still specifies a parameter to be set &lt;code&gt;serviceBusName&lt;/code&gt;. This will have to be specified in the CLI when calling the file.&lt;/p&gt;
&lt;h2 id=&#34;deploying&#34;&gt;Deploying&lt;/h2&gt;
&lt;p&gt;The first step, logging in &lt;code&gt;az login&lt;/code&gt;. This will open a browser to handle the authentication.
Next depending if you have multiple subscriptions: &lt;code&gt;az account set --subscription &amp;quot;MySubscription&amp;quot;&lt;/code&gt;.
And then we can already deploy &lt;code&gt;az deployment group create --name bicep --resource-group sbtest --template-file &#39;main.bicep&#39; --mode Incremental&lt;/code&gt;. The 2 things you might want to change here are a) the &lt;code&gt;--resource-group&lt;/code&gt; which is probably not called sbtest in your environment and b) the deployments &lt;code&gt;--name&lt;/code&gt; could be more descriptive. But please note that there is a maximum of 64 characters allowed for deployment names and inside the modules the name is appended with the current resource name! For the subscription deployment this is attempted to mitigate assuming there might be quite long subscription-names &lt;code&gt;name: &#39;${deployment().name}-${substring(subscription.name,0,min(length(subscription.name),25))}&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After the execution, you can check on the successful (or failed) deployment at the resource group.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/biceps/deployment.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;With our 4 levels of nested resources (namespace, topic, subscription, rule) we also created 4 files to specify those resources. servicebus.bicep to create the namespace, topic.biceps to crate the topic below and subscription.bicep creates the subscription and rules below the topic. main.bicep now specifies the parameter object and calls the modules and is the file that is referenced in the deployment.
But with those in place, all we need to do for new functionality with a new subscription is to add it to the &lt;code&gt;topics&lt;/code&gt; variable and redeploy. This approach also ensures the same settings for all subscriptions and allows you to change them for all subscriptions in bulk. Nifty, if you have 20+ subscriptions.
I hope this approach saves you some headache on deployment day as it should just be a CLI command to be run and if it worked for the test/staging environment, why wouldn&amp;rsquo;t it for the production?&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Dynamics CE Business Rules: Manipulating Status (Reasons)</title>
        <link>https://www.marius-wodtke.de/post/other/br-statusreason/</link>
        <pubDate>Sun, 12 Nov 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/br-statusreason/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/cover.png" alt="Featured image of post Dynamics CE Business Rules: Manipulating Status (Reasons)" /&gt;&lt;p&gt;I had to write this article because I was unable to find this information online: &amp;ldquo;Can you set a specific Status/Status Reason with a Business Rules in Dynamics?&amp;rdquo; So I had to go blind and just try and here is the outcome for you to make a confident statement about whether you can solve your requirement with a Business Rule (BR).&lt;/p&gt;
&lt;p&gt;To set the scene, let&amp;rsquo;s dive a little into my requirements. Quotes need to be confirmed by a certain group of people before they can be offered to the customer. This shall prevent excessive discounts. After a quote is confirmed it shall not be changed anymore. The proposed solution was to use Column Security (formerly called Field Security) and add the people who can confirm those quotes to a Column Security Profile to allow them to change that field. The problem is, that you can&amp;rsquo;t activate Column Security on the Status/Status Reason columns. It would not make any sense anyway because you then would also be unable to win or lose a quote afterward without access to the column. So the idea is to make a simple switch (boolean) with Column Security to only allow the approvers to change it and make a Business Rule to activate the quote when the field is set to prevent any changes after the quote is confirmed.&lt;/p&gt;
&lt;h2 id=&#34;setting-statecode&#34;&gt;Setting StateCode&lt;/h2&gt;
&lt;p&gt;We will be starting with a Business Rule set to &lt;em&gt;Entity&lt;/em&gt; Scope. It sets the Status (StateCode) to active for a &lt;em&gt;Draft&lt;/em&gt; Quote that is &lt;em&gt;Confirmed&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatecode.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatecodeStart.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatecodeChanged.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatecodeDone.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The rule works as expected, when the Confirmed field is set, the Status switches to Active and after saving the Quote is locked. The Status Reason was automatically switched to the default choice &lt;em&gt;In Progress&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;setting-statuscode&#34;&gt;Setting StatusCode&lt;/h2&gt;
&lt;p&gt;Next, the same again (&lt;em&gt;Entity&lt;/em&gt; Scope) just with setting Status Reason (StatusCode). Because in the standard the Status Reason &lt;em&gt;In Progress&lt;/em&gt; is there for both &lt;em&gt;Draft&lt;/em&gt; and &lt;em&gt;Active&lt;/em&gt;, I&amp;rsquo;ve introduced a custom choice called &lt;em&gt;Confirmed&lt;/em&gt; as well. This has the nice side effect, that it is distinguishable from a normal activation via the &lt;em&gt;Activate&lt;/em&gt; button which would default to &lt;em&gt;In Progress&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatuscode.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatuscodeStart.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatuscodeChanged.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityStatuscodeDone.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This does not work!&lt;/strong&gt; The combination &lt;em&gt;Confirmed&lt;/em&gt; and &lt;em&gt;Draft&lt;/em&gt; is obviously illegal but unlike changing Status in the previous section this situation is not autocorrected by the system and is therefore just not changed.&lt;/p&gt;
&lt;h2 id=&#34;setting-statecode-and-statuscode&#34;&gt;Setting StateCode and StatusCode&lt;/h2&gt;
&lt;p&gt;If setting only the Status Reason does not work, surely first setting Status and then Status Reason will work, right?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityBoth.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityBothStart.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityBothChanged.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/EntityBothDone.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Yes, it does. And by the way, it also works if we change the order and set Status Reason first, at least for the &lt;em&gt;Entity&lt;/em&gt; Scope this does not matter.&lt;/p&gt;
&lt;h2 id=&#34;using-form-scopes&#34;&gt;Using Form Scopes&lt;/h2&gt;
&lt;p&gt;When using Form Scopes instead it is usually very important to ensure all relevant fields are on the form. If they are visible or marked as read-only does not matter, they just need to be there. This is true for all fields manipulated by an action as well as all fields being evaluated in a condition.
But Status is different. The Form Editor claims it&amp;rsquo;s not on the form, but the Business Rule still works.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/FormBothStart.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/FormBothChanged.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/br-statusreason/FormBothDone.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;But as soon as I remove setting the Status again it stops working.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Conclusion time. Setting Status and Status Reason does work with Business Rules, just make sure that you set Status if it needs to change, Dynamics will not do that for you if you only set the Status Reason. If however, you set only Status, the Status Reason will be automatically corrected to the default choice for the given Status.&lt;/p&gt;
&lt;p&gt;One caveat with this and Quotes: When revising, all fields are copied, including the &lt;em&gt;Confirmed&lt;/em&gt; field&amp;hellip; So we need a clever solution for resetting that field too.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Extended) 3: Using the REST Endpoint</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/custom/rest/</link>
        <pubDate>Sun, 05 Nov 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/custom/rest/</guid>
        <description>&lt;p&gt;The &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/pnpcore/&#34; &gt;last time around&lt;/a&gt; we discussed the usage of PnP.Core as an easy-to-use helper to interact with Sharepoint from client code. The library has far too many dependencies to be suited for plugins, but that&amp;rsquo;s exactly what we will be looking into.&lt;/p&gt;
&lt;h2 id=&#34;sharepoint-rest-api&#34;&gt;Sharepoint REST API&lt;/h2&gt;
&lt;p&gt;The fact that Microsoft likes REST services has already saved me more than once with Azure integrations. And usually, the APIs are quite self-explaining once you make the first successful request.
Sharepoint is not much different, let&amp;rsquo;s take a look at some &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/get-to-know-the-sharepoint-rest-service?tabs=csom#sharepoint-rest-endpoint-examples&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;examples of Microsoft&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Get items from a list:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET https://{site_url}/_api/web/lists/getbytitle(&amp;#39;listname&amp;#39;)/items

Accept: &amp;#34;application/json;odata=verbose&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add a new item to a list:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;POST https://{site_url}/_api/web/lists/getbytitle(&amp;#39;listname&amp;#39;)/items

Content-Type: &amp;#34;application/json;odata=verbose&amp;#34;
Accept: &amp;#34;application/json;odata=verbose&amp;#34;

{
  &amp;#34;__metadata&amp;#34;: {
    &amp;#34;type&amp;#34;: &amp;#34;SP.Data.listname.ListItem&amp;#34;
  },
  &amp;#34;Title&amp;#34;: &amp;#34;MyItem&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That sounds easy, right? And up until now I always found how to do things in that API just by googling things like &amp;ldquo;Sharepoint REST API Upload File&amp;rdquo; or &amp;ldquo;Sharepoint REST API Set Metadata&amp;rdquo; (-&amp;gt; this one needs special headers!).&lt;/p&gt;
&lt;p&gt;But if we were to try any of these requests, none of them would work. They&amp;rsquo;d all return a 401 because we are missing the &lt;em&gt;Authorization&lt;/em&gt; Header.&lt;/p&gt;
&lt;h2 id=&#34;authorization&#34;&gt;Authorization&lt;/h2&gt;
&lt;p&gt;For authorizing the app registration from &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/appregistration/&#34; &gt;the first post&lt;/a&gt; it is important to note, that the API does not accept tokens created with a client secret. The token needs to be created with a certificate and that is why we set up the app registration that way.&lt;/p&gt;
&lt;p&gt;But getting a token with a certificate was not an easy task without the help of other NuGets, this topic has received &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/other/certificate-authentication/&#34; &gt;a separate article&lt;/a&gt;. So here is a quick rundown of the possibilities with only a little explanation.&lt;/p&gt;
&lt;h2 id=&#34;using-microsoftidentitymodelclientsactivedirectory&#34;&gt;Using Microsoft.IdentityModel.Clients.ActiveDirectory&lt;/h2&gt;
&lt;p&gt;This NuGet is nice because it does need to be ILMerged or similar since it&amp;rsquo;s loaded to the Dynamics Sandboxes. But it&amp;rsquo;s also deprecated! That means it will probably stop working as soon as the Dynamics SDK is loaded to the sandboxes no longer rely on this NuGet. So probably not the best option, but certainly the easiest for a quick PoC.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.IdentityModel.Clients.ActiveDirectory;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cert = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; X509Certificate2(certBytes); &lt;span style=&#34;color:#75715e&#34;&gt;// certBytes is the content of the .pfx certificate file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; certificate = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ClientAssertionCertificate(clientId, cert);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AuthenticationContext context = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; AuthenticationContext(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AuthenticationResult authenticationResult = context.AcquireTokenAsync(resource, certificate).Result; &lt;span style=&#34;color:#75715e&#34;&gt;// resource is the url of the sharepoint without site, e.g. https://crmXXX.sharepoint.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; authorizationHeaderContent = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Bearer {authenticationResult.AccessToken}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;using-microsoftidentityclient&#34;&gt;Using Microsoft.Identity.Client&lt;/h2&gt;
&lt;p&gt;This is the NuGet that you should use instead of &lt;code&gt;Microsoft.IdentityModel.Clients.ActiveDirectory&lt;/code&gt;. The problem here is the usual NuGet problem: You can only upload one assembly. The options are the unsupported ILMerge or the plugin packages which are in preview at the time of writing. I&amp;rsquo;ve tried this exact NuGet in a &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/&#34; &gt;dedicated article to plugin packages&lt;/a&gt;, so it will work, but as said, it&amp;rsquo;s in preview.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Identity.Client;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cert = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; X509Certificate2(certBytes); &lt;span style=&#34;color:#75715e&#34;&gt;// certBytes is the content of the .pfx certificate file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; client = ConfidentialClientApplicationBuilder.Create(clientId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .WithCertificate(cert)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .WithAuthority(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}&amp;#34;&lt;/span&gt;).Build();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; authenticationResult = client.AcquireTokenForClient(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;[] { &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/.default&amp;#34;&lt;/span&gt; }).ExecuteAsync().Result; &lt;span style=&#34;color:#75715e&#34;&gt;// resource is the url of the sharepoint without site, e.g. https://crmXXX.sharepoint.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; authorizationHeaderContent = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Bearer {authenticationResult.AccessToken}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bodging-something&#34;&gt;Bodging something&lt;/h2&gt;
&lt;p&gt;So lastly the method that does not require any NuGets. Microsoft develops Microsoft.Identity.Client is Open Source which allows us to grab the important bits for our certificate authentication.&lt;/p&gt;
&lt;p&gt;For the code below to work you will need to copy in some of the classes from Microsoft.Identity.Client.
Since my last article, the classes moved! If the links are dead again, try to search the file names in the repository.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JWT Models: &lt;a class=&#34;link&#34; href=&#34;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Internal/JsonWebToken.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Internal/JsonWebToken.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Signing a JWT Token: &lt;a class=&#34;link&#34; href=&#34;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/CommonCryptographyManager.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/CommonCryptographyManager.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Base64Conversions: &lt;a class=&#34;link&#34; href=&#34;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Utils/Base64UrlHelpers.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Utils/Base64UrlHelpers.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; I modified and stripped the classes to then not need other things like System.Text.Json and for example the Base64UrlHelper we only need Encode.&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cert = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; X509Certificate2(certBytes); &lt;span style=&#34;color:#75715e&#34;&gt;// certBytes is the content of the .pfx certificate file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; jwt = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; JsonWebToken(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; CommonCryptographyManager(), clientId, &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; signedToken = jwt.Sign(cert, Base64UrlHelpers.Encode(cert.GetCertHash()), &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; form = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grant_type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_credentials&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;, clientId },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_assertion_type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;urn:ietf:params:oauth:client-assertion-type:jwt-bearer&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_assertion&amp;#34;&lt;/span&gt;, signedToken },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/.default&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; tokenString = Client.Post(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token&amp;#34;&lt;/span&gt;, form);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; token = JsonSerializer.Deserialize&amp;lt;AzureActiveDirectoryToken&amp;gt;(tokenString).access_token;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; authorizationHeaderContent = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Bearer {token}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;No matter what method for gaining an access token you chose, now you will have a JWT. With that let&amp;rsquo;s look at one last request, and now one that is working:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;POST https://{site_url}/_api/web/folders

Content-Type: &amp;#34;application/json;odata=verbose&amp;#34;
Accept: &amp;#34;application/json;odata=verbose&amp;#34;
Authorization: &amp;#34;Bearer ey...&amp;#34;

{
  &amp;#34;__metadata&amp;#34;: {
    &amp;#34;type&amp;#34;: &amp;#34;SP.Folder&amp;#34;
  },
  &amp;#34;ServerRelativeUrl&amp;#34;: &amp;#34;documents/myFolder&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You guessed it? POST on the &lt;em&gt;folders&lt;/em&gt; API of course creates a new folder and in the body, we are passing that we want to create, a folder with the relative URL &amp;ldquo;documents/myFolder&amp;rdquo;.
With that, I will send you to the depths of the internet to find your exact use case, but keep your eyes peeled for some follow-up with more operations.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My First Shot at Sales Copilot</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/sales-copilot/</link>
        <pubDate>Sun, 29 Oct 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/sales-copilot/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/cover.png" alt="Featured image of post My First Shot at Sales Copilot" /&gt;&lt;p&gt;Microsoft is all in on AI. And that means we won&amp;rsquo;t get around it. But don&amp;rsquo;t get me wrong as well here, generative AI is cool! Naturally, by now the first customer request reached me and I&amp;rsquo;m skilling up on what the Sales Copilot for Dynamics CE can do right now (October 2023) and you will join me on that journey.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;Setup of the Copilot SHOULD be straightforward, but as my tenant is not in North America the cross-border data processing agreement kept me searching for a while. It&amp;rsquo;s in the bottom right corner of the environment page. That is the first hurdle: Is this agreement really ok for your organization? But to be honest I think MS will roll this out worldwide, so that might be a matter of waiting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/agreement.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After agreeing to that, the yellow box you see in the next screenshot will disappear and you can enable the assistant app by app or just everywhere with the checkboxes at the top. The settings are found at &lt;em&gt;Sales Hub &amp;gt; App Settings &amp;gt; Sales Copilot (preview)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/setup.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After a full refresh, the selected apps will now show the Copilot pane on the right side of the screen.&lt;/p&gt;
&lt;h2 id=&#34;settings&#34;&gt;Settings&lt;/h2&gt;
&lt;p&gt;Apart from the apps you can also configure the fields to be evaluated for summaries and &amp;ldquo;catch up&amp;rdquo;. Catch-up shall evaluate audit history for you, well at least for up to 10 fields.
In general, I think we will end up with specialized views for the correct field selection as this will be vital to the output of the AI and you probably don&amp;rsquo;t want to make any compromises with the requirements for a regular view.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/SummarySettings.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/CatchUpSettings.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The preview feature &amp;ldquo;Compose&amp;rdquo; has no settings at the time of writing.&lt;/p&gt;
&lt;h2 id=&#34;summarize&#34;&gt;Summarize&lt;/h2&gt;
&lt;p&gt;From now I will use this like a user. And as a user of ChatGPT, I expected chat to be a chat. Well, the first attempt did not go so well and the whole evening did not go any different: The &amp;ldquo;suggested requests&amp;rdquo; are the only requests this bot understands.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Summarize1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Summarize2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So this is the summarize request, you type the name of a record (use a slash &amp;ldquo;/&amp;rdquo; to get an autocomplete) and it will use the 7 fields from the view in settings to generate a short text.&lt;/p&gt;
&lt;h2 id=&#34;catch-up&#34;&gt;Catch-up&lt;/h2&gt;
&lt;p&gt;You can use the catch-up feature with the phrase &amp;ldquo;show me recent changes for&amp;rdquo; and it will evaluate the 10 fields from the view in settings for changes in the last 7 days. For the sample, I logged in with a second user and made some changes to the opportunity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/CatchUp1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Cool right, but the next sample does not work so great: I changed the estimated revenue twice and the bot only got the last change and failed to combine both changes into one (10000/12000 -&amp;gt; 15000).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/CatchUpAudit.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/CatchUp2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;news&#34;&gt;News&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Get latest news for&amp;rdquo; lets you specify an account and get a list from Bing News for it. So essentially the same as visiting bing.com/news and then entering the account name into the search box. But you don&amp;rsquo;t need to open another browser tab and with &amp;ldquo;/&amp;rdquo; you get an autocomplete which is nice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/News.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;today&#34;&gt;Today&lt;/h2&gt;
&lt;p&gt;The last of the chat function is &amp;ldquo;Prepare me for today&amp;rsquo;s meetings&amp;rdquo;. This shall evaluate notes and emails of the regarding record of appointments in the next 24 hours. So to use this feature, you would need a fully working Exchange sync to have some appointments and synchronized emails. Nevertheless, I tried to mock something up here.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Prepare1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Wow, that did not work so great! Because there was no regarding for the second appointment the bot did not list it at all. For me, that&amp;rsquo;s a problem, because who has all his appointments linked correctly? I might miss a meeting here! It should at least tell me it can&amp;rsquo;t prepare a summary, because there is no linked record.
But ok, let&amp;rsquo;s try again with both records linked.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Prepare2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Ok, that&amp;rsquo;s better, but still, I need to select a record to get the summary. So if I have 5 meetings, I need to go through this 5 times&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;compose&#34;&gt;Compose&lt;/h2&gt;
&lt;p&gt;First I had some trouble with this: You can&amp;rsquo;t use that feature from the Activities Grid, but from Timeline and Up Next it should work! But I&amp;rsquo;ve tried it with the Microsoft Account and that&amp;rsquo;s apparently also illegal, but at least Opportunity and Lead work, which would be in line with the configurations for summary and catch-up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Compose1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The second tab for Compose shows a summary of the email and offers to write a response, let&amp;rsquo;s try!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Compose2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Compose3.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Compose4.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The email tone can be selected as well and finally with &amp;ldquo;Add to email&amp;rdquo; we can insert the text and of course change it to our needs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/sales-copilot/Compose5.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ll be honest with you: I&amp;rsquo;m only mildly impressed by the chat features. Nothing in there seems to be more than &amp;ldquo;hardcoded&amp;rdquo; functionality. Everything from there can be achieved with well-designed forms (the Summary tab has that name for a reason) and Dashboards. This becomes a different story if, for example, the catch-up would allow you to query multiple records at the same time, imagine you have 10 open opportunities and Copilot can tell you which of these have changed over the last few days. For just one record I could simply visit the Audit History myself. But then again, I&amp;rsquo;m a professional, someone who has had little touch points with Dynamics yet might find a connection through these features! A chat is of course easier than to navigate forms if you don&amp;rsquo;t know what you are doing, so assuming it will get more options and maybe &amp;ldquo;multi-record features&amp;rdquo; the future looks brighter here.&lt;/p&gt;
&lt;p&gt;In contrast, I liked the compose feature right away. There is still potential, at least in my testing it did not seem to leverage the context of the surrounding records and prior emails, but if you are like me and waste time on formulating while you know what you want to write, this can save actual working time. My experience with TLDR-bots says that they do work well, so I think we can assume that Copilot will be helpful with longer emails as well.&lt;/p&gt;
&lt;p&gt;I want to use this opportunity to hint you to an AI that works much better: Bing Chat Enterprise. This can answer so many questions and the Sales Copilot would benefit from embedding the existing queries into this bot. With the speed this generative AI topic moves I can&amp;rsquo;t imagine such a powerful Copilot will spare Dynamics for long.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Service Bus 3: Processing Events with a plain Console App</title>
        <link>https://www.marius-wodtke.de/post/servicebus/console/</link>
        <pubDate>Sun, 22 Oct 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/servicebus/console/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/console/cover.png" alt="Featured image of post Service Bus 3: Processing Events with a plain Console App" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/&#34; &gt;the last post&lt;/a&gt;, we already set up a working processor for Dataverse events. But what if AppServices and WebJobs are not preferred or are simply not possible for you? An example would be because you are on OnPremise infrastructure or Azure VMs or the Azure Kubernetes Services are used. There has to be an option to create a processor without such helper technologies, right? Yes, of course there is and here we are going to explore how.&lt;/p&gt;
&lt;h2 id=&#34;general-preparations&#34;&gt;General preparations&lt;/h2&gt;
&lt;p&gt;You can find the full project code &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365ServiceBusProcessors/tree/main/DataverseEventProcessorPlain&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Something else often forgotten in this kind of article is the description of the used dependencies (here NuGets):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt; =&amp;gt; Contains the classes for Connecting to Service Bus&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Configuration&lt;/code&gt;/&lt;code&gt;.Json&lt;/code&gt; =&amp;gt; Used for reading appsettings.json&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Logging.Console&lt;/code&gt; =&amp;gt; Using the Console as a Logging Output, may not be required if you use a different logging mechanism&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.PowerPlatform.Dataverse.Client&lt;/code&gt; =&amp;gt; RemoteExecutionContext class for Deserialization&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Azure.Identity&lt;/code&gt; =&amp;gt; Only if you will use a managed identity if you are OnPremise, you can skip this&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And of course, check out &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/exporting-events/&#34; &gt;the first post&lt;/a&gt; of this series where we set up the service bus and our possibility of sending events to this service bus. So by now every time you create an account, a message should pop up in your subscription. Of course, it might also be helpful to have read &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/webjob/&#34; &gt;the last post&lt;/a&gt; as well for some context (e.g. regarding managed identities) as this is probably the easiest method to set up a processor.&lt;/p&gt;
&lt;h2 id=&#34;the-project&#34;&gt;The project&lt;/h2&gt;
&lt;p&gt;We will start by creating the project. I am choosing .NET Core here as by now the CRM SDK for .NET Core (Microsoft.PowerPlatform.Dataverse.Client) has reached General availability and I expect Microsoft to push us further in the .NET Core Direction in the future.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/console/NewProject.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Next, we install the NuGets to make sure the code will compile, here&amp;rsquo;s the list again for a quick copy: &lt;code&gt;Microsoft.Azure.WebJobs.Core&lt;/code&gt;, &lt;code&gt;Microsoft.Azure.WebJobs.Extensions.ServiceBus&lt;/code&gt;, &lt;code&gt;Microsoft.Extensions.Logging.Console&lt;/code&gt;, &lt;code&gt;Microsoft.PowerPlatform.Dataverse.Client&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And next is code. We only need two classes, program.cs and functions.cs. I chose the same split here as the WebJobs, technically of course this can all be written into the program.cs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; config = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ConfigurationBuilder().AddJsonFile(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appsettings.json&amp;#34;&lt;/span&gt;).Build();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; sbNamespace = config.GetSection(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AzureServiceBus&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fullyQualifiedNamespace&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; client = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ServiceBusClient(sbNamespace,&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; DefaultAzureCredential(), &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ServiceBusClientOptions());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So just like with the WebJob I created a file called appsettings.json, marked it for CopyToOutputDirectory and its content looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AzureServiceBus&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;fullyQualifiedNamespace&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mariuswodtke-dev.servicebus.windows.net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &amp;ldquo;fullyQualifiedNamespace&amp;rdquo; is passed to the &lt;code&gt;ServiceBusClient&lt;/code&gt; alongside the &lt;code&gt;DefaultAzureCredential&lt;/code&gt; for logging in with the managed identity and &lt;code&gt;ServiceBusClientOptions&lt;/code&gt; which you might want to customize depending on your open ports as these do change whether you use TCP or WebSockets here.&lt;/p&gt;
&lt;p&gt;As promised in the preparations, you don&amp;rsquo;t need to use a managed identity, if you use regular SAS keys like we did with Dataverse it will look like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; config = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ConfigurationBuilder().AddJsonFile(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appsettings.json&amp;#34;&lt;/span&gt;).Build();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; sbConnectionString = config.GetSection(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AzureServiceBus&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;connectionString&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; client = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ServiceBusClient(sbConnectionString);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AzureServiceBus&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;connectionString&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Endpoint=sb://mariuswodtke-dev.servicebus.windows.net/;SharedAccessKeyName=Processor;SharedAccessKey=REDACTED;EntityPath=dataverse&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This of course has the problem, that the SAS key is in the appsettings.json, so this plain file would need to be guarded. For example, the risk of the secret being committed to source control is always present.&lt;/p&gt;
&lt;p&gt;To gain such a connection string, you can copy the approach from the policy of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/exporting-events/&#34; &gt;the first post&lt;/a&gt;, just this time with the &lt;em&gt;Listen&lt;/em&gt; claim.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/console/Policy.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; functions = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Functions();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; processor = client.CreateProcessor(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dataverse&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;account-export&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ServiceBusProcessorOptions());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;processor.ProcessMessageAsync += functions.ProcessQueueMessage;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;processor.ProcessErrorAsync += functions.ProcessError;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; processor.StartProcessingAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Console.ReadKey();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we are creating a processor on the subscription. Here the &lt;code&gt;ServiceBusProcessorOptions&lt;/code&gt; might be interesting if you want to limit concurrent processing to 1 (MaxConcurrentCalls) like I suggested last time. Then we add the functions that will be the processors for the message and potential errors and finally call &lt;code&gt;StartProcessingAsync&lt;/code&gt; to start processing. Because this method returns as soon as processing has started, I need to block the program from exiting afterward.&lt;/p&gt;
&lt;p&gt;And finally, we need to take a look at the Functions class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; ILogger log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Functions()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        log = LoggerFactory.Create(_ =&amp;gt; _.AddConsole()).CreateLogger(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Task ProcessQueueMessage(ProcessMessageEventArgs args)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        DataContractJsonSerializer serializer = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; DataContractJsonSerializer(&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(RemoteExecutionContext));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; message = serializer.ReadObject(args.Message.Body.ToStream()) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; RemoteExecutionContext;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        log.LogInformation(message?.MessageName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Task.CompletedTask;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Task ProcessError(ProcessErrorEventArgs args)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        log.LogError(args.Exception.Message);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Task.CompletedTask;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the &lt;code&gt;ProcessError&lt;/code&gt; does nothing much for error handling, it just reports the error here, but you can do more advanced stuff here like immediately dead-lettering a message in case of certain errors.&lt;/p&gt;
&lt;p&gt;But as mentioned in the WebJob post, JSON deserialization is problematic here. But .NET binary is as well! You shall not use the &lt;code&gt;BinaryFormatter&lt;/code&gt; in .NET 6.0 anymore due to security issues. We can argue that we control the incoming messages, but the green squiggles are not beautiful and no static code review will like it, forming a constant source of needing to justify ourselves. So looking back at the problems mentioned last time: &lt;code&gt;Newtonsoft.JSON&lt;/code&gt; has problems with some properties of &lt;code&gt;RemoteExecutionContext&lt;/code&gt; not being settable. &lt;code&gt;System.Text.JSON&lt;/code&gt;&amp;rsquo;s problems start with the DateFormats and continue with the Collections not being writable after initialization. That would need a lot of &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;custom converters&lt;/a&gt; to fix this. The &lt;code&gt;JavaScriptSerializer&lt;/code&gt; would work but is not supported for .NET Core. Luckily &lt;code&gt;DataContractJsonSerializer&lt;/code&gt; does work as shown above (and it would work for a WebJob as well if you let the SDK pass you the message as a string).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;IMPORTANT:&lt;/em&gt;&lt;/strong&gt; The &lt;em&gt;Message Format&lt;/em&gt; on the Service Endpoint in Dataverse needs to be set to &amp;ldquo;JSON&amp;rdquo; for this to work.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And then again there is no logic, just logging a value of the context out to the console to prove that the deserialization worked.&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The result&lt;/h2&gt;
&lt;p&gt;So if we do a debug run now, the output should look a little something like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/console/Result.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Of course I can&amp;rsquo;t make suggestions for deployment here because I don&amp;rsquo;t know where you are deploying to. But essentially you need to make sure the content of the &lt;em&gt;bin&lt;/em&gt; folder is on the server and then make sure the .exe is always running when the server is running. It is not enough to just execute the .exe, because it will stop working as soon as the server restarts. Something like the &lt;em&gt;Windows Task Scheduler&lt;/em&gt; can help to overcome this.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this article, we formed a very generic processor for Dataverse messages in an Azure Service Bus. This processor can run on virtually any hardware and operating system. Just make sure the correct .NET runtime is installed and it will run.
The project is only 60 lines of code in total, but of course, it is missing the most important thing: Your logic! So I won&amp;rsquo;t bother you anymore, let&amp;rsquo;s start coding.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] First steps in the Power Pages Maker</title>
        <link>https://www.marius-wodtke.de/post/power-pages/editor/</link>
        <pubDate>Sun, 15 Oct 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/editor/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/editor/cover.jpg" alt="Featured image of post [VIDEO] First steps in the Power Pages Maker" /&gt;&lt;p&gt;After we &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/setup/&#34; &gt;created a Power Page&lt;/a&gt; to serve external users with a website, this time we will explore some of the editing capabilities. In only a few minutes of video, the site will gain multiple pages with images, text and buttons.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/EiF0P7DpSmc&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/EiF0P7DpSmc&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/editor/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Powerautomate: Multiplexing and App Association</title>
        <link>https://www.marius-wodtke.de/post/other/powerautomate-app-association/</link>
        <pubDate>Sun, 08 Oct 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/powerautomate-app-association/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-app-association/cover.png" alt="Featured image of post Powerautomate: Multiplexing and App Association" /&gt;&lt;p&gt;Are you using PowerAutomate (Flows) with Data? Then get ready for this change by Microsoft!&lt;/p&gt;
&lt;h2 id=&#34;multiplexing&#34;&gt;Multiplexing&lt;/h2&gt;
&lt;p&gt;Multiplexing is something that forbids leveraging the fact that only the owner of the flow needs a &lt;em&gt;Flow Premium&lt;/em&gt; license to offer automation to other users. If premium connectors are used within the flow and it offers direct value to other users, those users will need a &lt;em&gt;Flow Premium&lt;/em&gt; license as well.
There is a hole in this though: If one of the two ends (trigger or action) is directly related to a Dataverse app and the value is provided to the users of this app, the entitlement for this value can be derived from the license they have for Dataverse (e.g. &lt;em&gt;Sales Professional&lt;/em&gt;, &lt;em&gt;Customer Service Enterprise&lt;/em&gt; and so on).
In some situations, you still need to be careful, e.g. think about a Flow that triggers from Dataverse and stores a document in Sharepoint. Now if this is stored for the Dataverse user that should be no problem, but if the user using the document is not a Dataverse user they might need a &lt;em&gt;Flow Premium&lt;/em&gt; license to be entitled to gain the value from that flow.&lt;/p&gt;
&lt;h2 id=&#34;whats-new&#34;&gt;What&amp;rsquo;s new?&lt;/h2&gt;
&lt;p&gt;We will actually have to mark the flows that fall under this exception. That&amp;rsquo;s not too hard, it&amp;rsquo;s only the effort I&amp;rsquo;m just afraid of the effort for existing implementations with dozens or even a hundred flows.
But let&amp;rsquo;s do this, select a Flow, navigate to the &lt;em&gt;Details&lt;/em&gt; and you will find the &lt;em&gt;App Association&lt;/em&gt; section at the bottom right. Then &lt;em&gt;Add Association&lt;/em&gt; and then select one or more of your Dataverse Apps. After saving the Apps will be displayed in this view as well as the &lt;em&gt;Details&lt;/em&gt; of the Flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-app-association/Associate1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Associated Apps are found in the bottom right&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-app-association/Associate2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Select Add Association to add apps to the Flow&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-app-association/Associate3.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;After saving the apps, they will appear in the view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-app-association/Associate4.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The associated apps viewed from the Details pane of the Flow&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it! Now repeat this for all Flows and think about what apps they relate to. If you can&amp;rsquo;t pin a certain app, that might be the red flag that you are currently multiplexing and the users &amp;ldquo;using&amp;rdquo; the Flow might need a license.&lt;/p&gt;
&lt;h2 id=&#34;when-does-it-need-to-be-done&#34;&gt;When does it need to be done?&lt;/h2&gt;
&lt;p&gt;Microsoft announced notifications going out on October 15th and the enforcement (the flow being turned off) will happen after 90 days. So this means you have until after Christmas before this needs to be completed. This should be a sufficient time frame to perform the association, but still needs to be planned because it will be forgotten otherwise. The official documentation is &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-platform/admin/power-automate-licensing/faqs#what-are-the-different-enforcements&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;found here&lt;/a&gt;, check it for updates regarding the timeline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/powerautomate-app-association/Timeline.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This was something completely different for me, I hope you did not get bored with all that legal talk. While we talk about that: Be aware that nothing here is legal advice and the licensing guides do change regularly which may render the statements made here useless.
For the actual matter: The change here is not too big and it should be possible to find a few hours to complete the app association within 90 days. Yet you should be aware and to be honest I think the idea of this move is for you to reevaluate if you are multiplexing and should buy additional licenses.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Service Bus 2: Processing Events with a WebJob</title>
        <link>https://www.marius-wodtke.de/post/servicebus/webjob/</link>
        <pubDate>Sun, 01 Oct 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/servicebus/webjob/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/cover.png" alt="Featured image of post Service Bus 2: Processing Events with a WebJob" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/exporting-events/&#34; &gt;the first post&lt;/a&gt; of this series, we started with sending Dataverse Events to a Service Bus. Of course that in itself does not have any value, we need to do something with these Events!
So in this article, we will create a processor for these events, specifically a WebJob on Azure.&lt;/p&gt;
&lt;h2 id=&#34;general-preparations&#34;&gt;General preparations&lt;/h2&gt;
&lt;p&gt;You can find the full project code &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365ServiceBusProcessors/tree/main/DataverseEventProcessor&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Something else often forgotten in this kind of article is the description of the used dependencies (here NuGets):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Azure.WebJobs.Core&lt;/code&gt; =&amp;gt; General Webjobs stuff in Program.cs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Azure.WebJobs.Extensions.ServiceBus&lt;/code&gt; =&amp;gt; Bindings to Service Bus&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Logging.Console&lt;/code&gt; =&amp;gt; Using the Console as a Logging Output, may not be required if you use a different logging mechanism&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.PowerPlatform.Dataverse.Client&lt;/code&gt; =&amp;gt; RemoteExecutionContext class for Deserialization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And of course, check out &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/exporting-events/&#34; &gt;the first post&lt;/a&gt; of this series where we set up the service bus and our possibility of sending events to this service bus. So by now every time you create an account, a message should pop up in your subscription.&lt;/p&gt;
&lt;h2 id=&#34;the-project&#34;&gt;The project&lt;/h2&gt;
&lt;p&gt;We will start by creating the project. I am choosing .NET Core here as by now the CRM SDK for .NET Core (Microsoft.PowerPlatform.Dataverse.Client) has reached General availability and I expect Microsoft to push us further in the .NET Core Direction in the future.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/NewProject.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Next, we install the NuGets to make sure the code will compile, here&amp;rsquo;s the list again for a quick copy: &lt;code&gt;Microsoft.Azure.WebJobs.Core&lt;/code&gt;, &lt;code&gt;Microsoft.Azure.WebJobs.Extensions.ServiceBus&lt;/code&gt;, &lt;code&gt;Microsoft.Extensions.Logging.Console&lt;/code&gt;, &lt;code&gt;Microsoft.PowerPlatform.Dataverse.Client&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And next is code. We only need two classes, program.cs and functions.cs, I&amp;rsquo;ll start with program.cs&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; hostBuilder = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HostBuilder();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hostBuilder.ConfigureWebJobs((context, builder) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    builder.AddServiceBus(options =&amp;gt; options.MaxConcurrentCalls = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These first lines contain an important concept used in WebJobs: The HostBuilder. It configures the WebJob before it is run, this allows the WebJobs to be &amp;ldquo;triggered&amp;rdquo; and not just be a continuously running application. Well actually they are of course but things like waiting for a new message are not an explicit code operation but a definition within the Functions.
We are adding a Service Bus here, we will discuss later how the WebJob knows which Service Bus we want to connect to. The option for MaxConcurrentCalls = 1 makes the job process one message after another. This is great for debugging and usually makes sure the receiving database does not get overwhelmed. But it can also lead to a bottleneck within your processor. An idea for this I have seen in projects is to make this a variable in the appsettings that is set to 1. The value is then either scaled in deployment or directly in the running AppService on Azure by an Administrator. If you connect to Dataverse make sure to not use a value bigger than 10 because a) it won&amp;rsquo;t scale well and b) you might get issues with the ServiceClient since you cannot open to many connections to Dataverse.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hostBuilder.ConfigureLogging((context, b) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    b.AddConsole();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next is logging, I&amp;rsquo;m just using the Console here, but I still wanted to include this to the post, to make you aware that there are more options here, like Application Insights if you want to stay within Azure.
Once everything is configured we build and run the HostBuilder. This &lt;code&gt;RunAsync()&lt;/code&gt; will block the console indefinitely and usually it throws here if you misconfigured something.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; host = hostBuilder.Build();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (host)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; host.RunAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ok, over to the Functions.cs. As the name suggests, this is the entry point for our functionality.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; ProcessQueueMessage([ServiceBusTrigger(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dataverse&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;account-export&amp;#34;&lt;/span&gt;, Connection = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AzureServiceBus&amp;#34;&lt;/span&gt;)] RemoteExecutionContext message, ILogger log)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log.LogInformation(message.MessageName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Technically, these are only 2 lines of code, but a lot is going on. I&amp;rsquo;ll start with the &lt;code&gt;ServiceBusTriggerAttribute&lt;/code&gt;, the first parameter (&amp;ldquo;dataverse&amp;rdquo;) here is the topic and the second one (&amp;ldquo;account-export&amp;rdquo;) is the subscription. The Connection parameter (&amp;ldquo;AzureServiceBus&amp;rdquo;) is just a reference to the settings, so a quick dive into the appsettings.json file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AzureServiceBus&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;fullyQualifiedNamespace&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mariuswodtke-dev.servicebus.windows.net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the parameter refers to this setting. The setting can directly contain a connection string (&amp;ldquo;AzureServiceBus&amp;rdquo;: &amp;ldquo;Endpoint=sb://mariuswodtke-dev.servicebus.windows.net/;SharedAccessKeyName=Dataverse;SharedAccessKey=REDACTED;EntityPath=dataverse&amp;rdquo;) or an object as shown that spares the secret. Again I will refer you to a little later how that connection will actually work if we only pass the namespace URL of the Service Bus.&lt;/p&gt;
&lt;p&gt;Back to the Functions.cs code, the &lt;code&gt;ServiceBusTriggerAttribute&lt;/code&gt; decorates the real parameter of the function &lt;code&gt;RemoteExecutionContext message&lt;/code&gt;. &lt;code&gt;RemoteExecutionContext&lt;/code&gt; is part of the &lt;code&gt;Microsoft.Xrm.Sdk&lt;/code&gt; namespace and is the object we passed to the Service Bus, the WebJobs SDK will automatically deserialize it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;IMPORTANT:&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;If you followed the first post exactly, you will receive an error here&lt;/strong&gt;. This is because Newtonsoft.JSON refuses to deserialize &lt;code&gt;RemoteExecutionContext&lt;/code&gt;. There is a simple solution to this: Change the &lt;em&gt;Message Format&lt;/em&gt; to &amp;ldquo;.NETBinary&amp;rdquo;, this format will be deserialized. If it needs to be JSON for some reason you can skip the automatic deserialization by specifying message as string (&lt;code&gt;string message&lt;/code&gt;) and then deserializing yourself. The JavaScriptSerializer does work, but is exclusive to .NET Framework. (Check &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/console/&#34; &gt;the post about plain console processing&lt;/a&gt; for a working JSON solution)&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;setting-up-the-app-service&#34;&gt;Setting up the App Service&lt;/h2&gt;
&lt;p&gt;I will do this via the Azure Portal, after all, if you do something in Azure the first time around, it&amp;rsquo;s the easiest way to achieve a result quickly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/CreateWebApp.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So I used the &lt;em&gt;+ Create&lt;/em&gt; in my Resource Group, searched for &amp;ldquo;Web App&amp;rdquo; and filled the first tab. Some notable settings are the &lt;em&gt;Runtime Stack&lt;/em&gt; which I matched to the project runtime. For the App Service Plan, I created a plan in the Free tier, not something to use for production but a great option for a first test. Web Apps can be moved between App Service Plans if you later create a paid plan.&lt;/p&gt;
&lt;p&gt;Now in the newly created App Service (Web App) navigate to &lt;em&gt;Identity&lt;/em&gt; and turn the System Assigned Managed Identity on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/ManagedIdentity.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;handling-authentication&#34;&gt;Handling Authentication&lt;/h2&gt;
&lt;p&gt;And now we lift the secret of how the authentication works! So the WebJob SDK will automatically use the Managed Identity we just assigned, so the only thing we need to make sure is that this Managed Identity is authorized to access the Service Bus. This is done in the IAM Section of the Service Bus.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/IAM.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Role.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Member.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;IMPORTANT:&lt;/em&gt;&lt;/strong&gt; This needs to be done on the whole Service Bus (Namespace). The IAM Section is also available on the topic, but the WebJobs SDK wont be happy with that. This is one of the reasons I suggested a seperate Service Bus per stage. If that is not possible, you can fall back to the &lt;em&gt;Shared Access Policies&lt;/em&gt; of the topic, but you will need to store them securely, for example in an Azure Key Vault.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And we can do the same thing locally as well! Just authorize your own account as &lt;em&gt;Azure Service Bus Data Receiver&lt;/em&gt; and then in Visual Studio under &lt;em&gt;Tools&lt;/em&gt; =&amp;gt; &lt;em&gt;Options&lt;/em&gt;, find the &lt;em&gt;Azure Service Authentication&lt;/em&gt; and authenticate your account. Now Visual Studio can provide this Identity to the WebJobs SDK and you are ready for a debugging run of your&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; I&amp;rsquo;ve used a specific account for my Visual Studio here because my real account is not in the same Tenant, which often leads to problems and requires the components to manually set the tenant id, which usually means injecting the Identity manually. In this situation its easier to just create a new account, assign it a password and then enable that for the Azure Resources you want to access from your Code.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The result&lt;/h2&gt;
&lt;p&gt;Now it&amp;rsquo;s time for a debug run, but well, there is not much logic in my function. It just prints the &lt;code&gt;MessageName&lt;/code&gt; to prove that the deserialization works. Note the interfaces implemented by &lt;code&gt;RemoteExecutionContext&lt;/code&gt;, it does implement the &lt;code&gt;IPluginExecutionContext&lt;/code&gt; that you will already know from regular plugins. So everything is as expected, the &lt;code&gt;InputParameters&lt;/code&gt; will contain the &amp;ldquo;Target&amp;rdquo;, if you have configured a PreImage it will be in the &lt;code&gt;PreEntityImages&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Result.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;deploying&#34;&gt;Deploying&lt;/h2&gt;
&lt;p&gt;After the successful debug let&amp;rsquo;s deploy. While later you should look into Pipelines with GitHub or Azure DevOps, a first deployment (or publish) can happen directly from Visual Studio.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Publish1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The publish option appears after a right-click on the project&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Publish2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;We want to publish to Azure…&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Publish3.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;…as a WebJob&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Publish4.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Then we select the App Service (Web App) we created earlier&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Publish5.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;In the end, we are presented with the Publish Profile and can select Publish on the top right&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Publish6.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;And that publish should end up with a success&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After the deployment, you can check the logs at the App Service. Navigate to WebJobs there and select the Logs Icon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Log1.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/webjob/Log2.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So I guess it&amp;rsquo;s your turn now to implement the real logic. In this post, we created a .NET Core Console Application, configured and deployed it as WebJob, worked on a passwordless authentication and finally deployed it to Azure to process the messages. So the only thing left to do is to implement what shall actually be done with the message. Shall it be written to a third database/application? Do you want to enrich the Dataverse record? Maybe some advanced processing? I guess your stakeholders have something ready for you!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>[VIDEO] Setting up your first Power Page</title>
        <link>https://www.marius-wodtke.de/post/power-pages/setup/</link>
        <pubDate>Sun, 24 Sep 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/power-pages/setup/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/setup/cover.jpg" alt="Featured image of post [VIDEO] Setting up your first Power Page" /&gt;&lt;p&gt;Do you want to provide external users with a website fed with data from Dataverse? Instead of building APIs or synchronization mechanisms with the website&amp;rsquo;s database, you can also use Power Pages! This first video in the Power Pages series walks through a setup, even if you don&amp;rsquo;t have a Dataverse Environment yet.&lt;/p&gt;
&lt;p&gt;Check it out &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/kz3HtxDJIdA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://youtu.be/kz3HtxDJIdA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/power-pages/setup/video.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Service Bus 1: Exporting Dataverse Events</title>
        <link>https://www.marius-wodtke.de/post/servicebus/exporting-events/</link>
        <pubDate>Sun, 17 Sep 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/servicebus/exporting-events/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/cover.png" alt="Featured image of post Service Bus 1: Exporting Dataverse Events" /&gt;&lt;p&gt;Are you aware of the Service Endpoints of Dataverse? These are integration options to process Dataverse events like Create and Update outside of Dataverse/PowerPlatform. This &amp;ldquo;Outside of Dataverse&amp;rdquo; can be things like an API hosted anywhere, but it can also be an &lt;em&gt;Azure Service Bus&lt;/em&gt; and that is what I want to discuss here.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-service-bus&#34;&gt;What is a Service Bus&lt;/h2&gt;
&lt;p&gt;I will skip deeper explanations here, just the most important concepts: Service Bus is a message queueing service. So a &lt;em&gt;queue&lt;/em&gt; in Service Bus is really what the name says, a queue. The sender pushes messages in and the listener extracts them from oldest to newest.
The second concept is a topic. The sender still pushes messages to that topic, but from here the message can go into 0 to n subscriptions from where the listener(s) read from, still, oldest to newest. Think of it like a front-desk queue: You arrive, voice your request and get told to which queue you should enqueue to get the right employee for your request. But unlike the real world, the front-desk assistant can also clone you and enqueue you at 2 or more &amp;ldquo;queues&amp;rdquo; (aka subscriptions) at the same time.
The third important concept is buffering. A message is not gone from the queue as soon as it is read, instead it needs to be completed. That means if there is an error in processing the message, it won&amp;rsquo;t be completed and therefore will be read again after the timeout period for processing is over. You can configure what will happen to messages that can&amp;rsquo;t be processed within x attempts. Those messages can (and should) be moved to a dead-letter queue, where you can revisit them, for example after fixing a bug in your code.&lt;/p&gt;
&lt;h2 id=&#34;why-not-simply-use-plugins-instead&#34;&gt;Why not simply use Plugins instead&lt;/h2&gt;
&lt;p&gt;Usually, you would use a Plugin to process the Dataverse events but some key indicators might lead you towards an Azure Service Bus and a &lt;em&gt;Listener&lt;/em&gt; instead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are integrating a system that has outages. The Service Bus will buffer the messages during the outages.&lt;/li&gt;
&lt;li&gt;You are integrating a table that has frequent operations or a considerable amount of data. The Service Bus will buffer the messages and the listener will push them over at the rate the other system can receive them.&lt;/li&gt;
&lt;li&gt;You need libraries and don&amp;rsquo;t want to use the unsupported ILMerge. A listener deployed to Azure (or any OnPremise Server) can use as many NuGets and Libraries as it likes.&lt;/li&gt;
&lt;li&gt;You are integrating a system that is behind firewalls that do not allow inbound connections. The listener can be deployed inside the network, connecting only outwards to pull messages from the Service Bus instead of having them pushed into the network.&lt;/li&gt;
&lt;li&gt;You need to be able to deploy fixes during business hours. Service Bus will buffer the messages, while a Dataverse deployment would yield a disturbance for the users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So with these arguments, a lot of integration topics gain from using a Service Bus. Then there are almost always benefits if you can not or do not want to use Power Automate with its own retry mechanisms.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-service-bus&#34;&gt;Creating a Service Bus&lt;/h2&gt;
&lt;p&gt;We will create the Service Bus in the Azure Portal. Use the search box at the top to search for the Service &amp;ldquo;Service Bus&amp;rdquo;, select it and then &amp;ldquo;+ Create&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/NewServiceBus.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;I suggest using the Standard SKU. Premium is quite expensive and will only be viable for really big message volumes, volumes so big that we would question Dataverse as the Datastore for that amount of transactions anyway. On the other hand, Basic is quite cheap but lacks the Topics feature which is super handy.
Also, I do suggest creating different Service Bus Namespaces for different environments/stages of Dataverse. Based on the properties in the deployment, Microsoft might disagree on this, but splitting production and dev/test resources is always a good idea.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/NewTopic.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Next is the topic, which I&amp;rsquo;ve named &amp;ldquo;Dataverse&amp;rdquo; here: In another post about deployment we will explore the fact that having more topics or queues as endpoints increases the effort of deploying them. Therefore it makes sense to at least group the matters to not have more than a handful of topics for Dataverse in the Bus.
Just to have a sample: In a project, we did not stick to a single topic and introduced a second one when an integration needed a lot of entities and triggered on update of many fields of these entities. This would have pushed messages to existing subscriptions for fields the connected logic should not trigger. That could have been filtered out in code, but balancing the effort with a new endpoint was the easier option.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/Subscriptions.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/NewSubscription.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The Subscription is named &amp;ldquo;account-export&amp;rdquo; here, mimicking an integration. Since we will be pushing more events in the future, this subscription needs a filter as well. Since they will not show well, the properties to filter on are &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;http://schemas.microsoft.com/xrm/2011/Claims/EntityLogicalName%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http://schemas.microsoft.com/xrm/2011/Claims/EntityLogicalName&#34;&lt;/a&gt; and &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;http://schemas.microsoft.com/xrm/2011/Claims/RequestName%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http://schemas.microsoft.com/xrm/2011/Claims/RequestName&#34;&lt;/a&gt;. And maybe if you are not using separate Bus/Topic as suggested: &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;http://schemas.microsoft.com/xrm/2011/Claims/Organization%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http://schemas.microsoft.com/xrm/2011/Claims/Organization&#34;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For simple scenarios, you can just filter on a single entity like &amp;ldquo;account&amp;rdquo;. For this just set a correlation filter like in the screenshot. Sometimes you will also want to filter on the Message &amp;ldquo;Create&amp;rdquo; or &amp;ldquo;Update&amp;rdquo; if there is maybe also a Delete event being pushed to the Bus! This can be handled with the SQL filters:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.microsoft.com/xrm/2011/Claims/EntityLogicalName&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;account&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;AND&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.microsoft.com/xrm/2011/Claims/RequestName&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Create&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;OR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.microsoft.com/xrm/2011/Claims/RequestName&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Update&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/NewFilter.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And lastly, we need a Policy on the topic to allow Dataverse to push messages. We will only need the send permission for now, listening will be part of the next post.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/NewPolicy.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;configuring-the-service-endpoint&#34;&gt;Configuring the Service Endpoint&lt;/h2&gt;
&lt;p&gt;Now we need the Plugin Registration Tool. This can be included as NuGet to an existing Project or you download it from &lt;a class=&#34;link&#34; href=&#34;https://www.nuget.org/packages/Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt; (&amp;ldquo;Download Package&amp;rdquo;), change the extension from .nuget to .zip and unpack it. The path for me looks something like &amp;ldquo;\.nuget\packages\microsoft.crmsdk.xrmtooling.pluginregistrationtool\9.1.0.184\tools\PluginRegistration.exe&amp;rdquo;. Of course things like the version might differ for you.
Connect to your environment and then Register a new Service Endpoint with the Policy we just created. Please note that the wizard did not recognize it being a Topic and that I changed the format from &lt;em&gt;.NET Binary&lt;/em&gt; to &lt;em&gt;JSON&lt;/em&gt; for the sake of this tutorial. But I assume you already have a favorite anyway, one tip if you can use WebJobs: &lt;em&gt;.NET Binary&lt;/em&gt; works well here!
But I don&amp;rsquo;t want to overcomplicate that here: When doing your first development, stick to your preferred format for now, you can still change it until you have the first listener done.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/CreateServiceEndpoint.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/ConnectionString.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/Dialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And from now everything runs as if we were registering a normal plugin. Just be sure to make it asynchronous.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/CreateStep.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;checking-the-results&#34;&gt;Checking the results&lt;/h2&gt;
&lt;p&gt;Ok, everything is set up now, let&amp;rsquo;s create an Account and then we should find it in the Service Bus.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/NewAccount.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/TopicOverview.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/MessageBody.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After creating the Account, the Topic lists a Message in the subscription. Using the Integrated Service Bus Explorer at the Subscription lets us peek at the message and indeed it is our Account.
Here is also a picture of the Properties, this is important because the Filters we created earlier do not work on the body of the message, only on the properties! I cannot filter on &lt;em&gt;body.target.name&lt;/em&gt; being present.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/servicebus/exporting-events/MessageProperties.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This was obviously only part one of the journey. We have now exported messages in a fixed format set by Dataverse, but someone needs to interpret and process these messages. That is something we will discuss in future articles and we will also cover how this can be deployed between environments. But for now, let&amp;rsquo;s celebrate our achievement for today: We exported Dataverse events to Azure without writing a single line of code!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Advanced LINQ Queries for Dataverse (1)</title>
        <link>https://www.marius-wodtke.de/post/other/advancedqueries1/</link>
        <pubDate>Sun, 10 Sep 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/advancedqueries1/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/advancedqueries1/cover.png" alt="Featured image of post Advanced LINQ Queries for Dataverse (1)" /&gt;&lt;p&gt;When it comes to performance optimization I have a single piece of advice for you: Reduce the amount of queries, period. Forget about parallelizing, List vs. Array, a StringBuilder or loop unrolling, if you have a loop with a Retrieve inside, it&amp;rsquo;s almost always magnitudes more efficient to look at queries than anything else. Just because of the latency. If you have a loop of 10.000 items with a retrieve inside which takes 50ms, that&amp;rsquo;s 500 seconds. There are of course other methods to discuss like ExecuteMultiple and BypassCustomPluginExecution, but queries are the easiest and most efficient optimization to try.&lt;/p&gt;
&lt;h2 id=&#34;select-many-relationships&#34;&gt;Select Many Relationships&lt;/h2&gt;
&lt;p&gt;Using the Navigation Properties of Earlybounds you can load a related entity. The following query starts at the account and loads all related contacts.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; contacts = ctx.AccountSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Where(_ =&amp;gt; _.Id == accountId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .SelectMany(_ =&amp;gt; _.contact_customer_accounts)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = _.FirstName, LastName = _.LastName })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .ToList();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That alone does not yield an immediate optimization, because I could have expressed that as well with a &amp;ldquo;simple&amp;rdquo; query.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; contacts2 = ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Where(_ =&amp;gt; _.ParentCustomerId.Id == accountId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = _.FirstName, LastName = _.LastName })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .ToList();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;select-relationship&#34;&gt;Select Relationship&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; contact = ctx.AccountSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Where(_ =&amp;gt; _.Id == accountId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; _.account_primary_contact)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = _.FirstName, LastName = _.LastName })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .FirstOrDefault();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This sample is a little different and this time it&amp;rsquo;s an actual saving. We are selecting a single value relationship here, the primary contact of an account. If we do the same with simple queries, we will need two queries. Doing it with a single query saves the latency once, but will put a little more load on the database to perform the join, which is usually far less than a latency.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; account = ctx.AccountSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Where(_ =&amp;gt; _.Id == accountId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Account { PrimaryContactId = _.PrimaryContactId })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .First();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; contact2 = ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Where(_ =&amp;gt; _.Id == account.PrimaryContactId.Id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = _.FirstName, LastName = _.LastName })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .FirstOrDefault();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sadly this method still has some shortcomings, what if I want both values from the contact and account? The following code DOES NOT WORK, it will only fill the FirstName property.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; contacts = ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Where(_ =&amp;gt; _.Id == contactId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .Select(_ =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = _.FirstName, contact_customer_accounts = _.contact_customer_accounts })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .ToList();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;joining&#34;&gt;Joining&lt;/h2&gt;
&lt;p&gt;If we need both entities, we will have to switch to query syntax. I like to use the method syntax a lot because in my opinion, it&amp;rsquo;s quite readable for simple queries, but for these larger ones, arguably, query syntax is the better choice.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; model = (&lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; contact &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; account &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.AccountSet &lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; contact.ParentCustomerId.Id &lt;span style=&#34;color:#66d9ef&#34;&gt;equals&lt;/span&gt; account.AccountId
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; contact.ContactId == contactId
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Contact = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = contact.FirstName },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Account = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Account { Name = account.Name }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }).FirstOrDefault();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That sample outputs both the Account and Contact here. There are some things to observe here. First, I&amp;rsquo;ve used a &lt;em&gt;dynamic&lt;/em&gt; here for the select (&lt;code&gt;new {...}&lt;/code&gt;). This only works within the calling method, if you need to return both values to another method, consider making a small class or struct. Or you could return a &lt;code&gt;Tuple&amp;lt;Contact,Account&amp;gt;&lt;/code&gt;, but access with &lt;code&gt;tuple.Item1&lt;/code&gt; and &lt;code&gt;tuple.Item2&lt;/code&gt; is arguably not very readable.&lt;/p&gt;
&lt;p&gt;Also, notice how I&amp;rsquo;ve used &lt;code&gt;contact.ContactId&lt;/code&gt; in the where clause and &lt;code&gt;account.AccountId&lt;/code&gt; in the join. Using &lt;code&gt;contact.Id&lt;/code&gt; is legal in the simple query below, but if you are working with a Join, you will have to use the long id names instead.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cont = (&lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; contact &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; contact.Id == contactId
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = contact.FirstName }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ).FirstOrDefault();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;filtering-on-related&#34;&gt;Filtering on related&lt;/h2&gt;
&lt;p&gt;Another cool feature here is the ability to filter on the related entity. So in the sample below, we are only caring about the Contact, but we don&amp;rsquo;t have the ID of the Account yet. A simple query could mean that we first get the Account with that exact Name and then query by ParentCustomerId, but with a join, we can filter directly by &lt;code&gt;account.Name&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cont = (&lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; contact &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; account &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.AccountSet &lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; contact.ParentCustomerId.Id &lt;span style=&#34;color:#66d9ef&#34;&gt;equals&lt;/span&gt; account.AccountId
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; account.Name == &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Alpine Ski House (sample)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = contact.FirstName }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ).ToList();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;putting-it-all-together&#34;&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;The last query is everything in one: We will do two joins by also looking at the PrimaryContactId of the Account. We will also query by a field on the account and we will return all 3 records, the contact, its account and the primary contact of the account.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; model = (&lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; contact &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.ContactSet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; account &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.AccountSet &lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; contact.ParentCustomerId.Id &lt;span style=&#34;color:#66d9ef&#34;&gt;equals&lt;/span&gt; account.AccountId
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; primaryContact &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; ctx.ContactSet &lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; account.PrimaryContactId.Id &lt;span style=&#34;color:#66d9ef&#34;&gt;equals&lt;/span&gt; primaryContact.ContactId
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; account.Name == &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Alpine Ski House (sample)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Contact = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = contact.FirstName },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    PrimaryContact = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Contact { FirstName = primaryContact.FirstName },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Account = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Account { Name = account.Name }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }).ToList();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it gets a little special: Let&amp;rsquo;s assume we have 2 contacts that have that account as ParentCustomer. That means that the query will return 2 results. And that also means 2 Contacts, 2 Accounts and 2 PrimaryContacts. So you might need to post-process that result before handing it back to logic, e.g. by grouping by Account.Id. Or the logic simply does not care. Need a sample? A tool exporting a .csv file with a row for each contact with some values from Account and PrimaryContact simply would iterate through the results and write down the values.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Relationships and Joining are a great way to reduce queries and thus latency. Before thinking about any other optimization for slow Tools, APIs or Plugins, check your queries first and think about their dynamics. If query1 returns 100k results and for each result, another query2 has to be fired that is a BIG deal. We will soon look into optimizations for writing data to Dataverse as well to optimize you further!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Excel: Exporting deep joins and lost of rows</title>
        <link>https://www.marius-wodtke.de/post/other/excel-sql/</link>
        <pubDate>Sun, 03 Sep 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/excel-sql/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/cover.png" alt="Featured image of post Excel: Exporting deep joins and lost of rows" /&gt;&lt;p&gt;Hopefully, this article just comes out as a &amp;ldquo;short tip&amp;rdquo; regarding Excel and Dynamics. I do assume you know the standard tools, you can select the &lt;em&gt;Export to Excel&lt;/em&gt; option on a grid to open the records in Excel. There are multiple options with &lt;em&gt;dynamic&lt;/em&gt; and &lt;em&gt;static&lt;/em&gt; worksheets as well as an online option. The other thing is the &lt;em&gt;Excel Templates&lt;/em&gt;, they can predefine some evaluations and visualizations of the data.&lt;/p&gt;
&lt;h2 id=&#34;the-limitations&#34;&gt;The limitations&lt;/h2&gt;
&lt;p&gt;There are two limitations I want to discuss here: The options won&amp;rsquo;t export more than 100.000 rows and you can only show columns one lookup deep. The first limitation is pretty clear, but let me give you a sample for the second one.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s assume you have a contact view. You want to display the annual revenue of the account (parentcustomerid), that&amp;rsquo;s no problem for Dynamics. But if I now want to display the email address of the account&amp;rsquo;s primary contact (primarycontactid) as well, that&amp;rsquo;s not possible with the OOTB options.&lt;/p&gt;
&lt;p&gt;And since we can&amp;rsquo;t define the view, we can also not export it to Excel.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; You can define such a view with the &lt;em&gt;FetchXml Builder&lt;/em&gt; in the &lt;em&gt;XrmToolbox&lt;/em&gt;, but it will not behave correct. E.g. it wont display a name for the &amp;ldquo;illegal&amp;rdquo; column and in my testing the view also broke the Excel features.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;a-solution&#34;&gt;A solution&lt;/h2&gt;
&lt;p&gt;We can overcome both problems with the PowerQuery feature of Excel. PowerQuery is the language that powers PowerBI (&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/&#34; &gt;read more here&lt;/a&gt;) and does not use FetchXML to get data. Instead, it uses the read-only SQL Endpoint and that means it does not share many of the limitations of FetchXML based integrations.&lt;/p&gt;
&lt;p&gt;So I wrote the query in SQL, entered it in the &lt;em&gt;Get Data&lt;/em&gt; dialog, authenticated to Dynamics and then loaded the data. See below, pictures tell more than a thousand words here.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/ssms.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Defining the query with SSMS&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/getdata.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Get Data feature&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/dialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;While you can explore all tables here, I’ve inserted the prepared query&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/login.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/data.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;No need to transform data now, because we did that when preparing the SQL query, just Load Data&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/excel-sql/excel.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The finished Excel view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; The concrete requirement here involved 9 joins (one to the parent entity and then 8 different lookups from there) and a few hundred thousand rows, which took the SQL Server 1-2 minutes to refresh the worksheet.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;the-catch&#34;&gt;The catch&lt;/h2&gt;
&lt;p&gt;Well, the catch is that you can&amp;rsquo;t upload it to Dynamics! This file gets all the data and then the user has to define the filters in Excel. That can be a good or a bad thing based on your users. Also, while you can use it for importing changed data to the base entity with a data map, there is nothing like a &lt;em&gt;dynamic worksheet&lt;/em&gt; here, as mentioned, the SQL connection is read-only.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;To summarize, with the SQL endpoint of Dynamics you can export more than 100.000 rows and as many joined records as you like to Excel. But in return, you are losing a lot of the integration that lies within the Excel features because you are now accessing the SQL endpoint raw. While it can be the only option to generate such an Excel for the user, the acceptance of them having to open a file, refresh the data and then filter it to their needs might vary a lot based on your organization. Do your users love Excel anyway? Then this will be second nature to them. Are they used to Dataverse and the Advanced Find filters? They might simply hate it&amp;hellip;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>PowerBI: Embedding a Report to a form</title>
        <link>https://www.marius-wodtke.de/post/powerbi/embedded-report/</link>
        <pubDate>Sun, 27 Aug 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/powerbi/embedded-report/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/cover.png" alt="Featured image of post PowerBI: Embedding a Report to a form" /&gt;&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/first-dashboard/&#34; &gt;the last post&lt;/a&gt; of the PowerBI series, we added a Dashboard to Dynamics. I recently came across a shortcoming of this approach. It lacks the context menus. So for example the &lt;em&gt;Export data&lt;/em&gt; button is missing. Also. you will have to provide the filters within the visuals. So for example via the cross-filtering feature of PowerBI we were able to filter by country by clicking on it in the pie chart, but for example by city would have not been possible!&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;I suggest following &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/first-dashboard/&#34; &gt;the post &lt;em&gt;PowerBI: Publishing a first Dashboard to Dynamics&lt;/em&gt;&lt;/a&gt; because that already creates a Report from which the Dashboard is created. If you bring your own Report, make sure the PowerBI Embedding Feature is enabled on your environment under Settings -&amp;gt; Features -&amp;gt; Embedded Content.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/FeatureSwitch.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;add-report-to-dynamics&#34;&gt;Add Report to Dynamics&lt;/h2&gt;
&lt;p&gt;If you already tried to add the Report to the form, but there were none available, that&amp;rsquo;s because you need to add them to the solution first!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/AddReport.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/ReportWizard.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;embedding-the-report&#34;&gt;Embedding the Report&lt;/h2&gt;
&lt;p&gt;Now the Report is available in the Form Editor. Select it from the Components -&amp;gt; PowerBI Section. And that&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/EmbedReport.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/EmbedOption.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The remaining options.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-workspace&#34;&gt;The Workspace&lt;/h2&gt;
&lt;p&gt;Then the experience wasn&amp;rsquo;t so straight for me: The embedded report displayed weird errors about related data, but my Report in PowerBI did not! Turns out, Dataverse created its workspace and copied over the report and dataset. And that dataset needed a refreshing in my case to display correct data. So make the report work again in this new workspace and it should then also work in Dynamics. I can imagine other problems here like a broken data source connection that needs to be reauthenticated.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/Workspace.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The included contents from the Test workspace were copied to a new workspace&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The Result&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/embedded-report/Report.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Since the Dashboard from last time was pretty similar to the report, this embedding looks quite similar to the result of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/powerbi/first-dashboard/&#34; &gt;the previous post&lt;/a&gt;. But there are subtle differences. Notably for my use case, the &lt;em&gt;Export data&lt;/em&gt; button is present. But as we saw in the options in the Form Editor, we can also present the_Filter Pane_ to the user, opening more flexibility in using the report.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I still see so much to discuss here: We need to talk about the &lt;em&gt;JSON filter string&lt;/em&gt; option. Likewise, we need to get some external data in to show how this can display data that we do not want to import to dynamics and that also does not comply with the requirements for displaying it as an &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/externaltable/&#34; &gt;external table&lt;/a&gt;. And finally, this post dug up the question &amp;ldquo;Is there anything I need to look out for when deploying this?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;But on the plus side: If you have a ready-made report and your user should see it in Dynamics, we now have 2 great options available. The embedded report seems to be the more powerful choice, but it may be more natural for the users to have a Dashboard if you are not working with a prefiltering.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Without a NuGet: Certificate Authentication with AAD and C#</title>
        <link>https://www.marius-wodtke.de/post/other/certificate-authentication/</link>
        <pubDate>Sun, 20 Aug 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/other/certificate-authentication/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/other/certificate-authentication/cover.png" alt="Featured image of post Without a NuGet: Certificate Authentication with AAD and C#" /&gt;&lt;p&gt;Now this is something different and very specific. So I am writing this series on &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/&#34; &gt;Sharepoint&lt;/a&gt; and how to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/&#34; &gt;integrate it from the Dynamics Backend&lt;/a&gt;. And Sharepoint Online has a strange restriction for the current state-of-the-art &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Azure AD App-Only&lt;/a&gt; authentication. It only works if you use a certificate and not with a secret.&lt;/p&gt;
&lt;h2 id=&#34;authenticating-with-a-secret&#34;&gt;Authenticating with a Secret&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s first start with this rather easy method. There are lots of resources on the internet covering this, but it&amp;rsquo;s still important to cover it as a base because a lot of concepts will be similar. Of course, we will be doing it without a NuGet.&lt;/p&gt;
&lt;p&gt;Essentially we just need to send a POST request to https://login.microsoftonline.com/{yourtenantid}/oauth2/v2.0/token with a form content. The form content has the following 4 parameters:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grant_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_credentials&amp;#34;&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;: {yourappid}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_secret&amp;#34;&lt;/span&gt;: {yourappsecret}  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;: {whereyouareloggininto}/.&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;TenantId, AppId and AppSecret can be retrieved from the AppRegistration you created.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; Lost here? Check the section &amp;ldquo;The Portal Way&amp;rdquo; of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/appregistration/#the-portal-way&#34; &gt;this article&lt;/a&gt; for some pictures and descriptions on how to create an app registration and where the &lt;em&gt;Secrets&lt;/em&gt; section is.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The scope might be a little more difficult. Usually, it&amp;rsquo;s the URL of the app you are trying to log in. So for Dynamics this is something like XXX.crm.dynamics.com and for Sharepoint, it is the root URL of Sharepoint. So for example I am trying to log in to the site &lt;a class=&#34;link&#34; href=&#34;https://crm553494.sharepoint.com/sites/CRM&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://crm553494.sharepoint.com/sites/CRM&lt;/a&gt; that means my scope is &lt;a class=&#34;link&#34; href=&#34;https://crm553494.sharepoint.com/.default&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://crm553494.sharepoint.com/.default&lt;/a&gt;. Don&amp;rsquo;t be fooled here, just because you authenticate to the whole Sharepoint does not mean you are authorized for all sites within it. &lt;strong&gt;And remember, this won&amp;rsquo;t work due to the restriction that only authentication with a certificate is allowed with Sharepoint!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What you will get back is a JSON object that contains a property &amp;ldquo;access_token&amp;rdquo; that you will pass onto the real application in the header &amp;ldquo;Authorization&amp;rdquo; and the format &amp;ldquo;Bearer {access_token}&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Now as C#:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; PerformSPRequest(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; tenantId, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; resource, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; clientId, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; secret, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; site) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; token;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; form = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grant_type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_credentials&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;, clientId },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_secret&amp;#34;&lt;/span&gt;, secret },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/.default&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    FormUrlEncodedContent formContent = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FormUrlEncodedContent(form);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; url = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; aadClient = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HttpClient()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; response = aadClient.PostAsync(url, formContent).Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; responseContent = response.Content.ReadAsStringAsync().Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        response.EnsureSuccessStatusCode();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        token = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; JavaScriptSerializer().Deserialize&amp;lt;AzureActiveDirectoryToken&amp;gt;(tokenString).access_token;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spClient = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HttpClient()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        spClient.DefaultRequestHeaders.Authorization = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Bearer {token}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; content = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spUrl = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/sites/{site}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spResponse = spClient.PostAsync(spUrl, content).Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AzureActiveDirectoryToken&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; access_token { &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; As already mentioned, Sharepoint will reject the token due to it being created with a secret.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;debugging-a-token&#34;&gt;Debugging a token&lt;/h2&gt;
&lt;p&gt;This might become relevant when you are questioning why a token does not work. Paste your token to the website &lt;a class=&#34;link&#34; href=&#34;https://jwt.ms/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;jwt.ms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/other/certificate-authentication/jwt.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So a token consists of a header (red), a body (blue) and a footer/signature (green). The header is general information like that we are dealing with a JWT token and that RSA256 was used as an encryption algorithm. The body contains all the relevant information, like for whom (sub) and by whom (iss) the token was issued, how long is it valid (exp) etc. and so on. The footer is a signature of the body. This makes sure no one alters the body, for example extending the validity of an expired token. If you then check the signature against the body it would be invalid.
And in the end, all 3 parts are encoded as Base64 and split with dots, this is why they are so easy to analyze since the body is just a Base64 encoded JSON.&lt;/p&gt;
&lt;p&gt;Also, check the claims tab, this will explain to you how Sharepoint knows this is a token generated by a secret: The property &amp;ldquo;appidacr&amp;rdquo; gives it away!&lt;/p&gt;
&lt;h2 id=&#34;generating-a-certificate-token-with-a-nuget&#34;&gt;Generating a certificate token with a NuGet&lt;/h2&gt;
&lt;p&gt;There are multiple that can do it, but the NuGet &lt;code&gt;Microsoft.Identity.Client&lt;/code&gt; is the current official one by Microsoft.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Identity.Client;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; System.Security.Cryptography.X509Certificates;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; PerformSPRequest(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; tenantId, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; resource, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; clientId, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; secret, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; site) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;[] bytes = Convert.FromBase64String(secret);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cert = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; X509Certificate2(bytes);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; client = ConfidentialClientApplicationBuilder.Create(clientId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .WithCertificate(cert)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .WithAuthority(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}&amp;#34;&lt;/span&gt;).Build();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; authenticationResult = client.AcquireTokenForClient(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;[] { &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/.default&amp;#34;&lt;/span&gt; }).ExecuteAsync().Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spClient = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HttpClient()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        spClient.DefaultRequestHeaders.Authorization = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Bearer {authenticationResult.AccessToken}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; content = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spUrl = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/sites/{site}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spResponse = spClient.PostAsync(spUrl, content).Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see I am passing the Certificate as a Base64 string here because that&amp;rsquo;s easier to store for me with Dynamics. The constructor of &lt;code&gt;X509Certificate2&lt;/code&gt; can also take a file and you can work with a certificate store as well!
Nothing much more to say here, its just straightforward getting a token.&lt;/p&gt;
&lt;h2 id=&#34;why-i-dont-want-to-use-the-nuget&#34;&gt;Why I don&amp;rsquo;t want to use the NuGet&lt;/h2&gt;
&lt;p&gt;Essentially because I&amp;rsquo;m a Dynamics Developer. Dynamics 365 historically takes exactly one assembly for plugins and everything has to be contained inside apart from the system libraries like System.Net and so on. That&amp;rsquo;s because they are loaded to an isolated sandbox that will only contain your library. Well not quite&amp;hellip; Because the plugins you are running need to be provided with a connection to Dynamics, Microsoft&amp;rsquo;s CRM SDK is loaded as well and some more libraries, like &lt;code&gt;Microsoft.IdentityModel.Clients.ActiveDirectory&lt;/code&gt; which is likely used for the login. And this library can achieve a certificate authentication! Unfortunately, Microsoft has also marked this library deprecated in favor of Identity.Client, so you would rely on it. Also, this is a pretty specific dependency that could just stop working if Microsoft migrates to Identity.Client and we have no idea when it will break. So not a good option for production use cases.&lt;/p&gt;
&lt;p&gt;But what about &lt;code&gt;Microsoft.Identity.Client&lt;/code&gt;? It&amp;rsquo;s not loaded to the sandboxes, we could ILMerge it to our assembly! Yes, but that&amp;rsquo;s unsupported. The new Dependent Assembly Plugins feature can actually include this by packaging it together with the real assembly as a NuGet to upload. And it does work: In my &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/&#34; &gt;first shot post&lt;/a&gt; about this, this was the exact NuGet to include because of this exact challenge with Sharepoint.&lt;/p&gt;
&lt;p&gt;But this feature is in preview at the time of writing, none of the tooling other than the PRT (Plugin Registration Tool) supports it and frankly, it does not go that well with the existing project.&lt;/p&gt;
&lt;p&gt;When discussing this option again a colleague opened my eyes here with the comment &amp;ldquo;Did you run this with &lt;a class=&#34;link&#34; href=&#34;https://www.telerik.com/fiddler/fiddler-classic&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Fiddler&lt;/a&gt; and check what they [note: Microsoft] are doing?&amp;rdquo; Well, I closed as many programs as possible, ran an Integration Test from Visual Studio and had the result that we are going to talk about next.&lt;/p&gt;
&lt;h2 id=&#34;doing-it-without-a-nuget&#34;&gt;Doing it without a NuGet&lt;/h2&gt;
&lt;p&gt;So what is used here is quite cool: Instead of a classic 3-way handshake where AAD sends a challenge, you encrypt it with the Certificate and send it back, a self-signed JWT token is used.&lt;/p&gt;
&lt;p&gt;This token is issued by me for me and is signed by myself. When it is sent to AAD, it can check with the public part of the certificate that the signature is correct and can therefore be sure that the token, which only has a short lifespan, is issued by the correct client.&lt;/p&gt;
&lt;p&gt;To generate this token I went hunting in Microsoft&amp;rsquo;s NuGet. Luckily they do publish it open-source, therefore I was just able to grab relevant parts from there. I don&amp;rsquo;t want to make a super long exhibit here, so I will provide you with links to all relevant files in their repository instead and only print my part to the article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JWT Models: &lt;a class=&#34;link&#34; href=&#34;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Internal/JsonWebToken.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Internal/JsonWebToken.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Signing a JWT Token: &lt;a class=&#34;link&#34; href=&#34;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/CommonCryptographyManager.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/CommonCryptographyManager.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Base64Conversions: &lt;a class=&#34;link&#34; href=&#34;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Utils/Base64UrlHelpers.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Utils/Base64UrlHelpers.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; I modified and stripped the classes to then not need other things like System.Text.Json and for example the Base64UrlHelper we only need Encode.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;With these Classes in place, the code from above looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; System.Security.Cryptography.X509Certificates;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; PerformSPRequest(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; tenantId, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; resource, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; clientId, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; secret, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; site) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;[] bytes = Convert.FromBase64String(secret);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; cert = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; X509Certificate2(bytes);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; jwt = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; JsonWebToken(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; CommonCryptographyManager(), clientId, &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; signedToken = jwt.Sign(cert, Base64UrlHelpers.Encode(cert.GetCertHash()), &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; form = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grant_type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_credentials&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;, clientId },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_assertion_type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;urn:ietf:params:oauth:client-assertion-type:jwt-bearer&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_assertion&amp;#34;&lt;/span&gt;, signedToken },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/.default&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; tokenString = Client.Post(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token&amp;#34;&lt;/span&gt;, form);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; token = JsonSerializer.Deserialize&amp;lt;AzureActiveDirectoryToken&amp;gt;(tokenString).access_token;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spClient = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HttpClient()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        spClient.DefaultRequestHeaders.Authorization = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Bearer {token}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; content = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spUrl = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{resource}/sites/{site}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spResponse = spClient.PostAsync(spUrl, content).Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So the key to doing a simple certificate authentication with Azure Active Directory was the &amp;ldquo;client_assertion_type&amp;rdquo; being jwt-bearer and then the task is to create a valid bearer token. The fact that Microsoft publishes its code is a big help here since the relevant classes are not that big, the stripped classes are only around 400 loc without comments.&lt;/p&gt;
&lt;p&gt;Therefore skipping the NuGet is valid for the use case of authenticating with a certificate and has much fewer side effects than switching to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/&#34; &gt;Dependent Assembly Plugins&lt;/a&gt; and will help us big time in the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/&#34; &gt;Sharepoint Integration (Extended) series&lt;/a&gt;.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Cookies</title>
        <link>https://www.marius-wodtke.de/page/cookies/</link>
        <pubDate>Thu, 17 Aug 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/cookies/</guid>
        <description>&lt;p&gt;In general, this site is optimized to use no cookies other than functional. A functional cookie for example saves whether you selected the dark or the light theme.
However, some third-party functionality might not share this paradigm. The following sections try to explain the implications of enabling certain features with the toggles. Remember that you can always disable the toggles again and the corresponding third-party scripts will no longer be loaded, but also be aware that cookies that those scripts set remain in your browser until you clear them.&lt;/p&gt;
&lt;h2 id=&#34;comments&#34;&gt;Comments&lt;/h2&gt;
&lt;p&gt;Comments on this blog are delivered by &lt;a class=&#34;link&#34; href=&#34;https://giscus.app/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Giscus&lt;/a&gt;, which uses Github as the Backend. While Giscus itself is an open-source project that claims to use no tracking, their servers are hosted in the US, making you communicate overseas if you visit from Europe. Also if you want to leave a comment you will have to log in to &lt;a class=&#34;link&#34; href=&#34;https://github.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Github&lt;/a&gt;, which is owned by Microsoft. Check both &lt;a class=&#34;link&#34; href=&#34;https://giscus.app/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Giscus&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://github.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Github&lt;/a&gt; for their privacy statements before you decide to enable comments.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>PowerBI: Publishing a first Dashboard to Dynamics</title>
        <link>https://www.marius-wodtke.de/post/powerbi/first-dashboard/</link>
        <pubDate>Sun, 13 Aug 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/powerbi/first-dashboard/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/cover.png" alt="Featured image of post PowerBI: Publishing a first Dashboard to Dynamics" /&gt;&lt;p&gt;Did you know PowerBI Dashboards can be published to Dynamics? It&amp;rsquo;s a great feature! Not only does PowerBI have many options for displaying data that the regular Dynamics Dashboards lack, but it also has more options in styling. Not to speak of the possibility of integrating data from non-Dataverse sources.&lt;/p&gt;
&lt;h2 id=&#34;step-1-connecting-to-dataverse&#34;&gt;Step 1: Connecting to Dataverse&lt;/h2&gt;
&lt;p&gt;If you haven&amp;rsquo;t already, first install PowerBI Desktop. That can be done at the Microsoft Store or &lt;a class=&#34;link&#34; href=&#34;https://app.powerbi.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;app.powerbi.com&lt;/a&gt; by selecting the Download Icon at the top right. Technically I don&amp;rsquo;t think we will be using any feature today that would not work in the browser version at &lt;a class=&#34;link&#34; href=&#34;https://app.powerbi.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;app.powerbi.com&lt;/a&gt;. However, a best practice from my analytics colleagues: You are going to need a feature of the Desktop version sooner or later, so simply always use PowerBI Desktop.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/NewConnection.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;When creating a new Report, in the top ribbon, select &lt;em&gt;Get Data&lt;/em&gt;. Here you can choose from a lot of connections, for this post, we will use &lt;em&gt;Dataverse&lt;/em&gt;.
After entering your credentials you will be presented with a screen holding all available Dataverse instances and all tables within it. For the demonstration, I chose Account and Contact, since these 2 have a relationship that we will display later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/Tables.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/ConnectionSetting.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;DirectQuery is limited in its transformation options. Therefore Import should be chosen by default.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;step-2-transform-data&#34;&gt;Step 2: Transform Data&lt;/h2&gt;
&lt;p&gt;Here we are not going to do a lot for the first Dashboard. You should be aware that you can introduce new columns here and delete others to reduce the model and correct column values. That would be needed here since in the later screenshots you will see that &amp;ldquo;USA&amp;rdquo; is also written as &amp;ldquo;U.S.&amp;rdquo; and &amp;ldquo;United States&amp;rdquo; in the sample data. But at this stage I wouldn&amp;rsquo;t have known that issue, only after visualizing it I can go back to fix it in &lt;em&gt;Transform Data&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But I wanted to show you a cool detail here: I suspected needing to map the optionset values to friendly names. But it did that in the base language for me!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/code.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/codeName.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;step-3-relations&#34;&gt;Step 3: Relations&lt;/h2&gt;
&lt;p&gt;PowerBI also recognizes the relationships between these entities. Unfortunately, there are 2 of them. The parentcustomerid is where the contact references the account it belongs. And primarycontactid where an account selects its main contact. PowerBI can only have one of the two in the model, trying to create a star in the dependencies, a doubled relationship would hamper that structure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/Relations.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;step-4-visualizing&#34;&gt;Step 4: Visualizing&lt;/h2&gt;
&lt;p&gt;Yeah, I don&amp;rsquo;t want to talk too much about it. Apart from me never getting it to a beautiful state, it does not matter too much for the later stages. Be creative here. I used an account/country pie chart, an account list, a contact list and a gauge with the annual revenue for the demo: If I select a country in the pie chart, the account list is filtered. If I select an account there, the contact list gets filtered accordingly. The gauge filtering of course works for both of these filtering operations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/Report.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/ReportFiltered.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The report filtered down to a single account&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;step-5-the-powerbi-service&#34;&gt;Step 5: The PowerBI Service&lt;/h2&gt;
&lt;p&gt;In the screenshot above I already marked the publish button. We will use it now to upload the report to the PowerBI Service. For the demo the &amp;ldquo;MyWorkspace&amp;rdquo; default value is fine, but for an enterprise project, you will probably want to have a dedicated workspace for your Dataverse Dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/Publish.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/PublishDone.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now we continue at &lt;a class=&#34;link&#34; href=&#34;https://app.powerbi.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;app.powerbi.com&lt;/a&gt;. We can&amp;rsquo;t show the Report as a Dashboard in Dynamics, we need a PowerBI Dashboard! We can just create that in the Workspace and then navigate to the Report again and use the &lt;em&gt;Pin to Dashboard&lt;/em&gt; option from the ribbon to pin the current page. Make sure no unwanted filters are active because these would be snapshotted as well!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/NewDashboard.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/PinToDashboard.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/PinDialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now the Dashboard looks like the report and we can proceed showing it in Dynamics, but there is one more step to ensure the data stays up to date: The scheduled refresh. For this, head back to the Workspace, find the dataset and select the &lt;em&gt;Scheduled Refresh&lt;/em&gt; Icon. Here you will need to configure which account to use for the refresh and how often to refresh. Usually Daily is sufficient, but sometimes you will need quicker refresh rates. If they get really low you will have to take a look at your performance as well, because data might be outdated by the time it was processed by the service. On the other hand, some systems might have very long times between data changes, for example, budgetary databases only being updated once a year.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/ScheduledRefresh.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/RefreshDialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;step-6-embedding-in-dynamics&#34;&gt;Step 6: Embedding in Dynamics&lt;/h2&gt;
&lt;p&gt;Now the data is published (yet only available to the members of the workspace), kept up to date and part of a dashboard that we can publish. PowerBI Dashboards in Dynamics cannot be created for the whole system, only as personal Dashboards. So, head to an app with Dashboards and select &lt;em&gt;New PowerBI Dashboard&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/NewDynDashboard.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/DynDashboardDialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;In the wizard, we select our Dashboard from step 5 and then it is done!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/DynDashboard.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/DynDashboardFiltered.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; Missing the &amp;ldquo;PowerBI Dashboard&amp;rdquo; Option? Make sure the PowerBI Embedding Feature is enabled on your environment under Settings -&amp;gt; Features -&amp;gt; Embedded Content.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/FeatureSwitch.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;step-7-sharing&#34;&gt;Step 7: Sharing&lt;/h2&gt;
&lt;p&gt;Now, of course, not every user should go through these steps just because this is a personal Dashboard! Let&amp;rsquo;s share it. I&amp;rsquo;ve again marked the share button in the screenshot above. But that&amp;rsquo;s not enough! The users can then select the dashboard, but they won&amp;rsquo;t be able to see its contents since they don&amp;rsquo;t have access to the PowerBI Service Components we created.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/powerbi/first-dashboard/ManageWorkspace.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;When adding the users here, it is sufficient to only add them as viewers. They will still be able to use the filtering, but they can&amp;rsquo;t alter the dashboard. This contributor capability should only be given to a few users who are maintaining the report with you.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;While the article is quite long due to its many pictures, the actual time it takes to make such a dashboard is short. The most time spent in PowerBI is usually the modeling part. Since we used a Dataverse source here and just kept everything as is, that was quick as well. While publishing such a dashboard is arguably more complex than just creating a system Dashboard in Dynamics, you have much more freedom designing the Dashboard in PowerBI. Also, the possibility to include other data sources is a real game changer, but that&amp;rsquo;s something for another article.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My First Shot at Dataflows with SQL</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/dataflows/</link>
        <pubDate>Sun, 06 Aug 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/dataflows/</guid>
        <description>&lt;p&gt;This time I will do my first experiments with Dataflows. Spoiler alert: There are quite some options left that would need a separate article, so at first glance, this seems to be a powerful feature.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;As a data source the same Azure SQL Database will be used that we already used for &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/externaltable/&#34; &gt;the External Table post&lt;/a&gt;. If you do not have a table ready, that post describes how it was set up in Azure with minimal cost. For reference, this is what the structure of the table looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/SQLTable.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;creating-the-dataflow&#34;&gt;Creating the Dataflow&lt;/h2&gt;
&lt;p&gt;On make.powerapps.com, ensure you are on the correct environment and select Dataflows from the left menu. For me, it was hidden in the &lt;em&gt;More&lt;/em&gt; flyout. If you can&amp;rsquo;t find it there either, try &lt;em&gt;Discover all&lt;/em&gt; as this menu shows all Power Platform components. Still not finding it? Check that your environment has a database, this is required as a Dataflow will persist records to a Dataverse table.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/New.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;To create a new Dataflow, select New Dataflow and give it a name.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After giving our Dataflow a name, the next screen will present the selection of sources. There is a great variety of connectors, but for this first shot, the trusty SQL table will do. When I entered my server and database the wizard recognized that I had a connection to this database already and preselected it, nice! Therefore I will refer you to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/externaltable/&#34; &gt;the External Table post&lt;/a&gt; here as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/Connection.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now we select the table we want to import and then we can transform the data. This is very interesting as with the External Table the data has to be exactly right in the source database, this time around we can tweak it. As this is the &amp;ldquo;first shot&amp;rdquo; article, I have done the minimum here and only used &lt;em&gt;Add Table from Examples&lt;/em&gt; to add a year column that extracts from the last modified column.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/SelectTable.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Certainly, the button Select related tables triggers my interest in how we will deal with relationships with this feature.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/TransformData.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A column “year” was added.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Once we are done Transforming, for which you can spend days if the data is not already in the shape we need it, it&amp;rsquo;s time to select the destination.
First I took a Products table I created sometime earlier and tried the &lt;em&gt;Auto Map&lt;/em&gt; feature.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/AutoMap.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;That worked well everywhere where the names matched, the ID fields were not automatically mapped because they are named differently than in the database. So be prepared to do the mapping manually if your database uses more technical names.&lt;/p&gt;
&lt;p&gt;The other option is to create a new entity. I resorted to this to start from clean ground when evaluating the records, but take a look at the publisher prefix!
So then I exited the wizard to recreate the dataflow in a solution, but I did not find it in the menu in the solution, only Dataflow Template was available or importing an existing Dataflow. I think that&amp;rsquo;s something to revisit when looking at deployment of these, but this side track had a nice learning: When I exited the wizard the system saved my Dataflow as unpublished so I could edit it to finally create my fresh entity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/NewTable.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The last dialog is about when the import will happen, on-demand or scheduled. Theoretically, the wizard allows you to go down to minutes, but depending on the size of your data this will not be enough to insert/update it in Dataverse. I chose daily here, probably a common choice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/Schedule.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The Result&lt;/h2&gt;
&lt;p&gt;That will be a quick section: No problem here! At the end of the wizard, select publish, it will take a few minutes. Then open the Advanced Find, look for the entity you mapped or created and the data should be there.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/dataflows/AdvancedFind.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Everything looks fine here, all the data, including our custom &amp;ldquo;Year&amp;rdquo; column was transported. The creator/owner of these records might be interesting in use cases outside an experiment: I never had to select who this should be, so apparently it&amp;rsquo;s just the owner of the Dataflow. For a development environment that might be fine, but a production environment should probably have a service user here. Again something to look out for when trying to deploy a dataflow.&lt;/p&gt;
&lt;p&gt;Since the entity filled with data here is a regular entity, you can integrate it into Model Driven Apps, create forms and so on. You could even add new records or edit existing ones. However, keep in mind that the changed data will be overwritten with the next execution of the Dataflow. But adding additional fields that are not mapped from the source would stay intact as well as new records (unless you selected the option to delete records not present in the source).&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Dataflows import data to regular Dataverse tables. They achieve this with definitions that are much like PowerBI, giving us many online resources on how to transform data before loading it to Dataverse. In contrast to External Tables, Dataflows create an editable copy of the data. This also means that the data is not real-time and is only as fresh as the last refresh.&lt;/p&gt;
&lt;p&gt;There is much to explore here: Different column types like Choice and Currency. Relationships within the source or to Dataverse. Deployment topics like Dataflows vs Dataflow Templates. But for today&amp;rsquo;s experiment, this is enough. Keep your eyes peeled for more Dataflow content!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Extended) 2: Using PnP.Core</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/custom/pnpcore/</link>
        <pubDate>Sun, 30 Jul 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/custom/pnpcore/</guid>
        <description>&lt;p&gt;So now that we have a login from &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/appregistration/&#34; &gt;the last post&lt;/a&gt;, let&amp;rsquo;s use it! In this post, we will use PnP.Core to help us interact with Sharepoint. After all, I am a CRM Developer and not a Sharepoint specialist, so a library to help with my tasks is most welcome.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;So I do assume you have the service principle set up. If not it&amp;rsquo;s time to head back &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/custom/appregistration/&#34; &gt;here&lt;/a&gt;.
The next rather important consideration is PnP.Core as a library itself. It has a lot of dependencies, usually, that&amp;rsquo;s not a problem, but this means we won&amp;rsquo;t be able to upload it to Dynamics, no ILMerge or &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/&#34; &gt;Plugin Packages&lt;/a&gt; will help here due to unmanaged code in some dependencies. This means that this method is not for plugins but for standalone tools, APIs etc.&lt;/p&gt;
&lt;p&gt;To give you some samples of what I have had to build that benefited from the help of PnP.Core:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A tool that migrates a file archive from a file share to Sharepoint. Connect them to the according record in CRM by alternate key.&lt;/li&gt;
&lt;li&gt;An API function that takes a form filled by the user alongside pictures. The CRM user should be able to review the pictures when using the documents tab.&lt;/li&gt;
&lt;li&gt;A tool that cleans up all versions of documents, since the Sharepoint storage was going overboard with frequent changes to large files.
As you can see, the first 2 requirements are to &lt;em&gt;take files and &amp;ldquo;connect&amp;rdquo; them to Dynamics&lt;/em&gt; while the 3rd sample is an exclusive Sharepoint problem, but the reality is, no matter if the requirements fall more to Sharepoint or more to Dynamics, in the end usually there is no one else to implement it so after all, you as Dynamics developer need to somehow cover it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, I will be using .Net Core in this tutorial. This is technically not required, but with the &lt;a class=&#34;link&#34; href=&#34;https://powerapps.microsoft.com/en-us/blog/dataverse-service-client-is-now-generally-available/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;general availability of the Dataverse Service Client&lt;/a&gt; for .Net Core, I can&amp;rsquo;t see a good reason anymore to stick with .Net Framework for external tools like the one we will be building here.&lt;/p&gt;
&lt;p&gt;And one last thing: I do somehow assume you do know how to use the Dataverse Service Client or the classic CrmServiceClient (IOrganizationService), so the code shown will completely ignore the CRM bits and instead will mock corresponding queries as simple variables to keep the focus on what we are trying to achieve: Integrating Sharepoint.&lt;/p&gt;
&lt;h2 id=&#34;authenticating&#34;&gt;Authenticating&lt;/h2&gt;
&lt;p&gt;Before we can manipulate Sharepoint we need to log in.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve started with a Console App (.Net 6.0) and installed 2 NuGet packages &lt;em&gt;PnP.Core&lt;/em&gt; and &lt;em&gt;PnP.Core.Auth&lt;/em&gt;.
Then, with the new top-level statements feature I can code directly into the Program.cs file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Extensions.DependencyInjection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; Microsoft.Extensions.Hosting;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; PnP.Core.Auth.Services.Builder.Configuration;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; PnP.Core.Services;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; PnP.Core.Services.Builder.Configuration;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; authName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;certificate&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; siteName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CRM&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; host = Host.CreateDefaultBuilder()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.ConfigureServices((hostingContext, services) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    services.AddPnPCore(options =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options.PnPContext.GraphFirst = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options.HttpRequests.UserAgent = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ISV|MariusWodtke|PnPDemo&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options.Sites.Add(siteName, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PnPCoreSiteOptions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            SiteUrl = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://crmXXXX.sharepoint.com/sites/CRM&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    services.AddPnPCoreAuthentication(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            options.Credentials.Configurations.Add(authName,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PnPCoreAuthenticationCredentialConfigurationOptions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ClientId = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cce5e182-eb79-40d9-abd6-3dc7c4c54718&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    TenantId = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;7cd962d3-c5b5-4762-89f3-0e72d8f8b08c&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    X509Certificate = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PnPCoreAuthenticationX509CertificateOptions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        Certificate = GetCertificate(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CC4D7175E37641B38A4960BB8EFC8C56653DFF8E&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            options.Credentials.DefaultConfiguration = authName;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            options.Sites.Add(siteName,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PnPCoreAuthenticationSiteOptions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    AuthenticationProviderName = authName
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.UseConsoleLifetime()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.Build();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is very similar to the sample found in the &lt;a class=&#34;link&#34; href=&#34;https://pnp.github.io/pnpcore/using-the-sdk/readme.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;getting started guide&lt;/a&gt; of PnP.Core. I chose the &amp;ldquo;no appsettings.json&amp;rdquo; variant and changed the authentification credentials to use a certificate. While we are on the subject of the certificate, if you haven&amp;rsquo;t already, double-click the .pfx file we created last time, select &amp;ldquo;Local Machine&amp;rdquo; in the first dialog window and then click next until the dialog finishes, the defaults should be fine. Now our computer knows the secret part of the certificate and will be able to use it for logging in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/PnPCore/InstallCert.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The retrieval of that secret is done by the &lt;code&gt;GetCertificate&lt;/code&gt; method mentioned in the code above. It takes the Thumbprint of the certificate, which I always find in the AppRegistrations &lt;em&gt;Certificates &amp;amp; Secrets&lt;/em&gt; section, but there are probably other ways as well. The code of this method looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;X509Certificate2 GetCertificate(&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; certThumbprint)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    X509Store store = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; X509Store(StoreLocation.LocalMachine);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    store.Open(OpenFlags.ReadOnly);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; certs = store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (certs.Count &amp;lt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArgumentException(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;No certificate found by Thumbprint: {certThumbprint}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; certs[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since this method uses the &lt;code&gt;StoreLocation.LocalMachine&lt;/code&gt; it is important that you select &amp;ldquo;Local Machine&amp;rdquo; when installing the certificate. If you didn&amp;rsquo;t do so don&amp;rsquo;t worry, you can install the .pfx again or change the StoreLocation! The reason for me to use LocalMachine here is that it makes the deployment to servers easier where the program might not be executed by the same account deploying the server, hence the CurrentUser installation yields problems in this scenario.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-file&#34;&gt;Creating a file&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s do something! I will upload a file from my computer to Sharepoint. I know you are bursting with excitement for this amazing task, but doing this ensures that the login works and we have the correct permissions and then you can hop on doing other things and if you get weird errors you at least know it&amp;rsquo;s your code that is broken and not the service principle or something like that.&lt;/p&gt;
&lt;p&gt;To have a file to upload I added a file &lt;em&gt;Test.txt&lt;/em&gt; to the project, wrote &amp;ldquo;Hello World!&amp;rdquo; inside and set &lt;em&gt;Copy to Output Directory&lt;/em&gt; to &amp;ldquo;Copy always&amp;rdquo;. Next comes the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; host.StartAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; scope = host.Services.CreateScope())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; pnpContextFactory = scope.ServiceProvider.GetRequiredService&amp;lt;IPnPContextFactory&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; context = &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; pnpContextFactory.CreateAsync(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CRM&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; file = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Test.txt&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; accountName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fabrikam West&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; accoountId = Guid.Parse(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fb6b3f4b-1be7-e611-8101-e0071b6af231&amp;#34;&lt;/span&gt;).ToString(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;N&amp;#34;&lt;/span&gt;).ToUpper();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; foldername = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{accountName}_{accoountId}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; list = &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; context.Web.Lists.GetByTitleAsync(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Account&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; folder = &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; list.RootFolder.EnsureFolderAsync(foldername);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; spFile = folder.Files.Add(file, File.OpenRead(file), &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;host.Dispose();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, we used &lt;code&gt;IPnPContextFactory.CreateAsync&lt;/code&gt; to get a Context like the ServiceContext you know from Dynamics. Next &lt;code&gt;PnPContext.Web.Lists.GetByTitleAsync&lt;/code&gt; queries the library we are working in. Since the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/&#34; &gt;Sharepoint standard series&lt;/a&gt; focuses on the account, I&amp;rsquo;ve used it here as well.
Then we create a Folder in the library with &lt;code&gt;RootFolder.EnsureFolderAsync&lt;/code&gt; where EnsureFolder works like a GetOrCreate, so there are no problems if the folder is already present. And finally, we will call &lt;code&gt;Files.Add&lt;/code&gt; to upload our file. Straight forward I guess.
While I&amp;rsquo;ve left out all CRM calls you can already see that I&amp;rsquo;ve leveraged the knowledge from &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/innerworkings/&#34; &gt;the inner workings post&lt;/a&gt; to make sure the folder is exactly named like Dynamics would name it. This has an interesting side effect, when I now open up the Account in Dynamics and head to the &lt;em&gt;Documents&lt;/em&gt; section, Dynamics will show me the uploaded file, likely because it also uses something like EnsureFolder to create it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/PnPCore/DocumentsTab.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s cool, but I want to warn you about banking on this behavior: If someone changes the Account name by a single letter before opening the Documents section, Dynamics will generate a new, different folder.&lt;/p&gt;
&lt;p&gt;Instead, you should create a &lt;em&gt;documentlocation&lt;/em&gt; to connect them immediately. That way the changed name is not a problem, Dynamics finds the existing location and can show it. For this you will need to set these 3 fields:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/PnPCore/DocumentLocation.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;regardingobjectid and relativeurl should be no problem, this is the account and the foldername. In simple scenarios parentsiteorlocation is simple as well, query for the documentlocation with relativeurl &amp;ldquo;account&amp;rdquo;, so the logicalname of the entity. This stops being so easy when you have two sites with a Library &amp;ldquo;Account&amp;rdquo; since suddenly you have 2 documentlocations with the relativeurl &amp;ldquo;account&amp;rdquo; but different parent sites. Also if you want to connect to a location in the hierarchical setup, this gets trickier as well, but in this case, you will already have to deal with that once you are creating the folder because it then is no longer under the root folder but 2 folders deeper.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;But after retesting and writing this article, I have to say PnP.Core makes it easy to dip your toes into &amp;ldquo;Sharepoint Development&amp;rdquo;. Well we are not developing here, we are manipulating the content of Sharepoint with programs.
The setup is easy and the code you need to write to do basic manipulations is quite short. For more advanced manipulations that are not as intuitive as &lt;code&gt;Files.Add&lt;/code&gt;, check out their great documentation with samples and tutorials here: &lt;a class=&#34;link&#34; href=&#34;https://pnp.github.io/pnpcore/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://pnp.github.io/pnpcore/index.html&lt;/a&gt;
The only downside to this SDK is its dependencies, making it unsuitable for Dynamics Plugins. This is why we will explore using the Sharepoint REST API in future posts as well. A little heads up: This is of course more cumbersome, so I do use PnP.Core whenever the requirements allow it.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Standard) 4: The Inner Workings</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/standard/innerworkings/</link>
        <pubDate>Sun, 23 Jul 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/standard/innerworkings/</guid>
        <description>&lt;p&gt;Having some knowledge about the inner workings of this integration is often beneficial. It helps you track bugs and understand what can work and what can not.
Of course, I have never seen the code of Microsoft and therefore this is merely a compilation of observations.&lt;/p&gt;
&lt;h2 id=&#34;the-basics&#34;&gt;The basics&lt;/h2&gt;
&lt;p&gt;While we talked a lot about the &lt;em&gt;Sharepoint Document&lt;/em&gt; &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/customizing/&#34; &gt;in the post about customizing&lt;/a&gt;, here we will talk about the &lt;em&gt;Document Location&lt;/em&gt;. This entity mainly consists of a &lt;em&gt;Relative URL&lt;/em&gt; which holds the current path element and s &lt;em&gt;Parent Site or Location&lt;/em&gt; which is a hierarchical lookup to another &lt;em&gt;Document Location&lt;/em&gt; until eventually, this lookup will point to a &lt;em&gt;Sharepoint Site&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first talk about the other Document Locations: All those hold a relative URL as well, so if you go up from the leaf with URL &amp;ldquo;abc&amp;rdquo; to parent &amp;ldquo;def&amp;rdquo; and from there to another parent &amp;ldquo;ghi&amp;rdquo; these elements will result in a path &amp;ldquo;ghi/def/abc&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;And the &lt;em&gt;Sharepoint Site&lt;/em&gt; has an &lt;em&gt;Absolute URL&lt;/em&gt;. So if we assume that the &amp;ldquo;ghi&amp;rdquo; location looks up to the site &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://mycompany.sharepoint.com/sites/CRMDev%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://mycompany.sharepoint.com/sites/CRMDev&#34;&lt;/a&gt; the final full URL to the Sharepoint folder would be &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://mycompany.sharepoint.com/sites/CRMDev/ghi/def/abc%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://mycompany.sharepoint.com/sites/CRMDev/ghi/def/abc&#34;&lt;/a&gt;.
The sites can also be hierarchical. You could use this if you are going to integrate a lot of sites, making &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://mycompany.sharepoint.com/sites%22&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://mycompany.sharepoint.com/sites&#34;&lt;/a&gt; the base and then adding on child sites with the concrete relative URL.&lt;/p&gt;
&lt;p&gt;Why am I telling you all this? Dynamics is creating the sites for me &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/setup/&#34; &gt;when setting up&lt;/a&gt; the Sharepoint Integration and the locations are automatically created when I open &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/functionality/&#34; &gt;the documents tab&lt;/a&gt;.
Because we might want to create our own locations! Or find out the URL of an existing location. Important things when you want to do some information with something like a flow.&lt;/p&gt;
&lt;h2 id=&#34;common-path-patterns&#34;&gt;Common path patterns&lt;/h2&gt;
&lt;p&gt;The easiest is just {site}/{library}/{folder}. This is the default when no hierarchy is enabled. Dynamics creates a library for the entity with the entitylogicalname as URL and then the folders below it. In this situation, you can assume there are exactly two parents, one location that is not regarding any record and the site. This setup in three pictures:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/InnerWorkings/RecordFolder.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The folder regarding our account.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/InnerWorkings/LibraryFolder.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;When navigating to the parent we find the library.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/InnerWorkings/Site.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;And its parent is the site.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;With hierarchical its a bit more complicated: {site}/{library}/{folderParent}/{logicalname}/{folderChild}. So for example below the site we have the libary &amp;ldquo;account&amp;rdquo;, then the URL of the account record folder, then another folder with a logicalname, e.g. &amp;ldquo;incident&amp;rdquo; and finally our folder regarding the concrete record. At the latest in this setup it&amp;rsquo;s the best to think recursive: Step up to the parent until there is no parent left. Then combine the paths backward to get the full URL.&lt;/p&gt;
&lt;h2 id=&#34;when-is-a-folder-created&#34;&gt;When is a folder created&lt;/h2&gt;
&lt;p&gt;Dynamics only checks for the presence of a &lt;em&gt;Document Location&lt;/em&gt; which is &lt;em&gt;Regarding&lt;/em&gt; the current record when opening the Documents Tab. If one is present no folder or location is created, no matter if the existing location is valid!&lt;/p&gt;
&lt;p&gt;But when a folder is created the &lt;em&gt;Relative URL&lt;/em&gt; is interesting: It is the primary field of the record (the &amp;ldquo;name&amp;rdquo; field) where it replaces some illegal characters, e.g. a dot (.) is replaced with an underscore (_). Then follows an underscore and then the Guid of the record in all uppercase and without dashes (-). In my sample &amp;ldquo;a16b3f4b-1be7-e611-8101-e0071b6af231&amp;rdquo; becomes &amp;ldquo;A16B3F4B1BE7E6118101E0071B6AF231&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I think even if you are creating your folders, you should obey this scheme. It avoids some concurrency problems (the user opening the Documents tab while you are creating the folder) and it makes a lot of sense: The primary field is easy to identify for regular users while the Guid is unique, making sure there are no conflicts with identical folder names. Think about the contact here, it&amp;rsquo;s quite common for people to have the same first and last name, leading to an identical &lt;em&gt;fullname&lt;/em&gt; without actually having a duplicate. This situation should have 2 different folders for the 2 records, therefore it&amp;rsquo;s a good idea to always include the Guid.&lt;/p&gt;
&lt;h2 id=&#34;when-something-happens-in-sharepoint&#34;&gt;When something happens in Sharepoint&lt;/h2&gt;
&lt;p&gt;So it&amp;rsquo;s a bad idea to rename folders in Sharepoint. Because it is not reflected in the locations in Dynamics. It&amp;rsquo;s a one-way integration: Dynamics can create folders, write files, request a listing of files and download a file. All these operations are performed by the front-end component of Dynamics, no backend integration will sync back changes from Sharepoint.&lt;/p&gt;
&lt;p&gt;But if it happened you can fix it: Find out in the Office 365 Compliance Center Audit Log what was the original and new path of the renamed folder, find the document location with the original path element and change it to the new path. If some user has shortened my sample folders&amp;rsquo; name from &amp;ldquo;A Datum Corporation_A16B3F4B1BE7E6118101E0071B6AF231&amp;rdquo; to &amp;ldquo;A Datum Corporation&amp;rdquo; I just need to reflect that in the location record to fix the displayed error there is no folder with the relative URL &amp;ldquo;A Datum Corporation_A16B3F4B1BE7E6118101E0071B6AF231&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The components of the integration are easy to understand, having the &lt;em&gt;Sharepoint Site&lt;/em&gt; as a base and then a variable amount of hierarchy in the &lt;em&gt;Document Location&lt;/em&gt;. To get an effective URL you need to traverse the parents and combine the relative URLs backward. The grouping folders of the entities are named like the logical name of the entity and the actual folders for the records have a combination of the primary field and the records Guid to ensure uniqueness.
The last important note is that Dynamics does not react to changes in Sharepoint. This is no problem when you upload a new file or create an additional folder, but renaming folders is a delicate matter that needs to be covered in your workflow. Or you simply agree to not rename them once created.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Standard) 3: Customizing</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/standard/customizing/</link>
        <pubDate>Sun, 16 Jul 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/standard/customizing/</guid>
        <description>&lt;p&gt;If you are not entirely happy with the default functionality shown in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/functionality/&#34; &gt;the previous post&lt;/a&gt;, there are some options to tailor it. This article will discuss some customization options available to people and projects not willing to build extensive PCF controls or something similar.&lt;/p&gt;
&lt;h2 id=&#34;showing-metadata&#34;&gt;Showing Metadata&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;Documents&lt;/em&gt; Tab shows a grid and you can customize it. The entity is called &lt;em&gt;Sharepoint Document&lt;/em&gt; (sharepointdocument) and has the &amp;ldquo;Document Associated Grid&amp;rdquo; which is shown by default and the &amp;ldquo;All Sharepoint Documents&amp;rdquo; is shown when you select to show all locations.&lt;/p&gt;
&lt;p&gt;You can choose one of the predefined columns, maybe something like &amp;ldquo;Folder Child Count&amp;rdquo; might help the end users&amp;rsquo; experience. But you can also create custom columns. To do this, create a column in Sharepoint, then create it with the same &lt;strong&gt;Display&lt;/strong&gt; Name and type. Just for &lt;em&gt;Choice&lt;/em&gt;, I noticed that I needed to create it as a &lt;em&gt;Single Line of Text&lt;/em&gt; in Dynamics. Then you can add the new field to one of the views, displaying it to the user.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/SharepointColumn.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/DynamicsColumn.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/View.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/DynamicsGrid.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/SharepointView.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;One note though: You can&amp;rsquo;t edit these columns in Dynamics, you will have to switch over to Sharepoint with the &lt;em&gt;Open Location&lt;/em&gt; button.&lt;/p&gt;
&lt;h2 id=&#34;displaying-the-view-more-prominent&#34;&gt;Displaying the view more prominent&lt;/h2&gt;
&lt;p&gt;You can add the Documents grid to other tabs as well, not requiring the user to visit the &lt;em&gt;Documents&lt;/em&gt; Tab. It even works with &lt;em&gt;Quick View Forms&lt;/em&gt; if you for example want to display the documents of the parent account at the child contacts. However please note, that displaying this grid won&amp;rsquo;t create a location automatically like it happens in the &lt;em&gt;Documents&lt;/em&gt; Tab, the user will have to visit it once to create the folder. Or they use the &lt;em&gt;Add Location&lt;/em&gt; button to create it.&lt;/p&gt;
&lt;p&gt;To add the grid to an existing tab, in the Form editor select &amp;ldquo;Add Subgrid&amp;rdquo;. In the dialog, select &amp;ldquo;Show Related Records&amp;rdquo; and then the relationship to display is &amp;ldquo;Documents&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/AddSubgrid.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/Subgrid.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;customizing-the-ribbon&#34;&gt;Customizing the Ribbon&lt;/h2&gt;
&lt;p&gt;Another customizing option can be realized via the &lt;a class=&#34;link&#34; href=&#34;https://www.develop1.net/public/rwb/ribbonworkbench.aspx&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RibbonWorkBench&lt;/a&gt; of the &lt;a class=&#34;link&#34; href=&#34;https://www.xrmtoolbox.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;XrmToolbox&lt;/a&gt;. First, you need to create a new solution. Then add the &lt;em&gt;Sharepoint Document&lt;/em&gt; entity without any subcomponents. Now you can load the Solution into the RibbonWorkBench and start customizing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/Buttons.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The special document buttons are marked green.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For demonstration, I used the &lt;em&gt;Hide&lt;/em&gt; action on the &lt;em&gt;Flow&lt;/em&gt; Button which did not work for me in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/functionality/&#34; &gt;the previous post&lt;/a&gt; and published the solution to apply the changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Customizing/ButtonMissing.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Of course, you can also add new buttons or change &lt;em&gt;Display/Enable Rules&lt;/em&gt; on existing commands.&lt;/p&gt;
&lt;h2 id=&#34;document-location&#34;&gt;Document Location&lt;/h2&gt;
&lt;p&gt;There is also the entity &amp;ldquo;Document Location&amp;rdquo; (documentlocation). It is not directly shown in the grid, but the records of this entity hold the information where the actual Sharepoint folder is and is shown in some parts of the grid. The display name is shown in the breadcrumbs of the navigation and the flyouts for the &amp;ldquo;Location&amp;rdquo; buttons of the ribbon.&lt;/p&gt;
&lt;p&gt;Since the default name is &amp;ldquo;Documents on Default Site 1&amp;rdquo; I can imagine users wanting that to change. They can of course via &lt;em&gt;Edit Location&lt;/em&gt;, but if you can agree on a standard naming, an automation like a Flow could do that. But keep in mind that there might be multiple locations if users add an additional location, having them all named the same might lead to a nightmare finding the correct folder.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;While there are many fixtures with this integration, Microsoft has left room for us to customize the experience we have interacting with Sharepoint.
You can add the Sharepoint view to other tabs and manipulate the columns shown. You can also add custom columns and buttons to the view. You can also manipulate existing buttons to focus the user on his current task. Lastly, there are also some options to change the names on the locations, allowing easier sorting when having multiple.&lt;/p&gt;
&lt;p&gt;In the next post, we will discuss more about the internals of the Sharepoint integration, which might help you to create more advanced modifications and extensions.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Standard) 2: OOTB Functionality</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/standard/functionality/</link>
        <pubDate>Sun, 09 Jul 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/standard/functionality/</guid>
        <description>&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/setup/&#34; &gt;the previous post&lt;/a&gt; we set up the Sharepoint integration for our instance, what can we do now?&lt;/p&gt;
&lt;h2 id=&#34;the-documents-tab&#34;&gt;The Documents Tab&lt;/h2&gt;
&lt;p&gt;On the entities where you enabled the Integration in the wizard, you should be able to access the &lt;em&gt;Documents&lt;/em&gt; Tab. This is the main hub for interacting with Sharepoint.
I will be working with the Account for this post as I enabled this entity when setting up the integration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/DocumentsTab.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;newuploadpreviewdownload&#34;&gt;New/Upload/Preview/Download&lt;/h2&gt;
&lt;p&gt;With the &lt;em&gt;+ New&lt;/em&gt; button you can create new documents, but only subfolders and some of the most common Office formats. For everything else, you would use the &lt;em&gt;Upload&lt;/em&gt; button which displays a dialog to select a file from your computer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/New.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Once you created/uploaded a file you can just double-click it to preview or download it in case there is no previewer in the browser like for the .step file in the image above.&lt;/p&gt;
&lt;p&gt;If you double-click a folder you will navigate the grid inside the folder and the breadcrumbs will extend, you can use them to navigate up again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/Subfolders.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;check-inouteditdelete&#34;&gt;Check-In/Out/Edit/Delete&lt;/h2&gt;
&lt;p&gt;When you single-click a file (or mark the checkbox on the left) the ribbon changes. It now shows buttons for &lt;em&gt;Check in&lt;/em&gt;/&lt;em&gt;Check out&lt;/em&gt;, a feature to lock files for editing. In my experience, this is not used that much anymore since the office documents do support multiple users editing at the same time but maybe you will find this helpful when working with formats that do not support collaboration.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Edit&lt;/em&gt; button is something you won&amp;rsquo;t need, it&amp;rsquo;s the same as double click! &lt;em&gt;Delete&lt;/em&gt; should explain itself. A little bit more interesting is &lt;em&gt;Edit Properties&lt;/em&gt; as the name suggests that it allows editing the metadata columns. However, only the filename and title are available in the dialog, making it kind of a &amp;ldquo;Rename&amp;rdquo; button.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/SingleFile.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Another potentially interesting button is the &lt;em&gt;Flow&lt;/em&gt; button which usually lets you execute a Flow on the selected record, allowing admins to define Flows like &amp;ldquo;Send file for approval&amp;rdquo;. However, in my demo environment creating a Flow from here does not work! If you made it work somehow, let me know and I will happily update the article.&lt;/p&gt;
&lt;h2 id=&#34;location-actions&#34;&gt;Location Actions&lt;/h2&gt;
&lt;p&gt;There are several buttons for the &amp;ldquo;location&amp;rdquo;, which means the folder that is connected to Dynamics. Let&amp;rsquo;s first talk about &lt;em&gt;Add Location&lt;/em&gt;. While Dynamics created a folder for you when you entered the &lt;em&gt;Documents&lt;/em&gt; Tab this does not mean it needs to be the only folder! With add location you can add a second location (and more) and they do not necessarily need to be on the same site!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/Locations.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;When multiple Locations are created, the &lt;em&gt;Document Location&lt;/em&gt; Flyout comes in handy: Of course, now there are 2 different folders to check and you can use this flyout to switch between the folders. Or use &lt;em&gt;All Locations&lt;/em&gt; to display everything to gain an overview. It also helps that this view flattens all subfolders, displaying a path, very helpful when looking for items in more crowded structures. In this mode, you can&amp;rsquo;t upload a file because the grid wouldn&amp;rsquo;t know where to put the file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/AllLocations.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;You can also use &lt;em&gt;Edit Location&lt;/em&gt; to change things about the location. In the screenshots, you already saw that I changed the display name from its default &amp;ldquo;Documents on Default Page 1&amp;rdquo; to &amp;ldquo;Folder for: 234234&amp;rdquo;. You can also change the foldername in Sharepoint here, but maybe unexpectedly that does not move the folder, instead, it creates a new one and leaves the already created content behind. It&amp;rsquo;s still useful though: When a foldername is changed in Sharepoint this does not translate to the location in Dynamics, but with this function, you can move the location to the new foldername as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/EditLocation.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And finally: &lt;em&gt;Open Location&lt;/em&gt;. This is the single most used button by me. It opens the folder in Sharepoint, giving full access to the Sharepoint features. I already documented that &lt;em&gt;Edit Properties&lt;/em&gt; does not let you edit custom metadata columns, but of course, in Sharepoint, we can!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Functionality/SharepointView.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Open Location takes you directly to the Sharepoint folder&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The OOTB functionality of the Sharepoint integration is great for the average users&amp;rsquo; interactions: Upload, Edit and Download documents. Organize in folders and find files in the overview via &lt;em&gt;All Locations&lt;/em&gt;. Whenever the views in Dynamics are not sufficient, you can always head over to Sharepoint with just 2 clicks.&lt;/p&gt;
&lt;p&gt;Everything is nicely connected to the structured data in the Dynamics record with the possibility to create more private secondary locations etc. So all in all in my humble opinion the OOTB view already provides a great experience working with documents. It might lack automation options and interaction with metadata, but we will discuss these topics in future articles in this series!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Standard) 1: Setup</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/standard/setup/</link>
        <pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/standard/setup/</guid>
        <description>&lt;p&gt;Well, ordering is broken now, but as I wanted to write more about Sharepoint integration with Dynamics CE I asked myself &amp;ldquo;Shouldn&amp;rsquo;t we talk about the standard integration first?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;After all, Microsoft thinks it&amp;rsquo;s sufficient for most customers so why shouldn&amp;rsquo;t you get happy with it? Also, I am always relying on the standard experience for the frontend when automation is required.&lt;/p&gt;
&lt;h2 id=&#34;setting-up-a-sharepoint-site&#34;&gt;Setting up a Sharepoint site&lt;/h2&gt;
&lt;p&gt;This is pretty straightforward, I only want to share two considerations here. First, use one site per environment, sharing the site between multiple environments just leads to confusion and compromises the separation in security and after all, Sharepoint is billed by capacity and not per site. Second, there can be only one &amp;ldquo;Default Site&amp;rdquo;. That&amp;rsquo;s the site where Dynamics will auto-create folders when a user visits the &lt;em&gt;Documents&lt;/em&gt; Tab of a record, so you should consider which site shall be your default site when you are planning to integrate with multiple sites.&lt;/p&gt;
&lt;p&gt;So first head to https://{tenant-domain}-admin.sharepoint.com/ where you replace {tenant-domain} with the domain of your Azure Active Directory tenant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; You can also find the Sharepoint Admin Center via the quick links on the left menu at &lt;a class=&#34;link&#34; href=&#34;https://admin.microsoft.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://admin.microsoft.com/&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Head to &lt;em&gt;Sites&lt;/em&gt; -&amp;gt; &lt;em&gt;Active Sites&lt;/em&gt; and select &lt;em&gt;+ Create&lt;/em&gt; at the top. We need a &lt;em&gt;Team Site&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Create.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then give the site a name that shows its purpose well, maybe reference it being used for Dynamics CE and set yourself as owner. Check the &lt;em&gt;Advanced Settings&lt;/em&gt; here as well, making sure settings like the timezone are correct.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Settings.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;On the last screen you can already add members to the site, they will be added to an Office 365 group that was created alongside the site.&lt;/p&gt;
&lt;h2 id=&#34;authorizing-dynamics&#34;&gt;Authorizing Dynamics&lt;/h2&gt;
&lt;p&gt;In the &lt;em&gt;Advanced Settings&lt;/em&gt; (accessed from the cog symbol on the top right) choose &lt;em&gt;Document Management&lt;/em&gt; and &lt;em&gt;Enable Server-Based SharePoint Integration&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; This menu point vanishes after being performed.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Step through the wizard, if you are on an online Sharepoint site like described earlier, you can leave the standard settings until it asks for the URL, here you enter the URL of the site you created earlier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/WizardServer.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Valid.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;connecting-dynamics&#34;&gt;Connecting Dynamics&lt;/h2&gt;
&lt;p&gt;Next, we need &lt;em&gt;Document Management Settings&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/DocSettings.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;On the first screen, we need to enter the URL again and select the entities. Please note that some entities are preselected for you! You probably want to use the &amp;ldquo;select/deselect all&amp;rdquo; check box and then select only the entities you want to enable. For me, this will be the account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/WizardSettings.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;On the next page, you can optionally enable a hierarchical folder structure which is interesting if you for example think all documents of an account should reside under this folder, for example, all documents on the related cases, opportunities, orders and so on. Of course, this might make it a little harder to find the documents when searching in the Sharepoint structure, but might make for a great view from the CRM side of things. I unselected this option here because it does not make sense for me only enabling account for the integration.&lt;/p&gt;
&lt;p&gt;And then the wizard will create document libraries in Sharepoint for each entity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Hierarchy.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Final.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;testing-it&#34;&gt;Testing it&lt;/h2&gt;
&lt;p&gt;We are ready to go! Select an account in Dynamics and under &lt;em&gt;Related&lt;/em&gt;, select &lt;em&gt;Documents&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Documents.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt; &lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/DocumentsTab.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used the &lt;em&gt;Upload&lt;/em&gt; button here already and for the next step, use &lt;em&gt;Open Location&lt;/em&gt; to ensure that the document seen in the grid is actually in Sharepoint.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Standard/Setup/Folder.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Setting up the standard Sharepoint Integration for Dynamics CE is pretty straightforward and can be done in less than 30 minutes if you have enough privileges to execute the wizards on Sharepoint and Dynamics side.&lt;/p&gt;
&lt;p&gt;First, we created a site on Sharepoint. Then we enabled the server-based integration from Dynamics to Sharepoint. After this we can connect the site to Dynamics, in this step we will also enable entities and optionally define a hierarchy. After that the integration is ready for the users, they can upload, preview or download documents, organize in subfolders and some more things.&lt;/p&gt;
&lt;p&gt;In my experience, this works very well for most of the direct user interaction scenarios, so your users are hopefully already quite happy with these features, giving them quick access to documents through their Dynamics interface, all well organized within the records created by them.&lt;/p&gt;
&lt;p&gt;Check out &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/sharepoint/standard/functionality/&#34; &gt;the next post&lt;/a&gt; for a deeper dive into the OOTB functionality of this integration.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sharepoint Integration (Extended) 1: AppRegistration</title>
        <link>https://www.marius-wodtke.de/post/sharepoint/custom/appregistration/</link>
        <pubDate>Sun, 25 Jun 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/sharepoint/custom/appregistration/</guid>
        <description>&lt;p&gt;As this blog focuses on everything around Dataverse and how to integrate it, it&amp;rsquo;s now time to talk about Sharepoint.&lt;/p&gt;
&lt;p&gt;Dynamics CE has a Sharepoint integration, but it lacks. It works well with everything a user does manually but fails as soon as the requirements want the system to do something for the user. &amp;ldquo;Create a default folder structure&amp;rdquo;, &amp;ldquo;We need folders for all the records in the table&amp;rdquo;, &amp;ldquo;Automatically upload a generated file&amp;rdquo; and so on.&lt;/p&gt;
&lt;p&gt;In this post, we will create an AppRegistration that will resemble a service user for talking to Sharepoint. This is done for integrating with Sharepoint via backend code like C#. You can also do this integration with Power Automate, but having done this for a customer who prefers Power Automate my humble opinion is that it&amp;rsquo;s not worth it. Determining the folder position alone is too complex and by the amount of actions executed, the Flows become quite slow.&lt;/p&gt;
&lt;h2 id=&#34;the-portal-way&#34;&gt;The Portal Way&lt;/h2&gt;
&lt;p&gt;So if you like to point and click, we can set up a service user via the Azure Portal. All you need is an active Subscription, the cost will be minimal.
First, we need a KeyVault, maybe you already have one, otherwise search for &lt;em&gt;KeyVault&lt;/em&gt; in the search box at the top, select the KeyVault and &lt;em&gt;+ Create&lt;/em&gt; to start the creation process. The standard SKU is enough and for the name you need to be globally unique. For my name that&amp;rsquo;s quite easy, I&amp;rsquo;ve added a -dev here just in case I will use the KeyVault for anything else in the future, in this case, it is important to create separate KeyVaults for different stages and topics because once you give some application access to the contents it has access to all the secrets of this KeyVault. The other settings can stay as default for this tutorial, you can skip the other tabs and directly create the KeyVault.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/CreateKeyVault.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The name of a Key Vault has to be globally unique.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Next head to the Certificates section on the left menu and Create a Certificate. But even though I am the owner of the subscription I cannot access the KeyVault:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/GenerateCertificate.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;First, you will need to authorize yourself, but after that, you can generate a new Certificate.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;But as I am the owner of the KeyVault Azure Resource I can simply add appropriate permissions for myself. Under &lt;em&gt;Access Control (IAM)&lt;/em&gt;, select &lt;em&gt;Add role assignment&lt;/em&gt;. For my purposes I chose the &lt;em&gt;Key Vault Administrator&lt;/em&gt; role (although &lt;em&gt;Key Vault Certificates Officer&lt;/em&gt; would have been sufficient for today) and my account of course.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/AddRole.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The Key Vault Administrator role grants access to all of the contents of a KeyVault.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So after a full reload of the page I can now add a Certificate. I like to name them as I want to name the App Registration. So here I am creating it for the Dataverse &amp;lt;-&amp;gt; Sharepoint Integration and the development instance. Because it&amp;rsquo;s not that easy to differentiate the environments with the auto-generated names of my demos, I also add the domain name of the instance here. You could also handle that via tags, or you don&amp;rsquo;t need it at all because you named the environments correctly in the first place. The subject will be the same as the name, just with a prepended &lt;em&gt;CN=&lt;/em&gt;. I do change the &lt;em&gt;Lifetime Action&lt;/em&gt; with these because the Certificate will need to be handled manually anyway to email me instead of auto-renew.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/NewCertificate.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After creating the certificate, select it and then its only version. From here you can download it as CER (the public part of it) and as PFX (the private part). We will need the CER for the AppRegistration and the PFX for the actual Application as kind of a password (although this technically works differently).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/Download.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now we will create the App Registration. You can use the search box again to search for &amp;ldquo;App registrations&amp;rdquo; and select them. Then &lt;em&gt;+ New Registration&lt;/em&gt; to create a new one and we will name it like we named the certificate. So for me DataverseCE-SharepointIntegration-dev-org&amp;hellip; Everything else stays as default.&lt;/p&gt;
&lt;p&gt;Next switch to &lt;em&gt;Certificates &amp;amp; Secrets&lt;/em&gt;, and Upload a Certificate, as shown in the picture below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/Upload.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;After this step, the application we will build in the next post will be able to &lt;strong&gt;authenticate&lt;/strong&gt; against our Active Directory. However, it is not &lt;strong&gt;authorized&lt;/strong&gt; for Sharepoint yet. This is done in the section &lt;em&gt;API permissions&lt;/em&gt;. Select &lt;em&gt;Add a permission&lt;/em&gt;, in the Tab &lt;em&gt;Microsoft APIs&lt;/em&gt; find Sharepoint. Here we are looking for &lt;em&gt;Application Permissions&lt;/em&gt; and then theoretically we would like to use &lt;em&gt;Sites.Selected&lt;/em&gt;, so that the Application can only interact with certain sites. But the problem is: There is no GUI for selecting the sites on the Portal or Sharepoint, this has to be done via Powershell Modules and that&amp;rsquo;s something we will explore further down. So in this case we will have to go with &lt;em&gt;Sites.ReadWrite.All&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/AddPermission.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The last thing now is to grant admin consent. As shown below this is a button that just needs to be pressed to grant the permissions. Well, at least if you have that kind of access. In a corporate environment, you might not be that privileged and might have to ask an administrator for assistance, but in this demo tenant of course I won&amp;rsquo;t need this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/sharepoint/Custom/AppRegistration/Grant.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it, the App Registration is now granted permissions and we can use it as an identity to integrate with Sharepoint!&lt;/p&gt;
&lt;h2 id=&#34;the-powershell-way&#34;&gt;The Powershell Way&lt;/h2&gt;
&lt;p&gt;But especially in the aforementioned corporate environment the &lt;em&gt;Sites.ReadWrite.All&lt;/em&gt; Permission is far too powerful. What you want here is the &lt;em&gt;Sites.Selected&lt;/em&gt; and then enable the App for individual sites.
This can be done with the Powershell command &lt;code&gt;Grant-PnPAzureADAppSitePermission&lt;/code&gt; from the &lt;code&gt;PnP.PowerShell&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;But since we are using Powershell here anyway, let&amp;rsquo;s do the whole thing with Powershell.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the module: &lt;code&gt;Install-Module -Name PnP.PowerShell -SkipPublisherCheck -Scope CurrentUser&lt;/code&gt; (-SkipPublisherCheck is technically not needed but helps with unattended scripts since you don&amp;rsquo;t need to confirm the download.)&lt;/li&gt;
&lt;li&gt;Connect to your Sharepoint Site, $sharepointUrl stores the full URL here, e.g. https://crm123456.sharepoint.com/sites/Contoso: &lt;code&gt;Connect-PnPOnline -Url &amp;quot;$sharepointUrl&amp;quot; -Interactive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Next, we are going to create the AppRegistration. $tenantDomain holds the domain name of the tenant, in this case crm123456.onmicrosoft.com and $appName is the name of the app, so we can use something like DataverseCE-SharepointIntegration-dev again: &lt;code&gt;$appRegistration = Register-PnPAzureADApp -ApplicationName &amp;quot;$appName&amp;quot; -Tenant &amp;quot;$tenantDomain&amp;quot; -Store CurrentUser -SharePointApplicationPermissions &amp;quot;Sites.Selected&amp;quot; -Interactive&lt;/code&gt;
Please note that this action also creates the Certificate. Both the PFX and CER are dropped to the folder where you execute the command. It also uploads the CER to the AppRegistration and sets the Permission - &lt;em&gt;Sites.Selected&lt;/em&gt;. So a lot of the steps we executed by hand earlier, making the Powershell command far less error-prone.
You might want to upload the PFX to a KeyVault anyway to store it safely.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;Grant-PnPAzureADAppSitePermission&lt;/code&gt; we need the ClientId, we will save it from the returned information of &lt;code&gt;Register-PnPAzureADApp&lt;/code&gt;: &lt;code&gt;$clientId = $appRegistration.&amp;quot;AzureAppId/ClientId&amp;quot;;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Next we will grant Read/Write permissions to our AppRegistration:&lt;code&gt;$permission = Grant-PnPAzureADAppSitePermission -Permissions &amp;quot;Write&amp;quot; -Site $sharepointUrl -AppId $clientId -DisplayName $appName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;As needed: We saved the ID of the Permission to be able to manipulate it, there are 4 levels of permission, &lt;em&gt;Read&lt;/em&gt;, &lt;em&gt;Write&lt;/em&gt;, &lt;em&gt;Manage&lt;/em&gt; and &lt;em&gt;FullControl&lt;/em&gt; but the latter two you cannot use with the &lt;code&gt;Grant-PnPAzureADAppSitePermission&lt;/code&gt;, you will have to use this command: &lt;code&gt;Set-PnPAzureADAppSitePermission -Permissions &amp;quot;FullControl&amp;quot; -PermissionId $permission.Id &lt;/code&gt;
As said this is optional because you might simply be fine with &lt;em&gt;Read&lt;/em&gt; or &lt;em&gt;Write&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To elaborate on point 6: &lt;em&gt;Manage&lt;/em&gt; will be needed to for example manipulate metadata columns, so not the content, but the schema around. So needing to add metadata columns to a list will require the &lt;em&gt;Manage&lt;/em&gt; privilege while just setting a value to an existing column only requires &lt;em&gt;Write&lt;/em&gt;. &lt;em&gt;FullControl&lt;/em&gt; is for things like circumventing the bin, or not creating a new history version with an update of a document. So it is more of a rare use case, but I have needed this for a tool migrating documents together with metadata since setting the metadata after the upload created a new version of the file, essentially doubling the occupied space.&lt;/p&gt;
&lt;p&gt;If you need to enable the app for a second Sharepoint later, you can look up the client id in the &lt;em&gt;Overview&lt;/em&gt; section of the AppRegistration in the Azure portal and then start at point 4 by setting the &lt;code&gt;$clientId = &amp;quot;6ba7b810-9dad-11d1-80b4-00c04fd430c8&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is the consolidated full script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ps1&#34; data-lang=&#34;ps1&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$sp = Read-Host -Prompt &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enter the Url of the Sharepoint Site you want to use the principle with, e.g. https://crm123456.sharepoint.com/sites/Contoso&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$appName = Read-Host -Prompt &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enter the Display Name of the Application in AAD, e.g. DataverseCE-SharepointIntegration-dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantDomain = Read-Host -Prompt &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enter the AAD Tenants Primary Domain, e.g. crm123456.onmicrosoft.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Install-Module -Name PnP.PowerShell -SkipPublisherCheck -Scope CurrentUser
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Connect-PnPOnline -Url &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$sharepointUrl&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -Interactive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$appRegistration = Register-PnPAzureADApp -ApplicationName &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$appName&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -Tenant &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$tenantDomain&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -Store CurrentUser -SharePointApplicationPermissions &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Sites.Selected&amp;#34;&lt;/span&gt; -Interactive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$clientId = $appRegistration.&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AzureAppId/ClientId&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$permission = Grant-PnPAzureADAppSitePermission -Permissions &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Write&amp;#34;&lt;/span&gt; -Site $sharepointUrl -AppId $clientId -DisplayName $appName
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Set-PnPAzureADAppSitePermission -Permissions &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FullControl&amp;#34;&lt;/span&gt; -PermissionId $permission.Id 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So while creating the Sharepoint AppRegistration via the Portal is a good exercise for understanding how the components work together, doing it with the Powershell is a lot more fit for purpose. It reduces human error, can grant more granular permissions and once the script is set up, it&amp;rsquo;s much faster. Therefore I will always lean towards the 10 lines of code to create a service user for Sharepoint.&lt;/p&gt;
&lt;p&gt;In the next post, we will use the service user to interact with our site.&lt;/p&gt;
&lt;h2 id=&#34;links&#34;&gt;Links&lt;/h2&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.microsoft.com/en-us/powershell/sharepoint/sharepoint-pnp/sharepoint-pnp-cmdlets&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://docs.microsoft.com/en-us/powershell/sharepoint/sharepoint-pnp/sharepoint-pnp-cmdlets&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;http://johnliu.net/blog/2019/2/flowninja-hack-78-modifying-modified-by-and-modified-time-with-microsoft-flow&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http://johnliu.net/blog/2019/2/flowninja-hack-78-modifying-modified-by-and-modified-time-with-microsoft-flow&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My First Shot at External Tables with SQL</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/externaltable/</link>
        <pubDate>Sat, 17 Jun 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/externaltable/</guid>
        <description>&lt;p&gt;So this week I&amp;rsquo;ve looked into the &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/customize/create-edit-virtual-entities?view=op-9-1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Virtual Entities&lt;/a&gt;&amp;rdquo;, or as they are now called &amp;ldquo;External Tables&amp;rdquo; again. The problem the first time around was that it worked with the OData connector if you did everything right, but any minor mistake in configuring the attributes immediately broke the whole interface and debug information was not present. And in mistake I mean something like the server defines a string attribute and you create that as an optional string field in Dynamics; it needs to be required because it is not nullable&amp;hellip;&lt;/p&gt;
&lt;p&gt;Nevertheless, there is a beautiful new interface for configuring this now and it should also map the fields automatically, theoretically eliminating the human error here.&lt;/p&gt;
&lt;h2 id=&#34;the-server&#34;&gt;The Server&lt;/h2&gt;
&lt;p&gt;The new interface has only &lt;em&gt;Sharepoint&lt;/em&gt; and &lt;em&gt;SQL Server&lt;/em&gt; on offer for now. And Sharepoint lists needing to be integrated frankly just has not happened to me yet, so I went with the SQL Server. It is interesting because it can handle big amounts of data better than Dynamics (e.g. mass inserts) and a lot of applications can already write an SQL Database. Furthermore, there are synchronization features to OnPremise Servers and you can even integrate External Tables to the SQL Server as well, making it kind of a gateway to other databases.&lt;/p&gt;
&lt;p&gt;In Azure, I created an &lt;em&gt;SQL Server&lt;/em&gt; and an &lt;em&gt;SQL Database&lt;/em&gt; on the server. For the pricing tier, I chose serverless, cranked everything to its bare minimum and set auto-pause to one hour to reduce the cost to an absolute minimum. I always have that server running for SQL experiments and with its low usage and only a couple MB of occupied storage, it cost me less than one Euro last month. This is of course no advice on pricing, you would have to calculate that &lt;a class=&#34;link&#34; href=&#34;https://azure.microsoft.com/de-de/pricing/details/azure-sql-database/single/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;, but it gives you at least an idea of how low the cost for a PoC can be.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/AzureConfig.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;With &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver16&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SSMS&lt;/a&gt; I created a new table, according to the requirements for being integrated as an external table.&lt;/p&gt;
&lt;h2 id=&#34;table-requirements&#34;&gt;Table Requirements&lt;/h2&gt;
&lt;p&gt;I did not find direct requirements for the SQL tables, only the somewhat vague &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/virtual-entities/get-started-ve#limitations-of-virtual-tables&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;general limitations for external tables&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/customize/virtual-entity-odata-provider-requirements?view=op-9-1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the docs for OData tables&lt;/a&gt;. But with the variety of field types those were not a problem, with SQL I could imagine that you can have something with the bigger integer types that would not work.&lt;/p&gt;
&lt;p&gt;But the real requirement is that you need a Guid column. Often other data sources do not have this, they use an integer key or something similar. You can use those as alternate keys but the primary key for Dynamics is still the Guid, you need it. Fortunately, you can let the SQL Server generate that Guid for you, here is an example: &lt;code&gt;ALTER TABLE [dbo].[Products] ADD  CONSTRAINT [DF_Products_Id]  DEFAULT (newid()) FOR [Id]&lt;/code&gt; where Id is defined as &lt;code&gt;[Id] [uniqueidentifier] NOT NULL&lt;/code&gt;. This can be integrated into any table and it will be fit for purpose.&lt;/p&gt;
&lt;p&gt;But consider this: What if this table is not the source of the data but it&amp;rsquo;s rather copied there from somewhere else? Maybe as a straight copy or as an enriched dataset. No matter, you could get into a situation where you want to empty the table and fill it again from the source, but that will regenerate the Guids! All existing relationships with Lookups to the external table and all N:N relationships to it will be broken. So make sure to generate the Guid in the &amp;ldquo;real source&amp;rdquo; already. The other option to prevent this is to make an extra table with the Guid and the alternate key and then join them in a view. If you drop and reload the real table this matching table will persist and thus the Guids persist. But this adds overhead to manage the second table, so it might also be an option to only use this as a measure for the drop: Generate this helper table before you need to drop the main data table, then reimport the data and overwrite the newly generated Guids with the saved Guids from the helper table, matching by the alternate key.&lt;/p&gt;
&lt;p&gt;For reference, this is my table for tests:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CREATE TABLE [dbo].[Products](
	[Name] [nvarchar](50) NULL,
	[EAN] [int] IDENTITY(1,1) NOT NULL,
	[Description] [varchar](50) NULL,
	[Id] [uniqueidentifier] NOT NULL,
	[LastModified] [datetime] NOT NULL,
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Products] ADD  CONSTRAINT [DF_Products_Id]  DEFAULT (newid()) FOR [Id]
GO
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;creating-the-external-table&#34;&gt;Creating The External Table&lt;/h2&gt;
&lt;p&gt;Some pictures say more than a thousand words, let them speak and we will discuss the problems I&amp;rsquo;ve encountered afterwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/NewButton.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The external tables are within the new table menu&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/NewScreen.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The connection options are currently limited, we will create a SQL Server connection&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/AuthenticationMechanisms.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Several authentication mechanisms are offered, SQL Server Authentication even has the option for an OnPremise Data Gateway&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/ADIntegrated.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;AD Integrated sounded particularly interesting because I assumed this would work with the user account requesting the data, but this uses a central service account as well&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/Tables.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/Mapping.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;In the mapping, you can only change names, a good decision considering the problems with defining the data types manually&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/Summary.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/View.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;All fields show well in the view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/Form.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;And in a form as well! Keep in mind: As with every custom entity, the form was initially empty, so some further customizing was required&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-learnings&#34;&gt;The Learnings&lt;/h2&gt;
&lt;p&gt;My experiment did not go that smoothly of course. The first issue was that I tried the &lt;em&gt;Service Principle&lt;/em&gt; authentication method. Creating the connection went pretty fine and the connection showed as connected, but no matter how often I tried and how many refreshes I did, it would never show up as an existing connection in the wizard.&lt;/p&gt;
&lt;p&gt;That was not too problematic for me in this case, since I had no security department in my neck. I&amp;rsquo;ve simply created a local login for the &lt;em&gt;SQL Server Authentication&lt;/em&gt;. This is the script to do it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-- In the master database
CREATE LOGIN MyUserName   
    WITH PASSWORD = &amp;#39;MyPassword123!&amp;#39;;  
GO  

CREATE USER MyUserName FOR LOGIN MyUserName;  
GO  

-- In the database that has the data you want to connect to
EXEC sp_addrolemember &amp;#39;db_owner&amp;#39;, MyUserName
GO
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that showed up as seen on the screenshots above.&lt;/p&gt;
&lt;p&gt;But there was another error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/Error.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Googling around I then found &lt;a class=&#34;link&#34; href=&#34;https://powerusers.microsoft.com/t5/Building-Power-Apps/Server-doesn-t-exist-for-external-table/td-p/2095631&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this thread&lt;/a&gt; and simply setting the &lt;em&gt;Manually Manage Connection Reference&lt;/em&gt; from the &lt;em&gt;Advanced Options&lt;/em&gt; in the first step of the wizard was enough to fix the issue (that&amp;rsquo;s step 2 of the solution from the thread).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/ManualConnRef.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/externaltable/ConnRefWizard.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;This little trick just needs to be known I guess, while the first issue might be more problematic in bigger corporations where the SQL Server Login might raise security concerns.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So in total, I think Microsoft made a good step forward in easing the integration of external data with the new experience of external tables. The whole feature is not quite there yet, it&amp;rsquo;s not as easy as using something like a Power Automate Dataverse Connector or creating a regular table but the new wizard is a leap forward and we can see that MS cares about this feature and is willing to push it forward.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My First Shot at Plugin Packages (Dependent Assembly Plugins)</title>
        <link>https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/</link>
        <pubDate>Sat, 10 Jun 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/my-first-shot/plugin-packages/</guid>
        <description>&lt;p&gt;I recently noticed in a project that needs certificate authentication that &lt;em&gt;Microsoft.IdentityModel.Clients.ActiveDirectory&lt;/em&gt; is deprecated.
But this NuGet was particularly practical as it was loaded to the Dynamics 365 Sandboxes, letting me use it from plugin code. In contrast the new &lt;em&gt;Microsoft.Identity.Client&lt;/em&gt; is not in CRM and therefore traditionally would need &lt;em&gt;ILMerge&lt;/em&gt; to be deployed.&lt;/p&gt;
&lt;p&gt;However, Microsoft has a new preview feature for Dynamics CRM called &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/dependent-assembly-plugins&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Dependent Assembly Plugins&lt;/a&gt;&amp;rdquo;. This allows you to upload a .nupkg file and since a .nupkg is just a .zip file in the end, this means we can include multiple .dlls including ones from other NuGets.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;We are going to need the latest version of the PAC CLI. A simple way to get it is via the &lt;a class=&#34;link&#34; href=&#34;https://marketplace.visualstudio.com/items?itemName=microsoft-IsvExpTools.powerplatform-vscode&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Power Platform Tools&lt;/a&gt; of VSCode since I assume most of you will have VSCode already installed.
For deploying the package the latest version of the Plugin Registration Tool is advisable, a simple solution is to include the &lt;a class=&#34;link&#34; href=&#34;https://www.nuget.org/packages/Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool&lt;/a&gt; NuGet to your Project, after building the project you will find the &lt;em&gt;PluginRegistration.exe&lt;/em&gt; in your package folder.
You might also want to install the &lt;a class=&#34;link&#34; href=&#34;https://apps.microsoft.com/store/detail/nuget-package-explorer/9WZDNCRDMDM3&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NuGet Package Explorer&lt;/a&gt; to inspect the contents of the packages quickly.&lt;/p&gt;
&lt;h2 id=&#34;initializing-a-project&#34;&gt;Initializing a project&lt;/h2&gt;
&lt;p&gt;So for my use case, I already have a plugin project generating an assembly that is already uploaded to my Dynamics CRM instance. This previously had no dependencies on assemblies that are not loaded within the Sandbox. To transform the project to a Plugin Package, the &lt;code&gt;pac plugin init&lt;/code&gt; was used. Since I want to transform an existing project I first made a new Folder (simply left the default name, it does not matter) and executed the command inside that folder with the name of the original plugin project. I want to keep the namespaces etc the same here but obviously I can&amp;rsquo;t run the command in the &amp;ldquo;correct&amp;rdquo; folder since the files are already there.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/plugin-packages/PluginInit.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Next, I only took the .csproj from this newly created project and copied it back to my existing project. Since I chose the Namespace to be identical this overwrites the existing project.&lt;/p&gt;
&lt;p&gt;Of course, if you starting fresh you can simply run &lt;code&gt;pac plugin init&lt;/code&gt; in the root folder and start implementing from there!&lt;/p&gt;
&lt;h2 id=&#34;fixing-up-the-project&#34;&gt;Fixing up the project&lt;/h2&gt;
&lt;p&gt;So before we include any new dependencies now, let&amp;rsquo;s try to build!
And of course that does not work anymore, who would have guessed? In my case, the older .csproj style of the existing project meant that some Assembly Attributes were duplicated, which was easy to fix. Also, all the Assembly references are not included in the new project, they can be copied over from the old .csproj file when running a &lt;code&gt;git diff&lt;/code&gt;, but I took the opportunity here to do some housekeeping and went through the build errors, only adding the references needed by the current code. Finally, with the newer project style, there was also a class included in the project which I intentionally did not include in the old one, with the new style I had to explicitly exclude it.&lt;/p&gt;
&lt;p&gt;After that, the code was building and already I found a .nupkg file in my bin/debug folder, cool!&lt;/p&gt;
&lt;h2 id=&#34;integrating-a-dependency&#34;&gt;Integrating a dependency&lt;/h2&gt;
&lt;p&gt;Next up, integrating a dependency. A no-brainer would be something like &lt;em&gt;Newtonsoft.Json&lt;/em&gt;, but for my take &lt;em&gt;Microsoft.Identity.Client&lt;/em&gt; was already set. This comes with another dependency on &lt;em&gt;Microsoft.IdentityModel.Abstractions&lt;/em&gt;. The amount of other dependencies is critical since the Sandboxes still do not allow unmanaged code, so any dependency using it can kill your dreams of a helper library.
So I uninstalled &lt;em&gt;Microsoft.IdentityModel.Clients.ActiveDirectory&lt;/em&gt; from my project, installed &lt;em&gt;Microsoft.Identity.Client&lt;/em&gt; and then built to work through the errors. In these situations, the &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Separation_of_concerns&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Separation of concerns&lt;/a&gt;&amp;rdquo; is a blessing since that meant that I only needed to adapt the CertificateCredentialClient built for getting an &lt;em&gt;access token&lt;/em&gt; from a certificate.&lt;/p&gt;
&lt;p&gt;Naturally, after a rebuild you want to know whether everything is in the .nupkg as expected. This is where the &lt;em&gt;NuGet Package Explorer&lt;/em&gt; is handy since you can just drag and drop the .nupkg to the application window and it will show you the contents.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/plugin-packages/PackageExplorer.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;That worked nicely, I can even see that &lt;em&gt;System.Web.Extensions&lt;/em&gt; was included which is technically not needed, so I could remove it from the package by setting the dependency to not &lt;em&gt;Copy Local&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; I&amp;rsquo;ve noticed here that the NuGet is not updated on regular build unless you increase the package version, however a rebuild always packs a new .nupkg.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;registering-the-package&#34;&gt;Registering the package&lt;/h2&gt;
&lt;p&gt;With our package ready from the development side now, open the &lt;em&gt;Plugin Registration Tool&lt;/em&gt; and connect to your instance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/plugin-packages/NewPackage.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;From there, select “Register” and “Register New Package”.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/plugin-packages/Dialog.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;In the dialog, select your .nupkg file and the solution. Then hit import.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/plugin-packages/ViewPackage.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Now you won’t find the package in the default view, you first need to select it in “View” and “Display by Package”.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/my-first-shot/plugin-packages/Structure.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-catch&#34;&gt;The catch&lt;/h2&gt;
&lt;p&gt;No, the last screenshot already shows the steps and images. However, the reality is that your old assembly now has a &amp;ldquo;copy&amp;rdquo; and will only contain the types, but nothing else. So I migrated all the steps from the old assembly over to the one included in the package and then unregistered the old assembly with the same name. This was feasible for this assembly, but for larger projects, this will be a bigger effort. It would be much easier if regular tools like SPKL could interpret packages and register the steps. So for larger assemblies, you want to wait for such a feature (or include it yourself) to then just run the full registration on the new package and then unregister the old assembly.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/developer/data-platform/dependent-assembly-plugins&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Dependent Assembly Plugins&lt;/a&gt;&amp;rdquo; is a great addition to Dataverse. It offers a supported way to practice modern development by relying on helper libraries and splitting your code into multiple libraries. The PAC CLI offers an easy way to initialize such a project but for existing implementations, you should keep in mind that this feature &lt;del&gt;is still in preview&lt;/del&gt; and that the community tooling has not yet caught up with it (it&amp;rsquo;s released now, so keep an eye out for updates). Therefore you might want to wait with it before developing production code with it and rather resort to experimenting with the new possibilities this feature will enable you to deliver in the future.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>eForms with C# 4: Publishing a notice</title>
        <link>https://www.marius-wodtke.de/post/eforms-csharp/publishing/</link>
        <pubDate>Sat, 03 Jun 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/eforms-csharp/publishing/</guid>
        <description>&lt;p&gt;Oh, you&amp;rsquo;re back! So this means you&amp;rsquo;ve progressed with the validation from &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/validating&#34; &gt;the last article&lt;/a&gt; so much, that you are ready to publish a first notice. Well, at least to the preview environment.&lt;/p&gt;
&lt;h2 id=&#34;generating-a-client&#34;&gt;Generating a client&lt;/h2&gt;
&lt;p&gt;So again, new API, same game, let&amp;rsquo;s use &lt;a class=&#34;link&#34; href=&#34;https://github.com/RicoSuter/NSwag&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NSwag Studio&lt;/a&gt; to generate a client. I do assume you have read &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/validating&#34; &gt;the last article&lt;/a&gt;, so you are already familiar with the options of NSwag. The settings are quite similar here, so you can start with a copy of your old config and just change the &lt;em&gt;Specification Url&lt;/em&gt;, &lt;em&gt;Namespace&lt;/em&gt;, &lt;em&gt;Class Name&lt;/em&gt; and &lt;em&gt;Output file path&lt;/em&gt;. To recall, the swagger is found &lt;a class=&#34;link&#34; href=&#34;https://enotices2.preview.ted.europa.eu/esenders/swagger-ui/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt; and the specification URL is &lt;a class=&#34;link&#34; href=&#34;https://enotices2.preview.ted.europa.eu/esenders/api/v2/notice/v3/api-docs/public&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://enotices2.preview.ted.europa.eu/esenders/api/v2/notice/v3/api-docs/public&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also work off the configuration of the sample app, of course, it is found &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/clients/PublicationApi.nswag&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But no matter how much we fiddle with the settings there is always a problem with the output. Again NSwag can&amp;rsquo;t interpret the description correctly, it renders the metadata property which is specified as part of the body as well as the parameter in the OpenApi specification twice&amp;hellip; Seems like those APIs don&amp;rsquo;t like to be generated, I wonder whether the specifications were created by hand or if it&amp;rsquo;s just NSwag simply being buggy here. In this instance, I have edited the resulting client by hand since only four function signatures need to be edited. But of course, this is a nuisance since it needs to be done every time we regenerate the client.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/publishing/ClientProblem.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;One of the metadata inputs needs to be removed.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So then there is also a difference to the Validation API in that the API Key is not a parameter but shall be passed as the Authorization header. Again weird, that it&amp;rsquo;s not consistent between the APIs, but it&amp;rsquo;s not a problem, we can simply set the header like this: &lt;code&gt;PublicationHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(&amp;quot;Bearer&amp;quot;, ApiKey);&lt;/code&gt;. And here the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/client/ClientFactory.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ClientFactory&lt;/a&gt; that did nothing much for the Validation API is very handy because it alleviates the program code from worrying about the Authorization header, it is already injected when the client is generated!&lt;/p&gt;
&lt;h2 id=&#34;calling-the-api&#34;&gt;Calling the API&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s take our notice XML and publish the notice. You probably want to double-check your url to contain a &lt;em&gt;.preview.&lt;/em&gt; now to not accidentally publish something to the real platform.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; publicationClient = factory.GetPublicationClient();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; metadata = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Metadata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    NoticeAuthorEmail = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sam@ple.app&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    NoticeAuthorLocale = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; notice = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FileParameter(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; MemoryStream(serializedNotice), &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{mappedNotice.ID.Value}.xml&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/xml&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; publicationResponse = &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; publicationClient.SubmitNoticeAsync(notice, metadata);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So with the generated client straight forward again, this time instead of a byte array a File Parameter is requested, but the transformation can be achieved quickly with a MemoryStream.
What you get back is a success indicator and a URL with the validation result. So even if you have previously used the Validation API, the Publication API will still check your notice and you can use the URL for the Validation Report endpoint to double-check the results. They will be also emailed to you.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/publishing/SuccessfulPublish.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The resulting output of the sample app for a successful publish&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; For my testing I have created an account with the EU in the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/introduction/&#34; &gt;introduction article&lt;/a&gt;. This one does receive 403 errors when trying to publish a notice. Even on the preview instance! For publishing notices you need to pass an authorization process with the EU first, so for the screenshot I had to resort to an API Key from the project.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s done! A notice published and may it be only to the preview environment! So let&amp;rsquo;s recap, in &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/introduction/&#34; &gt;the beginning&lt;/a&gt;, we started by finding the API specifications and created an account with the EU. Then, with the help of &lt;a class=&#34;link&#34; href=&#34;https://github.com/OP-TED/eForms-SDK&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the eForms SDK&lt;/a&gt;, we &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/generating/&#34; &gt;created our first notice XMLs&lt;/a&gt;. After that, we &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/validating/&#34; &gt;refined our output&lt;/a&gt; with the Validation API. Finally, in this article, we generated a client for the Publication API, fixed the errors from the auto-generation and uploaded our validated notice to the service.&lt;/p&gt;
&lt;p&gt;With this the series comes to an end, I hope the read was helpful for your journey with eForms and thanks for stopping by!&lt;/p&gt;
&lt;p&gt;You will find all the code of this series in &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the repository of the sample app&lt;/a&gt;, happy browsing there as well.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>eForms with C# 3: Validating a notice</title>
        <link>https://www.marius-wodtke.de/post/eforms-csharp/validating/</link>
        <pubDate>Sun, 28 May 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/eforms-csharp/validating/</guid>
        <description>&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eforms-csharp/generating/&#34; &gt;the last post&lt;/a&gt;, we&amp;rsquo;ve generated and serialized a notice. In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eforms-csharp/introduction/&#34; &gt;the introduction&lt;/a&gt;, we&amp;rsquo;ve also already discussed that there is an &lt;a class=&#34;link&#34; href=&#34;https://cvs.preview.ted.europa.eu/swagger-ui/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;API for formally validating notices&lt;/a&gt;. So let&amp;rsquo;s see what our mapping is missing!&lt;/p&gt;
&lt;h2 id=&#34;generating-a-client&#34;&gt;Generating a client&lt;/h2&gt;
&lt;p&gt;For interacting with an API that already has an open API definition (here provided by Swagger) I like to use &lt;a class=&#34;link&#34; href=&#34;https://github.com/RicoSuter/NSwag&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NSwag Studio&lt;/a&gt; to generate a client. In the studio, you enter the URL of the definition and then set the checkbox for &lt;em&gt;CSharp Client&lt;/em&gt; and then click &lt;em&gt;Generate Outputs&lt;/em&gt; to see the resulting Client class. From here the output usually needs some tuning. In the pictures below you can see what settings I have changed personally to fix the namespace, switch the serializer, combining all operations into one client and so on. When the output holds to the eyes I like to save the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/clients/ValidationApi.nswag&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;.nswag&lt;/a&gt; to the folder where I&amp;rsquo;d like to have the .cs in the end. This makes it easy to reload the file and tweak settings or simply reproduce the output for a newer version of the API. Finally, specify the file name as a relative path and finally, click &lt;em&gt;Generate Outputs&lt;/em&gt; to generate the output as .cs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/validating/UpperConfig.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The upper part of the configuration in NSwag Studio.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/validating/LowerConfig.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The lower part of the configuration in NSwag Studio, including the output file path.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So then I like to also build a small factory around it. That lets me manipulate the underlying HttpClient easily to add things like required headers. You can find the reference &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/client/ClientFactory.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;. For the sample app, I&amp;rsquo;ve inserted the baseUrl via app settings and let the user insert the API Key manually via the console as this is a secret that I don&amp;rsquo;t want to upload to the repository. If possible, consider &lt;em&gt;Azure Key Vault&lt;/em&gt; for storing secrets like this.&lt;/p&gt;
&lt;h2 id=&#34;calling-the-cvs&#34;&gt;Calling the CVS&lt;/h2&gt;
&lt;p&gt;Next up: Calling the Validation API - or Central Validation Service (CVS).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; factory = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ClientFactory(config, apiKey);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; client = factory.GetValidationClient();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; request = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; InputNoticeValidation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    EFormsSdkVersion = mappedNotice.CustomizationID.Value,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Language = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ValidationMode = InputNoticeValidationValidationMode.Static,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Notice = serializedNotice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; response = &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; client.V1NoticesValidationAsync(factory.ApiKey, request);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;interpreting-the-response&#34;&gt;Interpreting the response&lt;/h2&gt;
&lt;p&gt;With a pre-generated client, this is quite straightforward to make the API call. However, the response is an XML file and as with the upload, this is untyped. As I did not find a matching schema right away in &lt;a class=&#34;link&#34; href=&#34;https://github.com/OP-TED/eForms-SDK&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the SDK&lt;/a&gt; I used the &lt;em&gt;Paste XML As Classes&lt;/em&gt; feature of Visual Studio (Edit -&amp;gt; Paste Special) to generate something that is deserializable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt;  Depending on how much you have mapped at this stage you might get 400 responses from the service which will pop up as ApiExceptions. It is important to note that the definition of the API states that this is caused by the parameters like Language and Version, which is also printed to the Exception Message. But make sure to check the Result as well, here the real response from the service is noted, in the picture below you can see that the actual error is regarding the XML itsel! It is missing required properties.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/validating/400Error.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The actual response from the service is in the Result, not the Message&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Sadly there is another problem: The definition of the API which returns an application/xml content is not interpreted by NSwag as an XML File being returned because there is no schema given for this. I had to edit the return value &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/clients/ValidationClient.cs#LL193C34-L193C34&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FileResponse(status_, headers_, &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; response_.Content.ReadAsStringAsync())), client_, response_);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can read the response and with a simple XML deserialization against our &lt;em&gt;Paste XML As Classes&lt;/em&gt; class we can also check the result for validation errors (schematronoutputFailedassert) and output those in a structured way. The sample app uses extension methods here to provide a small footprint in the main logic.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; schematronoutput = response.DeserializeAsShematron();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (schematronoutput.HasErrors())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Console.WriteLine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Validation Errrors:&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Console.WriteLine(schematronoutput.BuildErrorString());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Console.WriteLine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No Validation Errors!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;fixing-up-the-mapping&#34;&gt;Fixing up the mapping&lt;/h2&gt;
&lt;p&gt;Now this part I have to leave up to you of course, but the response does provide guidance. The validation errors do have a description and where to find the element that failed validation. Often you will also find things like Regexes that need to be matched and so on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/validating/200Response.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The validation errors do provide a description as well as XPaths.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;So while I can&amp;rsquo;t tell you what your notices will need and exploring all subtypes would take quite some time the Validation results can be mapped easily to the elements being failed and allowing for mostly easy fixes. Unless of course you simply do not have the data being required like a NUTS3 code that might need to be passed but is not entered by your user nor can be hard coded.&lt;/p&gt;
&lt;p&gt;Just one thing I can offer as a little help (at least it helped me): The SDK contains &lt;a class=&#34;link&#34; href=&#34;https://github.com/OP-TED/eForms-SDK/tree/develop/examples&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;some examples here&lt;/a&gt;, maybe something matches your needs or by chance, there is a sample for your subtype?&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, we generated a client, called the validation API with our previously generated and serialized XML, fixed the response output and finally filtered and interpreted errors.&lt;/p&gt;
&lt;p&gt;At this stage, you probably have a lot to do because I don&amp;rsquo;t expect your initial mapping to hold up against the Central Validation Service (CVS). In the next post we will publish a notice (at least to the preview stage), but until then we should have our mapping sorted out!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>eForms with C# 2: Generating XML</title>
        <link>https://www.marius-wodtke.de/post/eforms-csharp/generating/</link>
        <pubDate>Sun, 21 May 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/eforms-csharp/generating/</guid>
        <description>&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eforms-csharp/introduction/&#34; &gt;the first post&lt;/a&gt; of this series, we already explored the swaggers of the eForms APIs. But when exploring the functions you probably quickly came to the point &amp;ldquo;ok, but what do I upload here now?&amp;rdquo;. While the swagger documents the parameters of the APIs it does not document the content of the files(/base64) to upload.&lt;/p&gt;
&lt;h2 id=&#34;classes-from-schema&#34;&gt;Classes from schema&lt;/h2&gt;
&lt;p&gt;But &lt;a class=&#34;link&#34; href=&#34;https://github.com/OP-TED/eForms-SDK&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;eForms SDK&lt;/a&gt; to the rescue: It contains the folder &lt;em&gt;schemas&lt;/em&gt; which, as the name suggests, contains the schema in the form of xsd files. For generating C# classes there is the xsd.exe which was installed for you with Visual Studio. First, copy the schemas into your project, then right-click the folder and select &lt;em&gt;Open in Terminal&lt;/em&gt; and then we can use something like &lt;code&gt;xsd /c xyz.xsd&lt;/code&gt; to generate the classes for this schema. If you now try this with a file of the schemas you will quickly notice that this does not work because the .xsd of eForms (/maindoc) reference xsds of UBL in the /common folder and xsd.exe cant resolve this. You can however pass multiple files to xsd.exe so we could simply pass all the .xsd in the schema subfolders, that gives us a rather long statement in the console which is hard to repeat should the schema change in the future. But luckily you can also define a parameters file that holds the options for us so we can simply execute &lt;code&gt;xsd /c /p:parameters.xml&lt;/code&gt; to generate the classes for a dozen xsds. The &lt;em&gt;parameters.xml&lt;/em&gt; looks something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;xsd&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://microsoft.com/dotnet/tools/xsd/&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;generateClasses&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;language=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;CS&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;namespace=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;myNameSpace.Schemas&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;schema&amp;gt;&lt;/span&gt;.\maindoc\UBL-ContractNotice-2.3.xsd&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/schema&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;schema&amp;gt;&lt;/span&gt;.\common\UBL-CommonBasicComponents-2.3.xsd&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/schema&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;schema&amp;gt;&lt;/span&gt;.\common\UBL-CommonExtensionComponents-2.3.xsd&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/schema&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;schema&amp;gt;&lt;/span&gt;.\common\UBL-ExtensionContentDataType-2.3.xsd&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/schema&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/generateClasses&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/xsd&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One missing feature of xsd for me: It will always name the resulting .cs like the last .xsd file, which is not very descriptive since the file will contain a multitude of classes, so I like to rename it.&lt;/p&gt;
&lt;h2 id=&#34;sample-repo&#34;&gt;Sample repo&lt;/h2&gt;
&lt;p&gt;Of course, this series of articles comes with &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;a repository&lt;/a&gt; to store the full code. For example, the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/schemas/parameters.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;parameters.xml&lt;/a&gt; of which there is only an extract in this article. You might also find further tricks like the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/schemas/GenerateModels.ps1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GenerateModels.ps1&lt;/a&gt; which also renames the resulting file accordingly.&lt;/p&gt;
&lt;h2 id=&#34;building-a-model&#34;&gt;Building a model&lt;/h2&gt;
&lt;p&gt;When you want to publish a notice there are three root types that we want to instantiate: &lt;code&gt;ContractNoticeType&lt;/code&gt; for announcing tenders, &lt;code&gt;ContractAwardNoticeType&lt;/code&gt; for announcing the winner and maybe &lt;code&gt;PriorInformationNoticeType&lt;/code&gt; for pre-announcing a tender. We can simply write &lt;code&gt;new ContractNoticeType()&lt;/code&gt; now and then start to fill in the data from what the user entered into our actual app. For the sample repository, this will be static data from a &lt;code&gt;NoticeModel&lt;/code&gt;. One advice here: Due to the XML structure with UBL, the mapping will be quite verbose. Therefore do not start to map like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; notice = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ContractNoticeType 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    UBLExtensions = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; UBLExtensionType[]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; UBLExtensionType
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ExtensionContent = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ExtensionContentType
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Item = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; EformsExtension 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    NoticeSubType = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; NoticeSubTypeType
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        SubTypeCode = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; SubTypeCodeType
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            listName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;notice-subtype&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Value = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{(int)model.NoticeType}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead, try to make functions that split up the mapping to reduce your Christmas tree. In the sample code above, the &lt;em&gt;notice-subtype&lt;/em&gt; specifies what type of tender it is, e.g. a concession, a regular contract or a military contract. Each subtype has its requirements of fields, so it does not make much sense here to go over the mandatory fields I explored in my project since they might be very different for you. But let me at least point you to the &lt;a class=&#34;link&#34; href=&#34;https://github.com/OP-TED/eForms-SDK/tree/develop/notice-types&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;em&gt;notice-types&lt;/em&gt;&lt;/a&gt; folder of the SDK which contains a JSON definition for the fields of each subtype form. These are quite long but might be helpful. A larger application could parse those JSONs to render forms. Or the descriptions can be used to determine what a &amp;ldquo;Field BT-702(b) missing&amp;rdquo; message from the Validation API means (&amp;ldquo;Notice Official Language&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Aside from that, we will at least need &lt;code&gt;UBLVersionID = new UBLVersionIDType { Value = &amp;quot;2.3&amp;quot; }&lt;/code&gt; and &lt;code&gt;CustomizationID = new CustomizationIDType { Value = &amp;quot;eforms-sdk-1.6&amp;quot; }&lt;/code&gt; (of course the versions might have changed by the time you read this).&lt;/p&gt;
&lt;h2 id=&#34;serializing&#34;&gt;Serializing&lt;/h2&gt;
&lt;p&gt;So now that we have a draft of what we think is a notice but we cannot pass our model to eForms directly. Instead, we need to pass it as serialized XML. For that C# offers the &lt;code&gt;XmlSerializer&lt;/code&gt; class. There is just one trick I want to show you here: By default, the element will contain the namespace of the element but we can make these namespaces known globally like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; ns = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; XmlSerializerNamespaces();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ns.Add(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cbc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; serx = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; XmlSerializer(eform.GetType());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;serx.Serialize(stringWriter, eform, ns);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With that everything of the CommonBasicComponents will just have the prefix cbc.
And that&amp;rsquo;s it, because of the namespace fuzz I encapsulated the serialization in &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample/blob/main/eForms-CSharp-Sample-App/services/SerializeNoticeService.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;its own service&lt;/a&gt; in the sample which returns a byte array that can be saved to a local file for viewing for now.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt;  You might run into an issue with the AppealStatusType which is a two dimensional array. This is not generated correctly in my version of xsd, resulting in weird errors like &amp;ldquo;Serializing JavaScript is not supported&amp;rdquo;, just adding &lt;code&gt;[]&lt;/code&gt; to the XmlArrayAttributes type was enough to fix this.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/eForms-csharp/generating/AppealStatusType.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Here I tried to specify a PostImage in a PreOperation step. The error from Dynamics is passed through.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;To get an XML we first transformed the schema files (.xsd) of the eForms SDK into a .cs file with all the classes necessary to build a notice, award or prior information. Then we mapped our application model to the models of eForms and finally serialized the model into an XML file.&lt;/p&gt;
&lt;p&gt;Next up will be to validate that model for which we will use the Validation API of eForms. But that&amp;rsquo;s the topic of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/validating&#34; &gt;the next post&lt;/a&gt;!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>eForms with C# 1: Introduction</title>
        <link>https://www.marius-wodtke.de/post/eforms-csharp/introduction/</link>
        <pubDate>Sat, 13 May 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/eforms-csharp/introduction/</guid>
        <description>&lt;p&gt;If you are doing a major tender within the EU, you also need to publish the tender on a European level.
The eForms 2 interface allows you to do this programmatically, an important feature for organizations that publish tenders regularly. For everyone else, a subject matter expert will just enter the data manually into &lt;a class=&#34;link&#34; href=&#34;https://enotices.ted.europa.eu/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the web interface&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;eforms-with-c&#34;&gt;eForms with C#&lt;/h2&gt;
&lt;p&gt;Despite the title, the best option to interact with eForms programmatically is probably with Java. Simply because the EU also publishes &lt;a class=&#34;link&#34; href=&#34;https://github.com/OP-TED/eForms-SDK&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;an SDK&lt;/a&gt; for eForms in Java.
But in fact, what we need to do to publish a notice is generate some XML and push that to one or two REST interfaces. Should be doable in C# as well. Spoiler alert: Of course it is! But when doing something by foot without an SDK there is always a struggle and this series of articles shall help you get started and concentrate more on the actual business problems rather than spending time on finding out how the interface works.&lt;/p&gt;
&lt;p&gt;So whatever your reason is to stick to C#, we will make it work. My reasons are simple: This blog is mainly about Dynamcis Integration, Dynamics only takes C# for its plugins and my task was to integrate Dynamics with eForms. Simple task, but the execution was not that easy due to the lack of information on the internet.&lt;/p&gt;
&lt;h2 id=&#34;important-references&#34;&gt;Important references&lt;/h2&gt;
&lt;p&gt;First and foremost there is the &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/home/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;TED Developer Docs&lt;/a&gt;. While we won&amp;rsquo;t use the &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/eforms/latest/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;eForms SDK&lt;/a&gt; and the information around the &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/api/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;TED API&lt;/a&gt; is quite sparse at the time of writing there is still the search! And this was quite useful for me, for example with the codelists.
I need to specify a language but DE (German) is not accepted. Searching for the codelist name &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/eforms/latest/reference/code-lists/eforms-language.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;eforms-language&lt;/a&gt; clarifies that I need to specify it as DEU.&lt;/p&gt;
&lt;p&gt;Then there are two APIs for publishing the notices. The first one is technically optional, the &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/api/endpoints/cvs-ted-europa-eu.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Validation API&lt;/a&gt; only makes sure the uploaded XML is &lt;em&gt;valid&lt;/em&gt;. Valid here means schematically correct. The schematrons of the API do check quite some plausibility but that does not mean it&amp;rsquo;s legally valid as well. I can specify the address of my organization to be &lt;em&gt;Test 123 in 9999 Testcity, Testcountry&lt;/em&gt;, it&amp;rsquo;s a complete address but obviously incorrect information.
The API has a Swagger documentation as well, find the &lt;a class=&#34;link&#34; href=&#34;https://cvs.preview.ted.europa.eu/swagger-ui/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;preview environment here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The second one is for actually publishing a notice. This is the &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/api/endpoints/enotices2-ted-europa-eu-esenders.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Publication API&lt;/a&gt;. It will also run validation on the notice and you can retrieve the results after the upload. However since it has already saved your notice, you cannot upload a corrected XML with the same ID again. This is why I prefer to check my notices first and then &amp;ldquo;fire and forget&amp;rdquo; the notice since I&amp;rsquo;m sure it will work.
The API has a Swagger documentation as well, find the &lt;a class=&#34;link&#34; href=&#34;https://enotices2.preview.ted.europa.eu/esenders/swagger-ui/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;preview environment here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are 2 more APIs present: The &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/api/endpoints/ted-europa-eu.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Search API&lt;/a&gt; and the &lt;a class=&#34;link&#34; href=&#34;https://docs.ted.europa.eu/api/endpoints/viewer-ted-europa-eu.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Visualisation API&lt;/a&gt;. These are for finding and &amp;ldquo;printing&amp;rdquo; existing notices. This might be interesting for you if you are not coming from the producer side of notices but from the consumer side and need your application to be able to search and display tenders.&lt;/p&gt;
&lt;h2 id=&#34;testing-an-eforms-swagger&#34;&gt;Testing an eForms Swagger&lt;/h2&gt;
&lt;p&gt;The fact that the APIs have Swaggers allows us to try out such APIs quickly. For example, if we would have generated an XML file we can push them by hand for testing even if we have not yet implemented clients for interfacing with the API.&lt;/p&gt;
&lt;p&gt;For that, we will need an account at the &lt;a class=&#34;link&#34; href=&#34;https://developer.preview.ted.europa.eu/home&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Developer Portal&lt;/a&gt;. Click &lt;em&gt;Login&lt;/em&gt; at the top right and create an account. That account can have any email, even private ones! But this email will be used to send you validation reports when publishing a notice. After confirming the email you will want to log in and select the menu point &lt;em&gt;Manage API Keys&lt;/em&gt;. Generate a new key and head to the swagger of the &lt;a class=&#34;link&#34; href=&#34;https://cvs.preview.ted.europa.eu/swagger-ui/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Validation API (CVS)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When we use the &lt;code&gt;/notices/validation&lt;/code&gt; endpoint with the default values and our API key the result message should be something like &lt;em&gt;&amp;lsquo;The notice is an invalid XML file; it cannot be parsed&amp;rsquo;&lt;/em&gt; because the default value is of course not valid, but that&amp;rsquo;s fine for now. One thing I&amp;rsquo;d like to mention with the version: The default values use &lt;em&gt;eforms-sdk-1.0&lt;/em&gt; but looking at my results, it seems like the validation was greatly changed between that and version 1.6, so check the &lt;code&gt;version-range&lt;/code&gt; endpoint for the newest available version when checking with a real XML.&lt;/p&gt;
&lt;p&gt;The &lt;a class=&#34;link&#34; href=&#34;https://enotices2.preview.ted.europa.eu/esenders/swagger-ui/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Publication API&lt;/a&gt; works a little differently in terms of authentication. There is no simple header in the swagger that you can fill, instead, you will need to use the &lt;em&gt;Authorize&lt;/em&gt; Button on the top right and enter your API key, &lt;em&gt;Authorize&lt;/em&gt; and &lt;em&gt;Close&lt;/em&gt;. Then you can use an endpoint like &lt;code&gt;/notice/notices/{business-id}/validation-report&lt;/code&gt; with the default values to try out the API.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So this article was an introduction. It detailed that using C# for eForms should be a conscious decision, providing links to the documentation and a short overview of the associated APIs. Last we generated an API key for the preview environment of eForms and used it to explore the APIs. In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eForms-csharp/generating/&#34; &gt;the next post&lt;/a&gt;, we will take a deeper dive into the actual programming, generating an XML for a notice.&lt;/p&gt;
&lt;h2 id=&#34;special-thanks&#34;&gt;Special Thanks&lt;/h2&gt;
&lt;p&gt;To &lt;a class=&#34;link&#34; href=&#34;https://www.linkedin.com/in/sevketcokay/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Şevket Çokay&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.linkedin.com/in/florian-unterberger-3bab39106/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Florian Unterberger&lt;/a&gt; for supporting me with subject matter expertise and contacts in implementing eForms integration!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Plugin Self Registration 4: When to use it</title>
        <link>https://www.marius-wodtke.de/post/plugin-self-registration/outlook/</link>
        <pubDate>Sun, 07 May 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/plugin-self-registration/outlook/</guid>
        <description>&lt;p&gt;After we&amp;rsquo;ve explored the use of self-registering logic in the context of the generic name combination in the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/praxis/&#34; &gt;previous post&lt;/a&gt;, there is certainly the question &amp;ldquo;Should we use this everywhere now?&amp;rdquo; Certainly not!&lt;/p&gt;
&lt;h2 id=&#34;when-to-use-it-and-when-not&#34;&gt;When to use it and when not&lt;/h2&gt;
&lt;p&gt;The added plugin(s) for registering the logic are extra effort that we will try to avoid whenever possible. Think about this requirement: &amp;ldquo;We want to validate IBANs on incoming orders and mark invalid ones for review&amp;rdquo;. If this was done in the frontend you can easily provide a PCF control (Power Component Framework) to perform the check for any entity. However, if the orders are inserted via the backend - e.g. via integration with a shop system - this would mean that someone has to open every single order to check the result. Therefore a backend solution like a plugin or Flow might be preferred. And then again, if this is regular project work we will just create a regular plugin. If it has to work on several entities, we will implement it generically and just register it manually to the required entities. When it should work on an additional entity we will simply add another registration. However this of course stops working as soon as we&amp;rsquo;d like to turn this into some sort of ISV solution, we cannot know the entities to register the plugin to in our development environment.&lt;/p&gt;
&lt;p&gt;But in this case, we have a different option to avoid self-registration: Making a custom entity for &amp;ldquo;IBAN&amp;rdquo;. The customer will then add a relationship (e.g. lookup) to their entity instead of a string field and our validation logic can run on creating and updating the IBAN entity. Since we know this entity and its fields in the development environment the registration is static and easily done.&lt;/p&gt;
&lt;p&gt;So we should always look out first for an option to make the registration static before we resort to the dynamic registration explored in this series. This will be most of the time! But now you are prepared for the other few times as well where you a situation that allows for static registration &lt;strong&gt;is not feasible&lt;/strong&gt;. The sample of name combinations we&amp;rsquo;ve explored came to this situation because it was highly tied to the primary field of custom entities, something we can&amp;rsquo;t bend to a fixed set of entities, unfortunately.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With this, the series comes to an end. From the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/concept/&#34; &gt;initial idea&lt;/a&gt;, we moved on to &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/solution/&#34; &gt;a solution&lt;/a&gt; to ease the process and make it more feasible to implement. Then we looked into a &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/praxis/&#34; &gt;practical use case&lt;/a&gt; where plugin self-registration allows us to implement something that is otherwise pretty unthinkable. Finally, I&amp;rsquo;ve used this post to calm down my own and your mind a bit to not run out and implement everything with this new possibility and rather use it sensibly.&lt;/p&gt;
&lt;p&gt;Cheers, and see you in the next series!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Plugin Self Registration 3: Putting it into practice</title>
        <link>https://www.marius-wodtke.de/post/plugin-self-registration/praxis/</link>
        <pubDate>Sun, 30 Apr 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/plugin-self-registration/praxis/</guid>
        <description>&lt;p&gt;If you are a developer you probably have done a naming plugin some time in your career.
It is a common requirement for custom entities where the users can&amp;rsquo;t easily pick a name. Instead, it should maybe be a combination of fields, just like the fullname of the contact entity.
This is particularly common with the primary field as you can not make it calculated and Business Rules can only be a solution in very simple cases.
But you may face requirements for combined string fields of course.
However the requirement, you have multiple solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Business Rules are quite limited.&lt;/li&gt;
&lt;li&gt;Real-Time Workflows might work, but are not the most efficient and their future is anybody&amp;rsquo;s guess.&lt;/li&gt;
&lt;li&gt;Flows (PowerAutomate) are very flexible, allowing to cover many requirements, however, they are not synchronous, leaving the field empty after saving. For a primary field, the user will then see &amp;ldquo;New CustomEntity&amp;rdquo; at the top of the form after he saved the new record.&lt;/li&gt;
&lt;li&gt;A plugin has none of these shortcomings and is therefore often the preferred way of the customer. However I can&amp;rsquo;t count how many times I was requested by customers and business analysts to create such a plugin with trivial requirements and every time a &amp;ldquo;coding monkey&amp;rdquo; was needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now it&amp;rsquo;s easy to come up with the idea &amp;ldquo;Let&amp;rsquo;s just create something generic that can create strings from a template and let the non-coders fill it&amp;rdquo;. However, the problem lies in the plugin registration of Dynamics which does not allow for a global registration for that logic.&lt;/p&gt;
&lt;p&gt;And this is where the solution from the &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/solution/&#34; &gt;previous post&lt;/a&gt; comes in. When someone fills out the template in the configuration entity, we can register on create and update of the targeted entity and then execute the generic logic.&lt;/p&gt;
&lt;h2 id=&#34;configuration-samples&#34;&gt;Configuration Samples&lt;/h2&gt;
&lt;p&gt;So the template orientates on format strings of C# - I&amp;rsquo;m a techy after all. We write something like &lt;em&gt;{0} - {1}&lt;/em&gt; and then specify that 0 shall be &lt;em&gt;Field A&lt;/em&gt; and 1 &lt;em&gt;Field B&lt;/em&gt;. Now specify the entity and which field the result should be written to and we are golden.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/praxis/configOptionset.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A sample configuration with a string field and an optionset.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first look at some configuration samples before we dive into the inner workings.
So here we combine a regular string field with an optionset. The optionset of course contains a number as the value which would be part of the string, but with a C#-like modifier we can print it as the English label. The result looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/praxis/resultOptionset.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The result from the previous configuration.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Similar things are also possible with Money fields which are transformed to their decimal value allowing you to specify the &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;standard C# formats&lt;/a&gt; as well as &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;custom ones&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lookups will by default print like this: &lt;em&gt;transactioncurrency(b5f9a0f3-7db9-ed11-9886-000d3ab6ddad)&lt;/em&gt;. But we can use the modifiers &lt;em&gt;Name&lt;/em&gt;, &lt;em&gt;Id&lt;/em&gt; and &lt;em&gt;LogicalName&lt;/em&gt; to print something different or specify the logicalname of a field at the entity behind the lookup. And this is even chainable with a semicolon! But let&amp;rsquo;s look at some simple samples in this post to not overcomplicate the matter. After all, this is designed to cover the most basic requirements of name generation and not fancy pants stuff where it&amp;rsquo;s absolutely ok to let someone code that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/praxis/configEntityReference.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A sample Configuration with a money field and an entity reference.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/praxis/resultEntityReference.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The result from the previous configuration.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;That in itself is quite cool, but for me the real magic here is that this is possible on any entity, registering this to the most common entities like account, contact, opportunity, incident, &amp;hellip; would be no problem, but it works anywhere.&lt;/p&gt;
&lt;h2 id=&#34;the-inner-workings&#34;&gt;The Inner Workings&lt;/h2&gt;
&lt;p&gt;If you have read &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/solution/&#34; &gt;the last post&lt;/a&gt; you already know where this is going, of course we are going to generate records of the mwo_pluginstepregistration entity. To be precise, two records, one for Create and one for Update.
And for that, we won&amp;rsquo;t need a whole lot of code, creation and update of the mwo_namecombination is handled with &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365NameCombiner/blob/main/mwo.D365NameCombiner.Plugins/Executables/RegistrationExecutable.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;a mere 160 lines of code&lt;/a&gt; (at the time of writing) and deletions were handled in a separate class with &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365NameCombiner/blob/main/mwo.D365NameCombiner.Plugins/Executables/DeleteRegistrationExecutable.cs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;35 lines of code&lt;/a&gt;. It even covers checks for whether the fields exist and translates a deactivation of the name combination to a deactivation of the step and vice versa.
Why is it so short? We simply create or update another entity if our configuration entity is created or updated. By the way: The triggers on mwo_namecombination of course use SPKL for registering the plugin because we absolutely know which entity and fields we want to trigger for.&lt;/p&gt;
&lt;p&gt;But back to the registration, when you create a similar mechanism the heart of the registration will be a mapping to the registration, so let&amp;rsquo;s add a (shortened) extract of this code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c#&#34; data-lang=&#34;c#&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; step = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; mwo_PluginStepRegistration
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_EventHandler = &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt;(NameCombiner).FullName,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_EventHandlerType = mwo_EventHandlerType.PluginType,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_Name = &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;{nameof(NameCombiner)}_{subject.mwo_Table}_{subject.mwo_Column}_{message}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_Managed = &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_SDKMessage = message,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_PrimaryEntity = subject.mwo_Table,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_PluginStepStage = mwo_PluginStage.PreOperation,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_Asynchronous = &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_StepConfiguration = subject.Id.ToString(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_FilteringAttributes = filters, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_ImageType = isUpdate ? mwo_ImageType.PreImage : mwo_ImageType.None,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mwo_ImageName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Default&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    StateCode = isActive ? mwo_PluginStepRegistrationState.Active : mwo_PluginStepRegistrationState.Inactive,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    StatusCode = isActive ? mwo_PluginStepRegistration_StatusCode.Active : mwo_PluginStepRegistration_StatusCode.Inactive,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So with the design of the configuration entity, we made sure that we know the target entity (mwo_table) and the filters (here a combination of the fields {0} to {9}) and the rest is either static or semi-dynamic, based on statecode and the fact that we are registering to Create and Update. In this case, I do pass the ID of the name combination as configuration for the step. This is not needed, the logic could just query for name combinations registered to the target entity, but this would mean if two combinations are registered to the same entity, both combinations would be executed twice since there would be two registrations as well.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;What&amp;rsquo;s left to do now? Of course to implement all those cool ideas for generic logic that came to your mind while reading through the series! There will be one more post out soon elaborating more on the topic of strategies in self-registration and how you can get around it sometimes.
Needless to say: While the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365NameCombiner/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365NameCombiner&lt;/a&gt; served as an example for your self-registration ideas here it is of course a working solution with actual use! Check out its Wiki for more samples.
In the next and &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/outlook/&#34; &gt;last post&lt;/a&gt; of this series, we will take a look at when to use this - and when not.&lt;/p&gt;
&lt;h2 id=&#34;special-thanks&#34;&gt;Special Thanks&lt;/h2&gt;
&lt;p&gt;To &lt;a class=&#34;link&#34; href=&#34;https://www.linkedin.com/in/lukas-leitzinger-6b5654160/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Lukas Leitzinger&lt;/a&gt; for contributing to this repository with ideas and code!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Plugin Self Registration 2: The Managed Solution</title>
        <link>https://www.marius-wodtke.de/post/plugin-self-registration/solution/</link>
        <pubDate>Mon, 24 Apr 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/plugin-self-registration/solution/</guid>
        <description>&lt;p&gt;In &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/concept/&#34; &gt;the last post&lt;/a&gt;, we dived into the idea that one plugin could register a second plugin to allow for more flexible registration of plugins. But we&amp;rsquo;ve ended with the caveat that a lot of entities are involved and no relationships are allowed.&lt;/p&gt;
&lt;p&gt;My proposed solution to ease the process of self-registration is to introduce an abstraction layer in form another entity. As this will be a regular custom entity, we can relate to it as much as we like.&lt;/p&gt;
&lt;h2 id=&#34;the-repository&#34;&gt;The Repository&lt;/h2&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365LivePluginRegistration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Here&lt;/a&gt; you will find the public repository of this solution. If you want to use it, simply &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365LivePluginRegistration/raw/master/LivePluginRegistration_3_0_0_1_managed.zip&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;download&lt;/a&gt; and install the managed solution to your instance.&lt;/p&gt;
&lt;h2 id=&#34;the-mapping&#34;&gt;The Mapping&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re familiar with registering Plugins in Dynamics 365 using the traditional Plugin Registration Tool (PRT), understanding the mapping to the new &lt;em&gt;Plugin Step Registration&lt;/em&gt; (mwo_pluginstepregistration) entity can be helpful. In this section, we will explore how the Plugin Step Registration entity maps to the entities in the PRT, and how you can leverage it for registering steps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/solution/general.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The event handler here specifies what plugin to execute.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Plugin Type&lt;/em&gt; in the Plugin Step Registration entity corresponds to Plugin (plugintype) in the PRT. However, it&amp;rsquo;s important to note that you cannot create a new Plugin with the Plugin Step Registration entity - the Plugin must already exist! What you are creating here is a Step (sdkmessageprocessingstep). In the images below, you can see further mappings, they are straightforward. Filtering attributes can be specified using commas as separators. It&amp;rsquo;s worth mentioning that Secure Configuration and User Context are currently not supported. Delete AsyncOperation will be set to &amp;ldquo;Yes&amp;rdquo; if you select Stage PostOperation &amp;amp; Async = &amp;ldquo;Yes&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/solution/step.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;In this tab, the regular options you know from the PRT are exposed.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/solution/image.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Of course, images can be configured as well. Just keep in mind that some configurations are illegal ;)&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The image configuration tab allows you to configure an Image (sdkmessageprocessingstepimage) for the Step. The mapping is straightforward, but it&amp;rsquo;s important to remember that, for example, you cannot register a PreImage to the Message &amp;ldquo;Create&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/plugin-self-registration/solution/error.png&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Here I tried to specify a PostImage in a PreOperation step. The error from Dynamics is passed through.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The Step is now managed by the Plugin Step Registration record. This means that if you update the record, the Step will be updated as well. Similarly, if you deactivate the record, the Step will be deactivated, and if you delete the record, the Step will be deleted too. However, you can untie this one-way connection between the record and the actual Step by setting the field &lt;em&gt;Managed&lt;/em&gt; to &amp;ldquo;No&amp;rdquo;. This allows you to delete the record while leaving the Step untouched. If you&amp;rsquo;re not sure which record manages which Step, you can check the Plugin Step ID against the ID of the Step in the PRT.&lt;/p&gt;
&lt;h2 id=&#34;service-endpoints&#34;&gt;Service Endpoints&lt;/h2&gt;
&lt;p&gt;Additionally, it&amp;rsquo;s also possible to have Service Endpoints as Event Handlers (formerly known as Plugin Type Name). You can reference them by name in the Plugin Event Handler entity and then select them in the Event Handler Lookup on the Plugin Step Registration. However, it&amp;rsquo;s important to note that by their nature, Service Endpoints can only handle asynchronous steps, and attempting to register a synchronous step will fail.&lt;/p&gt;
&lt;h2 id=&#34;the-physical-relation&#34;&gt;The physical relation&lt;/h2&gt;
&lt;p&gt;As mentioned before we cannot store the actual step (sdkmessageprocessingstep) in a relationship like a lookup. Therefore the Plugin Step Registration saves the guid of the step to a string field and parses it back from there. This is of course rather unelegant, but this is why we will use this abstraction for our regular code, we will never need to worry about this again and can simply relate to the Plugin Step Registration.&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;Now that we know roughly how it works, how do we put this into practice? This is exactly the topic of &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/praxis/&#34; &gt;the next post&lt;/a&gt;, where we will use this to register a &amp;ldquo;naming plugin&amp;rdquo; to any entity.&lt;/p&gt;
&lt;h2 id=&#34;links&#34;&gt;Links&lt;/h2&gt;
&lt;p&gt;Also, check out &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365LivePluginRegistration/wiki/Mapping-with-Plugin-Registration-Tool&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this Wiki article&lt;/a&gt; detailing how the entity maps to the plugin registration tool.&lt;/p&gt;
&lt;h2 id=&#34;special-thanks&#34;&gt;Special Thanks&lt;/h2&gt;
&lt;p&gt;To &lt;a class=&#34;link&#34; href=&#34;https://www.linkedin.com/in/lukas-leitzinger-6b5654160/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Lukas Leitzinger&lt;/a&gt; for contributing to this repository with ideas and code!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Legal Notice</title>
        <link>https://www.marius-wodtke.de/page/legal/</link>
        <pubDate>Wed, 19 Apr 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/legal/</guid>
        <description>&lt;h2 id=&#34;marius-wodtke&#34;&gt;Marius Wodtke&lt;/h2&gt;
&lt;p&gt;Contact: &lt;a class=&#34;link&#34; href=&#34;mailto:marius.wodtke@outlook.com&#34; &gt;marius.wodtke@outlook.com&lt;/a&gt;&lt;br&gt;
Address: Marktgasse 21-23/27, 1090 Vienna, Austria&lt;/p&gt;
&lt;h2 id=&#34;guarantees&#34;&gt;Guarantees&lt;/h2&gt;
&lt;p&gt;This is a private website, there is no associated company. Especially my employer cannot be held liable for the content published here.&lt;/p&gt;
&lt;p&gt;The author tries to serve the information on this website with his best conscience. However he cannot guarantee the correctness of the information and will not be held liable for damage from using the information provided here.&lt;/p&gt;
&lt;p&gt;Even though I check my content as well as linked content when preparing posts, I cannot guarantee external content. This is especially true if the external content changes after I publish a post. Liability for linked content is only provided by the operator of the external website.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Plugin Self Registration 1: The Concept</title>
        <link>https://www.marius-wodtke.de/post/plugin-self-registration/concept/</link>
        <pubDate>Wed, 19 Apr 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/plugin-self-registration/concept/</guid>
        <description>&lt;p&gt;Tools like &lt;a class=&#34;link&#34; href=&#34;https://github.com/scottdurow/SparkleXrm&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;spkl&lt;/a&gt; do a great job at registering plugins.
But did you ever have to turn down an idea due to the reasoning &amp;ldquo;I can&amp;rsquo;t determine on which entities this would need to run when I implement this&amp;rdquo;?
While most of the time you can determine quite well at the design time which entities and fields need to be part of the trigger, sometimes, especially in very generic or configured logic you run into the problem of design time registrations.&lt;/p&gt;
&lt;h2 id=&#34;unfavorable-requirements&#34;&gt;Unfavorable requirements&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at some requirements that are problematic:
&amp;ldquo;We need to export all changes to certain entities in a specific format, we want to configure the entities and fields on the fly because we can&amp;rsquo;t determine a complete list now.&amp;rdquo;&lt;br&gt;
&amp;ldquo;We have a specific pattern of how to build the primary fields for our custom entities, implement that pattern for us so our admin can apply it to the new entities.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;If you had a generic logic that can run on any entity of course you could just deliver the plugin to the production and then tell the key user/admin/whomever to use the plugin registration tool to add a step to your plugin type, but lets be honest here, the audience for that is quite small. Something like a configuration entity would be beneficial for those scenarios. Even a key user would know the entity and field name to create a record in CRM, but creating a plugin step is a whole different story.&lt;/p&gt;
&lt;p&gt;And if we think about highly configurable logic that you might want to release to multiple customers as an ISV solution: It becomes unfeasible to train them on the Plugin Registration Tool and at the same time it is impossible to preconfigure the solution in development because you don&amp;rsquo;t have the customers custom entities and fields to register to.&lt;/p&gt;
&lt;h2 id=&#34;the-idea&#34;&gt;The idea&lt;/h2&gt;
&lt;p&gt;So for this problem, let me pose an idea to you: What if we just create a regular entity for configuring your logic? Then we create a plugin reacting to changes in your configuration entity and that will handle the registration of a plugin step for the real logic based on what someone just did at the configuration entity.
Of course we will then always have two plugins, one with the actual logic and one that will make sure that the logic is triggered at the right moment.&lt;/p&gt;
&lt;p&gt;To come up with a more visual representation of this idea: &amp;ldquo;Let&amp;rsquo;s transform a configuration entity into a &lt;a class=&#34;link&#34; href=&#34;https://github.com/scottdurow/SparkleXrm/wiki/spkl#how&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;spkl-CrmPluginRegistration&lt;/a&gt; at runtime&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;That sounds quite simple actually, but the devil lies in the detail: For registering a plugin step you will need to interface the following entities:
sdkmessage
sdkmessagefilter
plugintype
(serviceendpoint)
sdkmessageprocessingstep
sdkmessageprocessingstepimage
(sdkmessageprocessingstepsecureconfig)&lt;/p&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s quite something, right? On top of that you cannot create relationships from or to these entities, so you cannot simply link your configuration and will have to somehow keep the connection to your sdkmessageprocessingstep as well.
But fear not and stay tuned for &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/solution/&#34; &gt;the next post&lt;/a&gt; in this series where I will show you some help to simplify the process to only managing one regular entity so that you can focus on translating your configuration entity into a plugin step!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Archives</title>
        <link>https://www.marius-wodtke.de/page/archives/</link>
        <pubDate>Sat, 15 Apr 2023 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/archives/</guid>
        <description></description>
        </item>
        <item>
        <title>Free Tools</title>
        <link>https://www.marius-wodtke.de/page/free-tools/</link>
        <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/free-tools/</guid>
        <description>&lt;h2 id=&#34;power-pages&#34;&gt;Power Pages&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;[Archived]&lt;/strong&gt; &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesClientAPI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;PowerPagesClientAPI&lt;/a&gt; tries to replicate the ClientAPI of Model Driven Apps in order to streamline development.&lt;br&gt;
Archived due to Microsoft publishing an official &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-pages/configure/client-api&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Power Pages Client API&lt;/a&gt;.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/tags/powerpagesclientapi/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesTemplate&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;PowerPagesTemplate&lt;/a&gt; is a starting point for anyone who needs to do more than just a little bit of JS for their Site. Supports Typescript and Bundling for more complex projects.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/power-pages/framework-1/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;[Archived]&lt;/strong&gt; &lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/PowerPagesActions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;PowerPagesActions&lt;/a&gt; is an Adapter to directly call CustomAPIs from Power Pages.&lt;br&gt;
Archived due to Microsoft enabling this in &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-pages/configure/server-logic-overview&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Power Pages Server Logic&lt;/a&gt;. The capabilities of this addition are shown &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/5ow4btEGiFI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://youtu.be/mSM0Tg7RXWI&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/tags/powerpagesactions/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;xrmtoolbox--power-platform-toolbox&#34;&gt;XrmToolbox &amp;amp; Power Platform Toolbox&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.xrmtoolbox.com/plugins/plugininfo/?id=7e01b65d-b6da-f011-92b8-7ced8d44e4bd&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Teams Integration Enabler&lt;/a&gt; helps you to integrate custom entities with Teams Channels.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/custom-table/&#34; &gt;Read more&lt;/a&gt;
It is also &lt;a class=&#34;link&#34; href=&#34;https://www.powerplatformtoolbox.com/tools/6715d04f-fed4-4e52-b1a2-72ae859459b8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;available for the Power Platform Toolbox&lt;/a&gt;.
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/teams/custom-table-pptb/&#34; &gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;dataverse-low-code&#34;&gt;Dataverse (Low-Code)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365RibbonHelpers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365RibbonHelpers&lt;/a&gt; is a set of command and rules to enhance the possibilities with the ribbon without coding. Or simply a reference for anyone who is into high-code.
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/tags/d365ribbonhelpers/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365NameCombiner&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365NameCombiner&lt;/a&gt; uses D365LivePluginRegistration to enable synchronously generated naming of records without the need of writing a single piece of code.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/praxis/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Contributed to &lt;a class=&#34;link&#34; href=&#34;https://github.com/allandecastro/VATNumberValidator&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;VATNumberValidator&lt;/a&gt; to fix it after the old CORS proxy changed its licensing scheme.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/other/vat-pcf/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;dataverse-high-code&#34;&gt;Dataverse (High-Code)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/TimelineCustomConnector&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;TimelineCustomConnector&lt;/a&gt; contains samples to help getting started with &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/custom-connectors-timeline-control&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this feature&lt;/a&gt;.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/timeline/custom/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365ServiceBusProcessors&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365ServiceBusProcessors&lt;/a&gt; contains samples to help getting started with consuming messages produced by the Service Bus Integration of Dataverse.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/servicebus/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/D365LivePluginRegistration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;D365LivePluginRegistration&lt;/a&gt; enables programmers to register plugins dynamically at runtime. &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/plugin-self-registration/&#34; &gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ot&#34;&gt;OT&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Kunter-Bunt/eForms-CSharp-Sample&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;eForms C# Sample&lt;/a&gt; I had to integrate with Tender Electronics Daily (TED) an EU service for publishing tenders in Dataverse Plugins. And although this service is mandatory for bigger tenders, the information around integration and all of it was for Java. I hate those gaps in the internet, so I felt like filling it with this sample code.&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/eforms-csharp/&#34; &gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Links</title>
        <link>https://www.marius-wodtke.de/page/links/</link>
        <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/links/</guid>
        <description></description>
        </item>
        <item>
        <title>Microsoft Docs Contributions</title>
        <link>https://www.marius-wodtke.de/page/docs-contributions/</link>
        <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/docs-contributions/</guid>
        <description>&lt;p&gt;Self-awareness page for collecting my requests/suggestions for the docs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2024-07-26 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/power-pages-docs/pull/146&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;4 Fixes on Calling Cloud Flows from Power Pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-06-29 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/power-pages-docs/pull/144&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Microsoft Authentication Provider - Client Secret correction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-06-25 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/microsoft/PowerApps-Samples/issues/550&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Custom Connector for Timeline documentation - Icon numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-06-08 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/powerquery-docs/pull/547&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Add limitation for dataflows with administration mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-03-04 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/dynamics-365-customer-engagement/pull/3244&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Enable enhanced experience for email attachments - Configuration wording&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-03-04 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/power-automate-docs/issues/1388&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;File Behavior in new designer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-01-17 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/dynamics-365-customer-engagement/pull/3218&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Add Refresh step to Post configuration for custom entity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-01-11 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/powerapps-docs/pull/5089&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Create and use dataflows in Power Apps - ALM link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2024-01-11 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/powerquery-docs/pull/502&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Dataflows Solution Awareness - Limitation additions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2023-08-10 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/powerapps-docs/issues/4626&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Add reference to timeline pin setting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2023-09-28 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/powerapps-docs/issues/3698&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;More information regarding the certificate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2022-09-22 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/MicrosoftDocs/powerapps-docs/issues/3675&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;addOnPostSave in wrong hierarchy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2022-09-14 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/SharePoint/sp-dev-docs/issues/8433&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Breaking Changes in PnP.Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2022-09-14 - &lt;a class=&#34;link&#34; href=&#34;https://github.com/microsoft/vstest/issues/3938&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;[17.3.0] You must install or update .NET to run this application.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Search</title>
        <link>https://www.marius-wodtke.de/page/search/</link>
        <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/page/search/</guid>
        <description></description>
        </item>
        
    </channel>
</rss>
