Working with Sitecore Commerce 9 Update-2 Entity Composer in Code
Prior to the release of Sitecore Commerce version 9.0.2 if you wanted to add custom data to a commerce entity such as a customer, sell-able item, pricing, etc. you had to do so with custom code extending the commerce engine. Sitecore Commerce 9.0.2 introduces a new feature known as the Entity Composer as highlighted in this very helpful article here Sitecore Experience Commerce 9 Entity Composer. I highly recommend reading this Sitecore article before continuing.
You may notice that although this article does a great job of explaining how to use the Entity Composer from the Business Tools Control Panel, there is no mention of how you would use this new functionality in code. This will be my focus moving forward and it is assumed that you have familiarized yourself with how to use the entity composer from the Business Tools Control Panel.
For this example, I have already added a child view to the customer entity, which I then converted to a template and linked it By Entity to the customer entity.
As you can see here we now have a new custom section on the customer entity with one property of type string that is named Secondary Email. This is all fine and well, but the question arises What if we want to edit this value from code and not from the Control Panel? With a little code and request sleuthing the answer is apparent.
Before we delve into editing this custom property, here is a small non-comprehensive overview of what is possible within the Entity Composer. Every action and command that can be executed from the Business Tools Control correspond to pipelines and actions that can be seen in the Sitecore.Commerce.Plugin.Composer assembly. Some which include:
- Sitecore.Commerce.Plugin.Composer.DoActionAddChildViewBlock
- Sitecore.Commerce.Plugin.Composer.DoActionMakeTemplateBlock
- Sitecore.Commerce.Plugin.Composer.DoActionAddPropertyBlock
You can also see these as actions on any requested composer template or entity.
"$values": [{
"$type": "Sitecore.Commerce.EntityViews.EntityActionView, Sitecore.Commerce.Plugin.Views",
"DisplayName": "Edit View",
"Description": "Edits a view",
"IsEnabled": true,
"RequiresConfirmation": false,
"ConfirmationMessage": "",
"UiHint": "",
"EntityView": "EditView",
"Icon": "edit",
"Name": "EditView",
}, {
"$type": "Sitecore.Commerce.EntityViews.EntityActionView, Sitecore.Commerce.Plugin.Views",
"DisplayName": "Remove View",
"Description": "Removes the view",
"IsEnabled": false,
"RequiresConfirmation": true,
"ConfirmationMessage": "Are you sure you wish to perform this operation?",
"UiHint": "",
"EntityView": "",
"Icon": "window_close",
"Name": "RemoveView",
}, {
"$type": "Sitecore.Commerce.EntityViews.EntityActionView, Sitecore.Commerce.Plugin.Views",
"DisplayName": "Make Template",
"Description": "Makes a template",
"IsEnabled": false,
"RequiresConfirmation": false,
"ConfirmationMessage": "",
"UiHint": "",
"EntityView": "MakeTemplate",
"Icon": "tools",
"Name": "MakeTemplate",
}
If you inspect the network requests made from the Sitecore Commerce Business Tools while working with the entity composer, you can see that they are simple OData requests that utilize the ForAction field. Here is an example of the request made when a user clicks the edit view button of an associated child view.
The important things to note here are:
- The entityId will still correspond to the parent entity and not the id of the template or child view.
- It is important that the action and view names are EditView. This tells Sitecore that we are planning on editing the child view (so that right fields are returned) and that when we make a DoAction request after editing the fields the right composer pipeline is called.
In this example we are only using the EditView functionality, but should you want to create or associate a template from code you could follow the same idea of inspecting the requests that are made when you do such actions within the Business Tools Control Panel.
Now back to our example. We have a child view on all customers with the property of Secondary Email. It doesn t make much sense to leave this field unpopulated or the default value so to utilize our new field we will now be asking a customer to enter a secondary email on account creation and then we will pass that value to the Sitecore Commerce Pipeline CreateUser.
If you have extended commerce entities in the past, this part will still be the same as it was prior to the release of the commerce composer. (There is a helpful post by Vipin Baka on the Community Site here Extend Registration Model in storefront).
Finally, we get to the fun stuff. If you have gotten to this point in previous versions of Sitecore Commerce, you would have had to extend the Commerce Engine to take in our new custom Secondary Email Address and save it in the database. But thanks to the Entity Composer we now longer must. The Create User pipeline is as far as we need to go. Now we don t want to modify the way Sitecore will create the customer, so we can leave the majority of the code untouched. Another approach would be to create an entirely new pipeline block and add it to the end of the chain, but for example purposes I have added it to the CreateUser block itself.
Before we get into the code another quick overview of the process that is remaining unmodified and running behind the scenes.
- The create user request is sent to the engine.
- The customer is created in the database.
- The composer is checked to see if there are any templates linked to the entity.
- If yes, the composer template is added to the new customer as a child view.
After all of this runs we want to update the value of the property on the child view of the template. Remember that this is within the context of the create user pipeline. First, we will want to get the composer template that we want to edit in order to retrieve the item id. This can be achieved with the code below:
var composerEntityView = GetEntityView(container, "Entity-ComposerTemplate-Secondary Email", "", "master", "", new ServiceProviderResult());
You can find the ID of your template via the Business Tools Control Panel.
Once you have the composer entity, the item id will be within the child views.
var childView = (EntityView) composerEntityView.ChildViews.FirstOrDefault(x => x.Name == "TestCustomer");
var childViewId = childView.ItemId;
It is important to note that the item id will persist across all linked entities, so you can serialize it or save it in a config to avoid making this call every time.
Once you have the item id we can mimic the request for edit that is made from the Business Tools and as is displayed above like so:
var entityView = GetEntityView(container, entityId, "Composer-3ef588483fd54918a9e1f2e4d7dd3761", "EditView", "EditView", result);
The entityId in this case will be the customer and NOT the composer template. I have the item id hardcoded, (it doesn t change) but it corresponds directly to the childView.ItemId from above. Now that we have the entity view we can edit the properties and then submit it back to the engine.
entityView.Properties.FirstOrDefault(x => x.Name == "Secondary Email").Value = (string)request.Properties["SecondEmail"];
var resultCommand = DoAction(container, entityView, result);
If the CommerceCommand returns an OK response, the childview has been updated and it now contains our custom value. Here is the complete code that runs after the Process method in the CreateUser pipeline.
private void TestSave(Container container, string entityId, CreateUserRequest request, CreateUserResult result, string externalId)
{
var entityView = GetEntityView(container, entityId, "Composer 3ef588483fd54918a9e1f2e4d7dd3761", "EditView", "EditView", result);
if (request.Properties.ContainsProperty("SecondEmail") &&
entityView.Properties.Any(x => x.Name == "Secondary Email"))
{
entityView.Properties.FirstOrDefault(x => x.Name == "Secondary Email").Value = (string)request.Properties["SecondEmail"];
var resultCommand = DoAction(container, entityView, result);
}
}
This is just a very small example of how you can use the new Entity Composer to extend commerce entities without touching the commerce engine, but by following the method of mimicking the calls made from the Business Tools Control Panel the possibilities are very exciting. Perhaps you could conditionally add child views to an entity rather than a one-to-one link. You could also write a pipeline that converts already existing child views into templates and associates them with sellable items. Not to forget, there is a whole other world of extending the composer plugin itself.
I hope this helps you in using the new composer functionality to accelerate Sitecore Commerce custom development.