Dynamics CRM 365 Tips: Easy Way to Get AliasedValue

Have you ever join multiple tables when retrieving your data in the plugin? If so, most likely you already know what is AliasedValue is all about. This post will describe when and how you can use AliasedValue. But the objective of this post is to make a simple extension to consume AliasedValue easier. And like usual, I’ll show you how to make code using TDD (Test-Driven Development) style.

For example, you have this query in your code:

public static Entity[] GetSystemUser(this IOrganizationService service, Guid userId)
        {
            var query = new QueryExpression("systemuser")
            {
                ColumnSet = new ColumnSet("businessunitid"),
                NoLock = true
            };
            query.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, userId);

            var teamMembershipLink = query.AddLink("teammembership", "systemuserid", "systemuserid");
            teamMembershipLink.EntityAlias = "a";
            teamMembershipLink.Columns = new ColumnSet(false);

            var teamLink = teamMembershipLink.AddLink("team", "teamid", "teamid");
            teamLink.EntityAlias = "b";
            teamLink.Columns = new ColumnSet("name");

            var result = service.RetrieveMultiple(query);

            return result.Entities.Any() ? result.Entities.ToArray() : new Entity[] { };
        }

If you want to get the Team.Name, you need to do like this (which is not convenient to me because we need to parse the value to AliasedValue, only then we need to cast to the data type that we need it):

var systemUsers = service.GetSystemUser(context.UserId);
var teamNames = systemUsers.Select(su =>
{
	// get the attribute with type AliasedValue with format [Entity Alias].[Attribute Name]
	var aliasedValue = su.GetAttributeValue<AliasedValue>("b.name");
	return aliasedValue != null ? (string)aliasedValue.Value : "";
}).ToArray();

So now we will create SharedProjects project. The purpose of the SharedProjects is to be referenced by other’s projects. But the code itself automatically merged without needed to do it manually. This method is being called the official way to shared code across Dynamics CRM plugin development (even though I’m not convinced with this way because there are imitations with this SharedProjects model. E.G: can’t use a third-party library, only can share .cs files).

Create SharedLibs Related Project

First, you need to create a Shared Project and give it the name as SharedLibs (the namespace of your lib).

Shared Project type

Then you need to create a unit test project and give it the name SharedLibs.Tests. Add SharedLibs project to the reference and add NuGet package Microsoft.CrmSdk.CoreAssemblies on this project.

Then this is our first test for the extension in SharedLibs.Tests.EntityExtensionsTests.cs:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
using System;

namespace SharedLibs.Tests
{
    [TestClass]
    public class EntityExtensionTests
    {
        [TestMethod]
        public void GetAliasedEntity_Ok()
        {
            var entity = new Entity("account", Guid.NewGuid());
            entity["alias.new_description"] = new AliasedValue("account", "new_description", "hello-world");
            var testRef = new EntityReference("new_test", Guid.NewGuid());
            entity["alias.new_lookupid"] = new AliasedValue("account", "new_lookupid", testRef);

            var aliasEntity = entity.GetAliasedEntity("alias");
            Assert.AreEqual("hello-world", aliasEntity["new_description"]);
            Assert.AreEqual(testRef.Id, ((EntityReference)aliasEntity["new_lookupid"]).Id);
        }

        [TestMethod]
        public void GetAliasedEntity_NoAlias()
        {
            var entity = new Entity("account", Guid.NewGuid());
            entity["new_description"] = "hello-world";

            var aliasEntity = entity.GetAliasedEntity("alias");
            Assert.IsNull(aliasEntity.GetAttributeValue<string>("new_description"));
        }
    }
}

Then we can create the implementation for the extension in SharedLibs.EntityExtensions.cs:

using Microsoft.Xrm.Sdk;
using System.Linq;

namespace SharedLibs
{
    public static class EntityExtension
    {
        public static Entity GetAliasedEntity(this Entity entity, string alias)
        {
            var aliasEntity = new Entity();

            var attributes = entity.Attributes.Where(e => e.Key.Contains(alias)).ToArray();
            if (!attributes.Any()) return aliasEntity;

            foreach (var attribute in attributes)
            {
                var aliasedValued = attribute.Value as AliasedValue;
                if (aliasedValued == null) continue;

                if (string.IsNullOrEmpty(aliasEntity.LogicalName))
                {
                    aliasEntity.LogicalName = aliasedValued.EntityLogicalName;
                }
                
                aliasEntity[aliasedValued.AttributeLogicalName] = aliasedValued.Value;
            }

            return aliasEntity;
        }
    }
}

