Unearthing “Solution Layering” feature of Dynamics 365. How are the good old concepts of patching and cloning related to it?

If you really ask me, solution layering is not a functionality in itself. Rather it is a visual representation to see the order in which a solution changed the component. Since you have visual representation of how a component got modified during the course of deployment of multiple managed solutions, this can be used for troubleshooting as well.

I am not going to explain what is Solution Layering. The feature is explained in Microsoft Docs and many other wonderful blog posts. What I am going to explain here is how you can use this feature to your benefit. Believe me the concept of solution layering has always been there starting since 2011, first with managed solutions and then with CRM 2016 it got even more intricate with the introduction of solution patching and cloning. Till now the concept was running behind scenes but there was no way to visualize it. Well with Solution Layering you get just that and let’s see how.

I take the simplest of example. I take the Contact entity and play with the “First Name” field. And believe me with this example I can show you the entire solution layering visual involving patching, cloning and everything. So let’s get started.

Step 1:

Create a solution – Let’s call it “Sol A”. Version 1.0.0.0. Add the contact entity with just “First Name” field. Change length of “First Name” field to 100. Export the solution as managed and import it in target environment.

image

Open the target environment default solution and go to Contact–> Fields –> First Name. Select the field and from the top menu, click on “Solution Layers”. Why did I go to default solution? This is because solution layers is only visible for unmanaged solutions. And when default is present already why bother to create an unmanaged one for this demo.

image

image

This is our first glance of Solution Layering screen. As you can see ‘Sol A’ is at the top order indicating that the length specified for ‘Account Number’ field in Sol A is what in force. Awesome!

Step 2:

Now we repeat Step 1. Create solution named “Sol B”. Version 1.0.0.0. Add the “Contact” entity with just “firstname” field. Change length of “First Name” field to 200. Export the solution as managed and import it in target environment.

Now if I see Solution layering for the field in target environment, as expected you can see Solution B is of the highest order now indicating that changes coming in Sol B are now in effect. Is this something new in here? The answer is a BIG NO. We all know that managed solutions are stacked in order of import (not always and will come to that in a moment) and hence changes coming with Sol B are in effect here. Could I visualize this previously? NO. Solution layering does just that.

image

Step 3:

Now repeating our step of creating solution one more time. We now clone Sol A. Set the version number to 2.0.0.0. And change the length of the field to 250. Remember we already have a lower version of the solution already installed.

image

Now we export it as Managed and then import it in the target environment. Now this time the behavior is bit different. This time we get a different option whether we want to maintain customization or overwrite customization. Let’s first choose “overwrite customization”.

image

When the solution is now imported in the target environment, check for solution layers, you can see something different. You can see in the order in which the unmanaged solution is the doing the final honours here. Which is again something we know would happen as we choose overwrite customizations. But could not visualize before Solution layering was not available.

image

Step 4:

Now what we do. We open Sol B and then change the first name field to 300. Export it and then import it in the target. This time we maintain our customizations.

image

If we check the Solution layering now in the target then nothing new in here. We can see Unmanaged layer at the top again taking all the accolades.

image

Step 5:

Now something very important. We create a patch solution of Sol A.  Add the contact entity and the First Name field. Change the field length to 450.

image

Export it as managed and import it in the target environment. If I open Solution layering now, you can see the managed patch right above it’s base Sol A. Is this something new here guys? No it’s not. After all we all know that managed patches sit on top of base solution. But when I see this through solution layering its a treat. The patch just sits above Sol A and below Sol B.

image

And before I end this as I said at the start. Solution layering just gives a wonderful visibility of how the managed solutions are stacked up. And how a components’ final behavior is affected by multiple solutions imported here. The logic of the inner workings now get a wonderful UI to visualize.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

Advertisements

“Team Members Privilege Inheritance”–What is this doing in Security role screen of Dynamics 365?

My team mate had the same question for me. He was in the middle of preparing demo with the customer and he opened up Sales person security role to make some modifications and guess what, he sees something new in there. Just like the one in below screenshot.

image

