May 19, 2021

Writing plugins and If conditions

This may be a silly post but it's something bothered me enough to write it down, so I just start with a disclaimer:

if you are using a tool or some code to assist you writing a plugin go for it, this post is not about this.

Few weeks ago I watched a YouTube video about programming, it described the advantages to don't write else conditions, I don't recall the exact remarks but was something like this, so instead of writing:

if (condition == true) {
} else {
}

the author suggested to write something like

if (condition == true) {
}
if (condition == false) {
}

I don't agree by principle, some comments suggested that a switch can also be used, in the example the author probably put some else if statements and one thing I don't like is definitely else if.

Today I wrote a plugin, as usual I copied the wireframe from one that I previously created and I implemented the logic. It was working but the final part of my code was just a long list of closing brackets of several if conditions I had in the code. So I remember the YouTube video I watched and I told myself: Can I rewrite this plugin to avoid a concatenation of if conditions?

Usually my wireframe looks like:

if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) {
   Entity entity = context.InputParameters["Target"];
   if (entity.LogicalName == "account) {
      if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"] is Entity) {
          Entity postImage = (Entity)context.PostEntityImages["PostImage"];
          // rest of the code
      }
   }   
}          
When you start with this kind of style (copied from official documentation or tutorials) it's easy to follow the same structure, you go for the true path, and in the last if condition the main code is executed or else it fails, the majority of the previous if we don't care, because the plugin logic should probably be skipped.
Inside a plugin I usually forgot that I can use return if I want to close the plugin, it doesn't go to an error, just the main scenario should not be executed.

So I rewrote the plugin reversing the conditions, so my code looked like:
if (!context.InputParameters.Contains("Target") || !(context.InputParameters["Target"] is Entity)) { return; }
Entity entity = context.InputParameters["Target"];
if (entity.LogicalName != "account) { return; }
if (!context.PostEntityImages.Contains("PostImage") || !(context.PostEntityImages["PostImage"] is Entity)) { return; }
Entity postImage = (Entity)context.PostEntityImages["PostImage"];
// rest of the code     
One thing I noticed reversing the conditions is that I use often !string.IsNullOrWhiteSpace to check if a value is not empty and continue, inside this plugin I used string.IsNullOrWhiteSpace with a return (meaning I exit if the value is empty), made me curious to know which syntax the author(s) of this method may like.

Just a note, in the code I wrote above I retrieve the Image by name, my friend Daryl LaBar taught me that you can just check the existence and access the first item of the collection, because normally you register a single Image against your step.

This post is not a best practice one or similar, just writing down the thoughts I had in mind today.

May 8, 2021

Microsoft.PowerPlatform.Dataverse.Client and ClientSecret AuthType

.NET Core 3.1 is what I consider a good starting point if you plan to create a new project based on .NET Core, it's LTS (until end of 2022) and if you developed only with .NET Framework before, I usually suggest this version.

I know .NET 5.0 has been released for some months now but for what I am describing here it's not important, if you wish you can use this version.

If you are a developer and in the last years you worked with Dynamics CRM/CDS/Dataverse you probably know this NuGet package:
Microsoft.CrmSdk.XrmTooling.CoreAssembly

It's the package we usually install when we need to connect to Dataverse, it works but requires .NET Framework 4.6.2 or onward. It works also with ClientSecret AuthType, if you have this kind of connection string

AuthType='ClientSecret'; ServiceUri='https://mywonderfulorg.crm.dynamics.com';
ClientId='00000000-0000-0000-0000-000000000000'; ClientSecret='MyWonderfulClientSecret';

or if you are using Username & Password you may have something like this

AuthType='Office365'; Url='https://mywonderfulorg.crm.dynamics.com';
Username='username@mywonderfulorg.onmicrosoft.com'; Password='MyWonderfulPassword';

In your code you can connect with few lines:

CrmServiceClient service = new CrmServiceClient(connectionString);
WhoAmIResponse whoAmIResponse = (WhoAmIResponse)service.Execute(new WhoAmIRequest());
Console.WriteLine($"Connected with UserId: {whoAmIResponse.UserId}");

CrmServiceClient comes from the namespace Microsoft.Xrm.Tooling.Connector.

What if we want to use .NET Core? The NuGet package to use is currently in public preview:
Microsoft.PowerPlatform.Dataverse.Client

You can use this package also with .NET Framework and .NET 5.0 but the main reason is the compatibility with what we call .NET Core (and if you still want to say something like "but with .NET 5.0 we can bla bla bla" fine for me, also with .NET Core is possible to use .NET Framework packages but we would make the executable only compatible with Windows, I never suggested that kind of path but it's a possibility).

This package is not compatible with the Username & Password authentication, as stated inside the description: Note: that only OAuth, Certificate, ClientSecret Authentication types are supported at this time.
Meaning it's also a good time to move away from old forms of authentication.

Assuming you have a connection string with the ClientSecret AuthType how would our code changes? Just a type:

ServiceClient service = new ServiceClient(connectionString);
WhoAmIResponse whoAmIResponse = (WhoAmIResponse)service.Execute(new WhoAmIRequest());
Console.WriteLine($"Connected with UserId: {whoAmIResponse.UserId}");

ServiceClient comes from the namespace Microsoft.PowerPlatform.Dataverse.Client, that is also the name of the package, so make sure you are using it.

Hopefully next time I will remember that is called ServiceClient and not CrmServiceClient, DataverseClient or PowerPlatformClient, types I tried before checking what was inside the package.