Using the extension method will boost the clarity of the code. Now, you can call the extension, whenever we have the object with the type Entity. Here you can run the tests to see if the code success or not for our extension method.

Implementation

The scenario is pretty simple, we just want to be the set the Description of the new_customdocument table with the formula: BU: {CurrentUser.BussinessUnit.Name}. Team: {CurrentUser.Team.Name}. We just need to retrieve SystemUser join with TeamMembership join with Team to get all the data. Then for sure, we will create the test project and the plugin project for it.

Create a library project with the name: Demo.Plugins. Add NuGet package Microsoft.CrmSdk.CoreAssemblies. Add reference for SharedLibs also for this project.

Create a unit test project with the name: Demo.Plugins.Tests. Add NuGet package FakeXrmEasy. Also, we need to add a reference to Demo.Plugins.

Now we will create the testing in Demo.Plugins.Tests.DemoPluginTests.cs:

using FakeXrmEasy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
using System;

namespace Demo.Plugins.Tests
{
    [TestClass]
    public class DemoPluginTests
    {
        [TestMethod]
        public void DemoPluginTests_ShouldOk()
        {
            var user = new Entity("systemuser", Guid.NewGuid());
            user["businessunitid"] = new EntityReference("businessunit", Guid.NewGuid()) {Name = "BU001"};

            var team = new Entity("team", Guid.NewGuid());
            team["name"] = "TM001";

            var teamMembership = new Entity("teammembership", Guid.NewGuid());
            teamMembership["systemuserid"] = user.Id;
            teamMembership["teamid"] = team.Id;

            var fakedContext = new XrmFakedContext();
            fakedContext.Initialize(new[] { user, team, teamMembership });

            var target = new Entity();

            var pluginContext = fakedContext.GetDefaultPluginContext();
            pluginContext.UserId = user.Id;

            fakedContext.ExecutePluginWithTarget<DemoPlugin>(pluginContext, target);

            Assert.AreEqual("BU: BU001. Team: TM001.", target["new_description"]);
        }
    }
}

Last, this is the implementation in Demo.Plugins.DemoPlugin.cs:

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Linq;
using SharedLibs;

namespace Demo.Plugins
{
    public class DemoPlugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));
            var serviceFactory = (IOrganizationServiceFactory)
                serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            var service = serviceFactory.CreateOrganizationService(context.UserId);

            var target = context.InputParameters["Target"] as Entity;
            if (target == null) return;

            var systemUsers = service.GetSystemUser(context.UserId);
            if(!systemUsers.Any()) return;

            var bu = systemUsers[0].GetAttributeValue<EntityReference>("businessunitid");
            var buName = bu.Name;

            var teamNames = systemUsers.Select(su =>
            {
                var team = su.GetAliasedEntity("b");
                return team.GetAttributeValue<string>("name");
            }).ToArray();

            var teamText = string.Join(",", teamNames);

            target["new_description"] = $"BU: {buName}. Team: {teamText}.";
        }
    }

    public static class OrganizationServiceExtension
    {
        public static Entity[] GetSystemUser(this IOrganizationService service, Guid userId)
        {
            var query = new QueryExpression("systemuser")
            {
                ColumnSet = new ColumnSet("businessunitid"),
                NoLock = true
            };
            query.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, userId);

            var teamMembershipLink = query.AddLink("teammembership", "systemuserid", "systemuserid");
            teamMembershipLink.EntityAlias = "a";
            teamMembershipLink.Columns = new ColumnSet(false);

            var teamLink = teamMembershipLink.AddLink("team", "teamid", "teamid");
            teamLink.EntityAlias = "b";
            teamLink.Columns = new ColumnSet("name");

            var result = service.RetrieveMultiple(query);

            return result.Entities.Any() ? result.Entities.ToArray() : new Entity[] { };
        }
    }
}

If you see in Demo.Plugins.DemoPlugin.cs, you will notice that when we accessing Team.Name attribute, we just access it like when we get a value from an entity (not using AliasedValue). Pretty simple and nice right?

Here is the test result:

Test result

This feature is embedded also in Niam.Xrm.Framework. So basically this idea is already implemented on the framework. 

Here is the plugin step on the CRM:

Plugin step

Last, this is the result on the CRM screen:

CRM screen result

You can download the full source code here.

3 thoughts on “Dynamics CRM 365 Tips: Easy Way to Get AliasedValue

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.