A new option with two values – 1) Default – Team Privileges only & 2) Direct User (Basic) access level and Team privileges.

To be honest, I didn’t get much from the options. So I fell back to my bible – Microsoft Docs. And this is what I found.

There has been few times when even after reading Microsoft Docs I could not get much and this was one of them. I was not able to understand much but what I could get is it has to do something with Azure AD groups. Before continuing further to understand this, you should first understand that Azure AD groups (Office 365 & Security) can now own records in Dynamics 365. I have detailed this in my blog here. I would suggest you understand that first before continuing.

We all know that a user in Dynamics 365 inherits security privileges from the teams it is associated with. First things first, this option we are talking about is only valid when a user inherits security privileges from Azure AD Office 365 team or Azure AD Security team in Dynamics 365. This option is not valid for owner teams.

Now that we have limited the set, what is this feature all about? For a long time Dynamics 365 had a peculiar behavior that there there must be a security role explicitly assigned to user even though the user may be part of multiple teams which have security roles and privileges to access Dynamics 365. If the user logs in, he would get an error like the screenshot below.

image

I have explained this in my blog some time back – Can a user work only with team roles in Dynamics 365. So to make it work you just needed to assign a security (dummy one) explicitly to the user. It does not matter whether the security role has any privilege or not.

This setting takes care of just this in case of Azure AD Office 365 and Security Team. If you set the option “Direct User (Basic) access level and Team privileges”

CRM would no longer throw an error even if no security roles are explicitly assigned to the user. The user can just work with team roles in Dynamics 365. If we keep the default option – “Default – Team Privileges only”, it falls back to the same behavior where an explicit role is required by the user to work with CRM.

Please note that for traditional owner teams this has no effect.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

Azure AD Groups can own your Dynamics 365 records. Learn it how?

Just another day in office and just another new stuff to learn. I bet this one will excite almost all the CRM consultants worldwide.

So what I am talking about in here. Well I am talking about “team ownerships”. I think it is being used in almost every project I have come across till date. Teams are integral part of CRM security and none can deny it. You may be thinking – “come on debajit. We play with this stuff day in and day out. what new are you going to tell us”. If you are aware of the latest feature where Azure AD groups can own Dynamics 365 records, well nothing new in here. But in case this is sounding new, believe me you are in for a fun ride.

Let’s start with a revelation – Now Azure AD groups can own records in Dynamics 365. All this time there used to be owner teams. Now both Azure AD office and security group can own your business records. Let’s see how.

So here I login to the https://portal.azure.com. Choose the active directory for my CRM instance and click on “Groups” menu item on the left bar.

image

Let’s create a new group.  Below are the group details. I have chosen Group Type as “Office 365”. However you can choose the other option “Security” as well.

I also add one member “CRM User 2” to the group.

image

So I have my new group created. Observe the Object ID field value I have highlighted in the below screenshot. If you remember, I told that this Office 365 security group can now own records in Dynamics 365. And Object ID value is going to be of prime importance to set that up.

image

I login to CRM –> Settings –> Security –> Teams –> New Team.

Details of the team specified below. I have chosen then team type as “AAD Office Group” and in the Azure AD Object Id field, I have put the Object ID copied from the previous step.

image

Save the team. CRM will validate the Object ID entered. Please note that you cannot add members to the team from Dynamics 365 screen. To add a member to the group, you would need to do that from the Azure portal or Office 365 admin screen.

image

So my team is saved. As you can see, there is no “+” icon in the Team Members section. It may take a moment to refresh the team members from Azure. If it doesn’t there is another trick up my sleeve which I am going to share with you soon. Let’s hold on to that for a moment.

So now I have team that represents a Office 365 group. The next step is to go ahead and assign security role to it. I click on Manage roles and assign “Sales Manager” role to it.

image

If you team members section is still not refreshed and does not show the user you added there in the portal, no worries we will get it sorted out.

So now you have a team which has members (we know it is there as we added in the portal) and has security role. So let’s make it own a record.

I go to Accounts and choose any account and then click on Assign. As you can see from the below screenshot, you can choose your Office 365 team you just created.

