#CompositeDesignPattern
Dynamics CRM development can be much easier and efficient using "Object Oriented Design" techniques. Let me explain.
Suppose the case study of dynamics CRM is such that xyz business is having customers as companies each company has sub branches which is again customers. Customers have people representing contacts for the business.
If I say that customers are accounts then sub branches means self relationship with accounts so there is 1-n self relation between accounts. Now for representatives of company, we have contacts entity in CRM. so there is 1-n relation between accounts and contacts.
Means
1 - N relation : Accounts -> Accounts
1 - N relation : Accounts -> Contacts
Now suppose I want to get all such information, logic and relationships in terms of object oriented design, what I should do is. Firstly I will identify the objects in this case study.
I have two objects Accounts and Contacts.
So now I will surely and definitely define classes for these objects. i.e. the pillar of object oriented design.
I have Account class and Contact class. Should I start writing these classes and adding properties for the respective objects? But what if I have properties which are same in both objects. Or I will prefer to make and identify properties similar for both classes and all other classes which can be added later for CRM case studies.
So for the purpose of adding similar properties I will like to define an Abstract class. This class will hold the parent class properties and act as base class to share the properties and methods for all child classes (entities of CRM).
Model: (Objects)
Plugin Code
㉺㉼㉴㉳㉽㉾㈕㈔㈆㈅㈄㈄㈃㈁㈀㉤ Ali Hamza Wadood | Microsoft Dynamics CRM Developer | Software Engineer - Microsoft Technologies(Asp.Net, Asp.Net MVC)
LinkedIn ㉺㉼㉴㉳㉽㉾㈕㈔㈆㈅㈄㈄㈃㈁㈀㉤
Dynamics CRM development can be much easier and efficient using "Object Oriented Design" techniques. Let me explain.
Suppose the case study of dynamics CRM is such that xyz business is having customers as companies each company has sub branches which is again customers. Customers have people representing contacts for the business.
If I say that customers are accounts then sub branches means self relationship with accounts so there is 1-n self relation between accounts. Now for representatives of company, we have contacts entity in CRM. so there is 1-n relation between accounts and contacts.
Means
1 - N relation : Accounts -> Accounts
1 - N relation : Accounts -> Contacts
Now suppose I want to get all such information, logic and relationships in terms of object oriented design, what I should do is. Firstly I will identify the objects in this case study.
I have two objects Accounts and Contacts.
So now I will surely and definitely define classes for these objects. i.e. the pillar of object oriented design.
I have Account class and Contact class. Should I start writing these classes and adding properties for the respective objects? But what if I have properties which are same in both objects. Or I will prefer to make and identify properties similar for both classes and all other classes which can be added later for CRM case studies.
So for the purpose of adding similar properties I will like to define an Abstract class. This class will hold the parent class properties and act as base class to share the properties and methods for all child classes (entities of CRM).
Model: (Objects)
using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Account
{
class Model
{
}
/// <summary>
/// Abstract Entity Class
/// </summary>
public abstract class CRMEntity
{
public Entity Entity;
public EntityReference EntityReference;
public string EntityDisplayName;
public string Href;
public string CurrentAttributeName;
//Other Common Properties
public CRMEntity(Entity entity)
{
this.Entity = entity;
this.EntityReference = new EntityReference(entity.LogicalName, entity.Id);
this.Href = "https://datainvent-sandbox.crm3.dynamics.com/main.aspx?etn=" + Entity.LogicalName + "&id=" + Entity.Id.ToString() + "&newWindow=true&pagetype=entityrecord";
// https://datainvent-sandbox.crm3.dynamics.com/main.aspx?etn=insr_inspectionelement&id=60a3e6da-5584-e711-8125-480fcff44541&newWindow=true&pagetype=entityrecord
}
public CRMEntity(Entity entity, string EntityDisplayName) : this(entity)
{
SetName(EntityDisplayName);
}
public void SetName(string EntityDisplayName)
{
this.EntityDisplayName = EntityDisplayName;
this.Entity.Attributes.Add("EntityName", this.EntityDisplayName);
}
public bool IsValidAttribute(string name)
{
if (this.Entity.Attributes.Contains(name))
return true;
return false;
}
}
public class Account : CRMEntity
{
public Account ParentAccount { get; set; }
public List<Account> SubAccounts = new List<Account>();
public List<Contact> Contacts = new List<Contact>();
public Account() : base(new Entity(EntityNames.Account)) { }
public Account(Entity entity) : base(entity) { }
public Account(Entity entity, Account ParentAccount) : base(entity) {
this.ParentAccount = ParentAccount;
}
public string Name
{
get
{
CurrentAttributeName = "name";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "name";
this.Entity[CurrentAttributeName] = value;
}
}
public Guid AccountId
{
get
{
CurrentAttributeName = "accountid";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<Guid>(CurrentAttributeName);
}
return Guid.Empty;
}
set
{
CurrentAttributeName = "accountid";
this.Entity[CurrentAttributeName] = value;
}
}
public string AccountNo
{
get
{
CurrentAttributeName = "accountno";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "accountno";
this.Entity[CurrentAttributeName] = value;
}
}
}
public class Contact : CRMEntity
{
public Account ParentAccount { get; set; }
public Contact() : base(new Entity(EntityNames.Account)) { }
public Contact(Entity entity) : base(entity) { }
public string FirstName
{
get
{
CurrentAttributeName = "firstname";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "firstname";
this.Entity[CurrentAttributeName] = value;
}
}
public string LastName
{
get
{
CurrentAttributeName = "lastname";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "lastname";
this.Entity[CurrentAttributeName] = value;
}
}
public Guid ContactId
{
get
{
CurrentAttributeName = "contactid";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<Guid>(CurrentAttributeName);
}
return Guid.Empty;
}
set
{
CurrentAttributeName = "contactid";
this.Entity[CurrentAttributeName] = value;
}
}
public EntityReference ParentAccountId
{
get
{
CurrentAttributeName = "parentcustomerid";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<EntityReference>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "parentcustomerid";
this.Entity[CurrentAttributeName] = value;
}
}
}
public class AccountHierarchy
{
public List<Account> Accounts { get; set; }
public List<Contact> Contacts { get; set; }
public AccountHierarchy() {
Accounts = new List<Account>();
Contacts = new List<Contact>();
}
}
public class EntityNames
{
public static string Account = "account";
public static string Contact = "contact";
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Account
{
class Model
{
}
/// <summary>
/// Abstract Entity Class
/// </summary>
public abstract class CRMEntity
{
public Entity Entity;
public EntityReference EntityReference;
public string EntityDisplayName;
public string Href;
public string CurrentAttributeName;
//Other Common Properties
public CRMEntity(Entity entity)
{
this.Entity = entity;
this.EntityReference = new EntityReference(entity.LogicalName, entity.Id);
this.Href = "https://datainvent-sandbox.crm3.dynamics.com/main.aspx?etn=" + Entity.LogicalName + "&id=" + Entity.Id.ToString() + "&newWindow=true&pagetype=entityrecord";
// https://datainvent-sandbox.crm3.dynamics.com/main.aspx?etn=insr_inspectionelement&id=60a3e6da-5584-e711-8125-480fcff44541&newWindow=true&pagetype=entityrecord
}
public CRMEntity(Entity entity, string EntityDisplayName) : this(entity)
{
SetName(EntityDisplayName);
}
public void SetName(string EntityDisplayName)
{
this.EntityDisplayName = EntityDisplayName;
this.Entity.Attributes.Add("EntityName", this.EntityDisplayName);
}
public bool IsValidAttribute(string name)
{
if (this.Entity.Attributes.Contains(name))
return true;
return false;
}
}
public class Account : CRMEntity
{
public Account ParentAccount { get; set; }
public List<Account> SubAccounts = new List<Account>();
public List<Contact> Contacts = new List<Contact>();
public Account() : base(new Entity(EntityNames.Account)) { }
public Account(Entity entity) : base(entity) { }
public Account(Entity entity, Account ParentAccount) : base(entity) {
this.ParentAccount = ParentAccount;
}
public string Name
{
get
{
CurrentAttributeName = "name";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "name";
this.Entity[CurrentAttributeName] = value;
}
}
public Guid AccountId
{
get
{
CurrentAttributeName = "accountid";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<Guid>(CurrentAttributeName);
}
return Guid.Empty;
}
set
{
CurrentAttributeName = "accountid";
this.Entity[CurrentAttributeName] = value;
}
}
public string AccountNo
{
get
{
CurrentAttributeName = "accountno";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "accountno";
this.Entity[CurrentAttributeName] = value;
}
}
}
public class Contact : CRMEntity
{
public Account ParentAccount { get; set; }
public Contact() : base(new Entity(EntityNames.Account)) { }
public Contact(Entity entity) : base(entity) { }
public string FirstName
{
get
{
CurrentAttributeName = "firstname";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "firstname";
this.Entity[CurrentAttributeName] = value;
}
}
public string LastName
{
get
{
CurrentAttributeName = "lastname";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<string>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "lastname";
this.Entity[CurrentAttributeName] = value;
}
}
public Guid ContactId
{
get
{
CurrentAttributeName = "contactid";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<Guid>(CurrentAttributeName);
}
return Guid.Empty;
}
set
{
CurrentAttributeName = "contactid";
this.Entity[CurrentAttributeName] = value;
}
}
public EntityReference ParentAccountId
{
get
{
CurrentAttributeName = "parentcustomerid";
if (this.IsValidAttribute(CurrentAttributeName))
{
return this.Entity.GetAttributeValue<EntityReference>(CurrentAttributeName);
}
return null;
}
set
{
CurrentAttributeName = "parentcustomerid";
this.Entity[CurrentAttributeName] = value;
}
}
}
public class AccountHierarchy
{
public List<Account> Accounts { get; set; }
public List<Contact> Contacts { get; set; }
public AccountHierarchy() {
Accounts = new List<Account>();
Contacts = new List<Contact>();
}
}
public class EntityNames
{
public static string Account = "account";
public static string Contact = "contact";
}
}
Plugin Code
using Account;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Account
{
public class DemoPlugin : IPlugin
{
AccountHierarchy AccountHierarchy = new AccountHierarchy();
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
string entityName = "cms_applicant";
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{ }
else return;
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName != entityName)
return;
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
Account Account = new Account(entity);
FillSubAccounts(ref Account, service);
FillContacts(this.AccountHierarchy.Accounts, service);
// Now to update respective Sub Accounts or Contacts
// finding parent account of an account
// finding sub account of an account
// finding contacts of an accounts
// this is very easy using objects oriented technique
// To iterate over accounts I can do
foreach(Account account in this.AccountHierarchy.Accounts)
{
if (account.ParentAccount != null) {
Account parentaccount = account.ParentAccount;
account.Name = parentaccount.Name + " " + account.AccountNo;
}
foreach (Account sub in account.SubAccounts) {
Account subaccount = sub;
}
// to find contacts of this account I can do
foreach(Contact cont in account.Contacts)
{
Contact contact = cont;
contact.FirstName = account.Name;
}
}
/// To get a particular account I can do
///
Account ParticularAccount = this.AccountHierarchy.Accounts.Where(a => a.ParentAccount.Name == "Beth").Select(e => e).FirstOrDefault();
/// Entity Properties will automatically set when properties for respective objects will set
/// to get CRM entity object associated with account class I can do
Entity ent = ParticularAccount.Entity;
/// Similarty the common properties of Entity, Entity Reference etc. can be set
/// in abstract class and these will be available for every class generated and
/// inheritance as per CRM objects.
///
/// AND so ON...
///
}
public void FillSubAccounts ( ref Account ParentAccount, IOrganizationService service) {
String fetch1 = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' >" +
"<entity name='" + EntityNames.Account + "'>" +
"<all-attributes />" +
"<filter type='and'>" +
"<condition attribute='parentaccountid' operator='eq' value='" + ParentAccount.Entity.Id.ToString() + "' />" +
"<condition attribute='statecode' operator='eq' value='" + "0" + "' />" + // only Active
"</filter>" +
"</entity>" +
"</fetch>";
EntityCollection _entCol = service.RetrieveMultiple(new FetchExpression(fetch1));
if (_entCol != null && _entCol.Entities.Count > 0)
{
foreach (Entity entity in _entCol.Entities)
{
Account Account = new Account(entity, ParentAccount);
if (ParentAccount != null)
{
ParentAccount.SubAccounts.Add(Account);
}
this.AccountHierarchy.Accounts.Add(Account);
//tracingService.Trace("Total Elements List {0} ", _entCol.Entities.Count);
FillSubAccounts(ref Account,service);
}
}
}
public void FillContacts(List<Account> Accounts, IOrganizationService service) {
foreach (Account account in Accounts)
{
// tracingService.Trace("In loop of quesion method {0} ", lstElements.Count.ToString());
String fetch1 = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' >" +
"<entity name='" + EntityNames.Contact + "'>" +
"<all-attributes />" +
"<filter type='and'>" +
"<condition attribute='parentaccountid' operator='eq' value='" + account.Entity.Id.ToString() + "' />" +
"<condition attribute='statecode' operator='eq' value='" + "0" + "' />" +
"</filter>" +
"</entity>" +
"</fetch>";
EntityCollection _entCol = service.RetrieveMultiple(new FetchExpression(fetch1));
if (_entCol != null && _entCol.Entities.Count > 0)
{
foreach (Entity _ent in _entCol.Entities)
{
Contact contact = new Contact(_ent);
account.Contacts.Add(contact);
this.AccountHierarchy.Contacts.Add(contact);
}
}
// tracingService.Trace("Total Qs List {0} ", _entCol.Entities.Count);
}
}
}
}
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Account
{
public class DemoPlugin : IPlugin
{
AccountHierarchy AccountHierarchy = new AccountHierarchy();
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
string entityName = "cms_applicant";
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{ }
else return;
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName != entityName)
return;
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
Account Account = new Account(entity);
FillSubAccounts(ref Account, service);
FillContacts(this.AccountHierarchy.Accounts, service);
// Now to update respective Sub Accounts or Contacts
// finding parent account of an account
// finding sub account of an account
// finding contacts of an accounts
// this is very easy using objects oriented technique
// To iterate over accounts I can do
foreach(Account account in this.AccountHierarchy.Accounts)
{
if (account.ParentAccount != null) {
Account parentaccount = account.ParentAccount;
account.Name = parentaccount.Name + " " + account.AccountNo;
}
foreach (Account sub in account.SubAccounts) {
Account subaccount = sub;
}
// to find contacts of this account I can do
foreach(Contact cont in account.Contacts)
{
Contact contact = cont;
contact.FirstName = account.Name;
}
}
/// To get a particular account I can do
///
Account ParticularAccount = this.AccountHierarchy.Accounts.Where(a => a.ParentAccount.Name == "Beth").Select(e => e).FirstOrDefault();
/// Entity Properties will automatically set when properties for respective objects will set
/// to get CRM entity object associated with account class I can do
Entity ent = ParticularAccount.Entity;
/// Similarty the common properties of Entity, Entity Reference etc. can be set
/// in abstract class and these will be available for every class generated and
/// inheritance as per CRM objects.
///
/// AND so ON...
///
}
public void FillSubAccounts ( ref Account ParentAccount, IOrganizationService service) {
String fetch1 = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' >" +
"<entity name='" + EntityNames.Account + "'>" +
"<all-attributes />" +
"<filter type='and'>" +
"<condition attribute='parentaccountid' operator='eq' value='" + ParentAccount.Entity.Id.ToString() + "' />" +
"<condition attribute='statecode' operator='eq' value='" + "0" + "' />" + // only Active
"</filter>" +
"</entity>" +
"</fetch>";
EntityCollection _entCol = service.RetrieveMultiple(new FetchExpression(fetch1));
if (_entCol != null && _entCol.Entities.Count > 0)
{
foreach (Entity entity in _entCol.Entities)
{
Account Account = new Account(entity, ParentAccount);
if (ParentAccount != null)
{
ParentAccount.SubAccounts.Add(Account);
}
this.AccountHierarchy.Accounts.Add(Account);
//tracingService.Trace("Total Elements List {0} ", _entCol.Entities.Count);
FillSubAccounts(ref Account,service);
}
}
}
public void FillContacts(List<Account> Accounts, IOrganizationService service) {
foreach (Account account in Accounts)
{
// tracingService.Trace("In loop of quesion method {0} ", lstElements.Count.ToString());
String fetch1 = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' >" +
"<entity name='" + EntityNames.Contact + "'>" +
"<all-attributes />" +
"<filter type='and'>" +
"<condition attribute='parentaccountid' operator='eq' value='" + account.Entity.Id.ToString() + "' />" +
"<condition attribute='statecode' operator='eq' value='" + "0" + "' />" +
"</filter>" +
"</entity>" +
"</fetch>";
EntityCollection _entCol = service.RetrieveMultiple(new FetchExpression(fetch1));
if (_entCol != null && _entCol.Entities.Count > 0)
{
foreach (Entity _ent in _entCol.Entities)
{
Contact contact = new Contact(_ent);
account.Contacts.Add(contact);
this.AccountHierarchy.Contacts.Add(contact);
}
}
// tracingService.Trace("Total Qs List {0} ", _entCol.Entities.Count);
}
}
}
}
㉺㉼㉴㉳㉽㉾㈕㈔㈆㈅㈄㈄㈃㈁㈀㉤ Ali Hamza Wadood | Microsoft Dynamics CRM Developer | Software Engineer - Microsoft Technologies(Asp.Net, Asp.Net MVC)
LinkedIn ㉺㉼㉴㉳㉽㉾㈕㈔㈆㈅㈄㈄㈃㈁㈀㉤