Dataverse: Performance Benchmark Create Related Entity via a chain of Create vs RelatedEntities property

Today's blog post will be very technical, comparing creating a chain of related entities vs using RelatedEntitiesproperties.

Demo's Relationship

As you can see in the above image, Contact can have multiple Parents. And each of the Parents can have multiple Children. For our demo today, we will create 1 Contact > 10 Parents > and for each parent, we will create 10 children. Please note that for testing purposes, there is no plugin at all (this scenario was updated after a request from Aylwin Wijaya).

To run the benchmark, I'm using BenchmarkDotNetand using Microsoft.PowerPlatform.Dataverse.Client.

For the benchmarking class itself, I'm using the below code:

using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Xrm.Sdk;
using Microsoft.PowerPlatform.Dataverse.Client;
using Entities;
namespace DataverseClient
{
    [MemoryDiagnoser]
    [Orderer(BenchmarkDotNet.Order.SummaryOrderPolicy.FastestToSlowest)]
    [SimpleJob(launchCount: 1, warmupCount: 0)]
    public class AdvanceCreatePerformanceTest
    {
        public IOrganizationService GetService()
        {
            var builder = Helper.CreateHostBuilder().Build();
            var serviceProvider = builder.Services;
            return serviceProvider.GetRequiredService<ServiceClient>();
        }
        [Benchmark]
        public void CreateNormal()
        {
            var service = GetService();
            var randomName = Guid.NewGuid().ToString().Replace("-", "");
            var contact = new Contact
            {
                FirstName = "Temmy",
                LastName = randomName
            };
            var contactId = service.Create(contact);
            for (int i = 0; i < 10; i++)
            {
                var parent = new tmy_Parent
                {
                    tmy_ContactId = new EntityReference(Contact.EntityLogicalName, contactId),
                    tmy_number = $"Parent {i} {randomName}"
                };
                var parentId = service.Create(parent);
                for (int y = 0; y < 10; y++)
                {
                    var child = new tmy_Child
                    {
                        tmy_number = $"Child {i} {y} {randomName}",
                        tmy_ParentId = new EntityReference(tmy_Parent.EntityLogicalName, parentId)
                    };
                    service.Create(child);
                }
            }
        }
        [Benchmark]
        public void CreateAdvance()
        {
            var service = GetService();
            var randomName = Guid.NewGuid().ToString().Replace("-", "");
            var contact = new Contact
            {
                FirstName = "Temmy",
                LastName = randomName
            };
            var relationshipParent_Child = new Relationship(nameof(tmy_Parent.tmy_child_ParentId_tmy_parent));
            var listParent = new List<tmy_Parent>();
            for (int i = 0; i < 10; i++)
            {
                var listChild = new List<tmy_Child>();
                var parent = new tmy_Parent
                {
                    tmy_number = $"Parent {i} {randomName}"
                };
                for (int y = 0; y < 10; y++)
                {
                    var child = new tmy_Child
                    {
                        tmy_number = $"Child {i} {y} {randomName}"
                    };
                    listChild.Add(child);
                }
                parent.RelatedEntities[relationshipParent_Child] = new EntityCollection(listChild.ToArray())
                {
                    EntityName = tmy_Child.EntityLogicalName
                };
                listParent.Add(parent);
            }
            var relationshipContact_Parent = new Relationship(nameof(Contact.tmy_parent_ContactId_contact));
            contact.RelatedEntities[relationshipContact_Parent] = new EntityCollection(listParent.ToArray())
            {
                EntityName = tmy_Parent.EntityLogicalName
            };
            service.Create(contact);
        }
    }
}

The above code shows that the CreateNormal method will create Contact > Parent > and Child one by one. The CreateAdvance method will call the Createmethod once and pass the related entity with the RelatedEntityproperty.

Then, for the Program.cs here is the code:

using BenchmarkDotNet.Running;
using DataverseClient;
var builder = Helper.CreateHostBuilder().Build();
var serviceProvider = builder.Services;
BenchmarkRunner.Run<AdvanceCreatePerformanceTest>();

As you may expected, here is the result of the benchmark (showing CreateAdvance as more efficient compared to the CreateNormal method):

Method Mean Error StdDev Gen0 Allocated
CreateAdvance 6.090 s 0.1665 s 0.4856 s - 1.09 MB
CreateNormal 36.720 s 0.5192 s 0.4335 s 2000.0000 8.62 MB

Result Benchmark

Below is the screenshot for the proof 🤣:

Proof running

Happy CRM-ing!

Leave a comment

Your comment is sent privately to the author and isn't published on the site.