image

Works like a charm. Now office 365 admin can just manage groups from Office 365 admin screen or Azure portal, add or remove members from the team and all this will be automatically reflected in Dynamics 365 without the need of the Office 365 admin to login to Dynamics. Wonderful isn’t it?

image

Alas, till now “CRM user 2” whom I added to this group in Azure portal is not reflected. No worries, now it’s time to make it work.

I login to Dynamics 365 with CRM User 2. First time when I login, I see something like this. No Accounts, Contacts or other entities which I should have access by virtue of team roles.

image

But if I go back to my team now, I can user CRM User 2 added. Don’t know if it is a bug or behavior but it automatically gets refreshed in CRM when the user logs in.

image

Coming back to CRM user 2 screen, if I just refresh the page everything falls back to as desired.

image

I believe it’s a great feature and will be certainly helpful for many CRM consultants whenever they design security for their implementations.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

CreatedOn, CreatedBy, ModifiedOn and ModfiedBy– All you need to know to maintain history during Data Migration in Dynamics 365

These are the days of PowerApps. Canvas Apps, Model driven apps, flows and myriad of new features which are introduced lately or over the past couple of years. And yet in day-day jobs in our client projects we tend to argue over mundane things which in-fact I remember discussing during my initial days in CRM with 2011 version.

And one of them is data migration from one system to another maintaining their history. Whenever we talk about history, the bone of contention are the below four fields

  • CreatedOn
  • ModifiedOn
  • CreatedBy
  • ModifiedBy

We always suggest that any type of data operation should go through CRM SDK. Off course there are scenarios where you would perform DB to DB data migration but barring those scenarios it is always through SDK whether it is through batch JOB, plugin or SSIS Data adapter.

And here I was confidently updating my customer that we would be maintaining the history of all mentioned fields. However I get a stern objection saying while we can maintain the created on, we can’t do so for the remaining fields. And without wasting much time in argument I referred this wonderful blog written by the wonderful Aileen Gusni on the same topic way back in 2014 and it still holds good today! I was a big fan of her blogs during those early days in CRM and I still believe that she had one of the best content if not the best I have ever come across.

Coming back to the topic, the blog clearly mentions that you can maintain history for all of this. But still I was getting complaints that whatever code suggested there doesn’t work. And that is because we fail to understand the context where the code to set custom values for these four fields would work. If you have already understood the context, no need of reading further. Aileen’s blog is good enough!

But if you are still in doubt, here we dive deep.

Scenario 1: Create a console app and modify the fields using SDK.

var createEnt = new Entity("account");

createEnt["name"] = "Data Migration Create from Console";

createEnt["overriddencreatedon"] = new DateTime(2018, 03, 01);
createEnt["createdon"] = new DateTime(2017, 03, 01);
createEnt["modifiedon"] = new DateTime(2018, 03, 01);

// below is the GUID of one crm user named – CRM User 1
createEnt["modifiedby"] = new EntityReference("systemuser", Guid.Parse("67312B4B-9265-E911-A9BA-000D3A33BCD9"));
createEnt["createdby"] = new EntityReference("systemuser", Guid.Parse("67312B4B-9265-E911-A9BA-000D3A33BCD9"));

service.Create(createEnt);

As of the day I am writing this blog, it is 12th of May, 2019.

Below is the snapshot of record I just created.

Let’s concentrate on the first field – “Created On”. As you can see CreatedOn is field is set to the custom value I set. But wait, it is set to 3rd of March, 2018. If you observe the above code sample, I have set two fields for “Created On” field.

createEnt["overriddencreatedon"] = new DateTime(2018, 03, 01);
createEnt["createdon"] = new DateTime(2017, 03, 01);

And it is the value of “overriddencreatedon” field which is taking effect here. So remember – “If you are trying to maintain the historical createdon value during data migration using CRM SDK from external application, it is the value of “overriddencreatedon” field which is important.

The other fields “ModifiedBy”, “ModifiedOn” and “CreatedBy” were set based on runtime values and whatever set in the code had no effect. So if you are trying to maintain historical data from these fields during data migration using SDK methods from external application, you would not be able to do so.

image

Scenario 2: Set values inside a plugin on pre-create.

Here I do the same code stuff but this time I do it in the pre-create of Account. Here is the code sample.

public void Execute(IServiceProvider serviceProvider)
         {
             var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
             var target = (Entity)context.InputParameters["Target"];

            target["overriddencreatedon"] = new DateTime(2017, 03, 02);
             target["createdon"] = new DateTime(2017, 03, 01);
             target["modifiedon"] = new DateTime(2018, 03, 01);

   // Guid of CRM User 1
     target["modifiedby"] = new EntityReference("systemuser", Guid.Parse("67312B4B-9265-E911-A9BA-000D3A33BCD9"));
     target["createdby"] = new EntityReference("systemuser", Guid.Parse("67312B4B-9265-E911-A9BA-000D3A33BCD9"));
       

}

This time I create the record using UI. Below is the snapshot of the record after I create it.

image

Whoooo! All my custom values are taking effect here. And notice here, overriddencreatedon this time has no effect. It is the createdon value which is reflecting now. So all you need to do to maintain history for these four fields is set the value in the pre-create pipeline. Whether you are importing, creating from console or using SSIS data adapters, the plugin would fire and this would take effect.

Scenario 3: Set values inside a plugin on pre-update.

I am not going deep here. But this would perform the same like pre-create. I am not sure which scenario you would like to use pre-update to update these historical fields. But the just from the holistic point of view this would work exactly like pre-create.

Scenario 4: Using update Method of SDK.

If you are setting these fields using service.Update method of SDK, this would not take any effect. Even the created on won’t work this time unlike our first scenario no matter if you set the createdon or overriddencreatedon field.

Hope this helps next time you do data migration.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

Getting FormContext in Power Apps Custom Component framework–Gotchas!

Before I proceed further some disclaimers here. If you are thinking from the title that I have been able to resolve the problem completely, unfortunately it’s not. My approach works for certain scenarios and not for all scenarios and I will explain where it works and where it doesn’t. And this is of the time I am writing this blog. Very soon Microsoft may release something much simpler.

Ever since I wrote the article on in-depth walkthrough of Power Apps custom control, I have been flooded with questions and one of them was – “How do I get the formContext inside my custom control code so that I can interact with other attributes on the form?”. And in the series of blogs, I have used Xrm.Page construct to access some attributes on the form and as we all know, it has been deprecated. Honestly when I used Xrm.Page I used it for brevity as I  didn’t find much time to explore all the variables that the custom framework has to offer and I thought it would be easy one.

But when I went back and started searching I could not find anything the context provides to get the formContext.

I would be happy to know here that I have missed and there is a simple way to do this, I would take this blog post off and would be relieved as personally I am stuck because of this.

But if you are in the same boat as me, you can keep reading.

So here I was baffled. After-all I took it for granted and then it was not there or may be I could not find it. Searched the heck out of Google and could not find it as well.

So falls back to find it myself. I started digging and after hours of effort came up with a way to do this. May not be ingenious or best way but works for certain scenarios (doesn’t work for all. that’s the sad part.)

The first thing that struck me is that I need to keep the formContext somewhere globally so that I can see it in my control code as well. And the only way seemed logical is to do that in the onload form event.

So one part is sorted out. But how about accessing global variables in different context. After all the custom component code and the form on-load script works in different frames.

Just to illustrate, the form scripts execute in one of the frames below “top” where custom component code executes.

Form Script frame                                                           Custom component code frame

image                image

So if I can maintain a global variable holding the form context in the “top” frame, it would be available in custom component code.

I took Account Entity as an example and registered a form load event handler. Below is the script code.

if (typeof (Demo) == "undefined") {
    Demo = { __namespace: true };
}

Demo.Account = {
    _parent: parent,
    formContext: null,
    OnLoad: function (e) {
        debugger;
        this.formContext = e.getFormContext();
        this._parent.pageContext = this.formContext;
    }
};

Demo.Account.OnLoad is called on Account form load. When the function is called, it sets a variable called “pageContext” in the parent scope with the value of “formContext”. So far so good!

Here is my custom component embedded on the same account form.

image

I have registered an event handler for on click of submit button. What will I do now is try to access the formContext which is now stored a variable called “pageContext”.

When I click the submit button, the click event fires as expected.

image

If I go to console now and evaluate “pageContext” variable, it is nothing but “formContext”.

image

image

Whoooo! So everything just works fine. You can keep on using the this variable now in your control. Wasn’t it simple? Yes it was. Is it a complete solution. The answer is “NO”.

Before I go into the problem, lets go back to our basics here. If you are from the world of ASP.NET web programming and is aware of page lifecycle, you would be aware that page_init method will call control_init method of all the controls on page and then page_load will fire post page_init. The page_load method would only complete when all the controls on the page has been initialized and load completed.

If we bring this knowledge to our custom component, the init method custom component would fire before my form on-load script fires. So when the user opens up a record, the page load begins and the init method of your control fires. “Please note that by the time the init method of your control fires, the form load of the page has not been complete yet and hence the form load script won’t fire by that time.”

So what this mean? If you are trying to access the “pageContext” variable that we have used earlier you won’t find it in there. Even worse is if you are opening are moving from one record to another and try to access the pageContext inside your init method, you may end up getting the pageContext value from previous record.

So what are the other alternatives?

  • There might be simpler ways coming up in next releases or I have missed a simple way or something is already there which didn’t catch  my eye (believe me I would be happy is someone points that out).
  • Keep using Xrm.Page until MS releases a simpler like we still do for web-resources embedded in CRM form.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

{Dynamics 365 for Sales AI} How are relationship analytics calculated? Let Microsoft Docs tell you

Recently I have been working on setting up various features of Sales AI for a customer and I have to tell you that there is so much to it than meets the eye. And one of them is the Relationship Analytics. If you go to Account/ Contact/ Lead/ Opportunity form then you would see some Relationship Analytics showing up. Below is the screenshot for an opportunity.

image

But the question is – “How is Microsoft showing up all these values? After all they must be based on some logic right?”.

Then answer is – “ Yes they are.” But believe me, if you are not aware of the logic behind it, it may be very difficult to understand. And frequently when we are distress we come home to find salvation. And so you should come back to Microsoft Docs to understand what the complexity is all about.

When I asked my colleague to refer to docs, honestly they missed out this section in the entire documentation. Hence in this article I am sharing the exact link in Docs which explain this.

And here it goes.

This link directly takes you to relationship analytics calculation logic which my otherwise be difficult to find.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

What does “context” object hold in Power Apps custom component framework. Organization info, user info, web API methods and more!

After my detailed post on Power Apps Custom component framework, I have getting loads of queries and among them the common ones are

  • How do we get organization details within my Power Apps custom control
  • How to get context user information
  • How to get record information on which the control  is embedded
  • How to query Web API from my control code.

The answer to all of these lies in the context object that gets passed to the init method of your control. Just like with Plugin context, the control context contains all information about the current environment in which it is executing.

So let’s explore what the context has in store for us. And what’s better way than the wonderful developer tools of Chrome browser. Below is a screenshot of the property bag that context contains.

image

The important ones I just highlighted. So let’s go a bit granular on the highlighted one.

1. page:

context.page can help you with useful values like the id of the current app which is loaded, the guid of the record and the logical name of the record on which the control is showing up.

And also our very own lovable method – getClientUrl().

image

2. orgSettings

I think the name justifies all. This gives us all the values related to the CRM organization. And this is the same one you get when you query for Organization Settings using Xrm.Utility.getGlobalContext()

image

3. userSettings

This will give you information about the logged in user security roles, language code, user id etc.

image

4. webAPI

Well this can be very important. Specially if you want to query CRM web api from your control. Here you get all the methods to communicate with CRM Web API.

image

Hope that gives you all the information to get started with your first custom component.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)