Associate and Disassociate Many to Many relationship records using C# in Microsoft Dynamics CRM 2011

If we have any N:N(Many to Many) relationship in Microsoft Dynamics CRM 2011, need to manually assign relationship between two entities using SDK.

This example explains how to associate and disassociate N:N(Many to Many) relationship records in CRM 2011 thru SDK. In my example I have N:N relationship between Contact and Account. Relationship name is ls_contact_account.


       // Creates the custom many-to-many relationship between the contact and account.
        public void CreateContactAccountRelationship(IOrganizationService service, EntityReference accountRef, EntityReference contactRef)
        {
            //If one of the ID's is null, do nothing
            if (accountRef == null) return;
            if (contactRef == null) return;
            if (accountRef.LogicalName != "account") return;
            if (contactRef.LogicalName != "contact") return;

            var accountID = accountRef.Id;
            var contactId = contactRef.Id;

            //The relationship schema to create
            string relationshipName = "ls_contact_account";

            //Create a query that will check to see if the relationship already exists between this account and contact
            QueryExpression query = new QueryExpression(relationshipName)
            {
                NoLock = true,
                ColumnSet = new ColumnSet(false),//only get the row ID, since we don't need any actual values
                Criteria =
                {
                    Filters =
			        {
				        new FilterExpression
				        {
					        FilterOperator = LogicalOperator.And,
					        Conditions =
					        {
                                //Get the row for the relationship where the account and contact are the account and contact passed in
						        new ConditionExpression("accountid", ConditionOperator.Equal, accountID.ToString()),
						        new ConditionExpression("contactid", ConditionOperator.Equal, contactId.ToString()),
					        },
				        },
			        }
                }
            };
            var result = service.RetrieveMultiple(query);
            //Check if the relationship was not found
            if (result == null || result.Entities == null || result.Entities.Count < 1)
            {
                //The relationship was not found, so create it
                service.Associate(accountRef.LogicalName, accountRef.Id, new Relationship(relationshipName), new EntityReferenceCollection() { contactRef });
            }
        }

       // Deletes the many-to-many relationship record between the contact and account.
        public void DeleteContactAccountRelationship(IOrganizationService service, EntityReference accountRef, EntityReference contactRef)
        {
            //If one of the ID's is null, do nothing
            if (accountRef == null) return;
            if (contactRef == null) return;
            if (accountRef.LogicalName != "account") return;
            if (contactRef.LogicalName != "contact") return;

            var accountID = accountRef.Id;
            var contactId = contactRef.Id;

            //The relationship schema to create
            string relationshipName = "ls_contact_account";

            //Create a query that will check to see if the relationship already exists between this account and contact
            QueryExpression query = new QueryExpression(relationshipName)
            {
                NoLock = true,
                ColumnSet = new ColumnSet(false),//only get the row ID, since we don't need any actual values
                Criteria =
                {
                    Filters =
			        {
				        new FilterExpression
				        {
					        FilterOperator = LogicalOperator.And,
					        Conditions =
					        {
                                //Get the row for the relationship where the account and contact are the account
						        new ConditionExpression("accountid", ConditionOperator.Equal, accountID.ToString()),
						        new ConditionExpression("contactid", ConditionOperator.Equal, contactId.ToString()),
					        },
				        },
			        }
                }
            };
            var result = service.RetrieveMultiple(query);
            //check if record exists
            if (result != null && result.Entities != null && result.Entities.Count > 0)
            {
                //Delete the N:N relation
                service.Disassociate(accountRef.LogicalName, accountRef.Id, new Relationship(relationshipName), new EntityReferenceCollection() { contactRef });
            }
        }

Merge accounts using C# in Dynamics CRM 2011

When merging two records, you specify one record as the master record, and Microsoft Dynamics CRM treats the other record as the child record or subordinate record. It will deactivate the child record and copies all of the related records (such as activities, contacts, addresses, cases, notes, and opportunities) to the master record.

Here is the same code to merge two accounts.


 // Merge Accounts
        public void MergeAccounts(Guid masterAccountId, Guid subOrdinateAccountId)
        {
            try
            {
                //Create CRM Connection        
                IOrganizationService crmConnection = GetCRMConnection();

                //Create the target for the request.        
                EntityReference target = new EntityReference();

                //Id is the GUID of the account that is being merged into.        
                //LogicalName is the type of the entity being merged to, as a string        
                target.Id = masterAccountId;
                target.LogicalName = "account";

                //Create the request.        
                MergeRequest merge = new MergeRequest();

                // SubordinateId is the GUID of the account merging.    
                merge.SubordinateId = subOrdinateAccountId;
                merge.Target = target;
                merge.PerformParentingChecks = false;
                Entity updateContent = new Entity("account");
                var cols = new ColumnSet(new[] { "primarycontactid", "websiteurl", "telephone1", "fax", "emailaddress1" });

                //Get Master Account Primary Contact,Website,Phone,Fax,Email
                var masterAccount = crmConnection.Retrieve("account", masterAccountId, cols);

                //Get Subordinate Account Primary Contact,Website,Phone,Fax,Email
                var subOrdinateAccount = crmConnection.Retrieve("account", subOrdinateAccountId, cols);

                //If PrimaryContact,Website,Phone,Fax,Email fields data are populated on the Subordinate Account and NOT populated on the Master Account. updated these Subordinate account values to the Master record.                  
                if (!masterAccount.Contains("primarycontactid") && subOrdinateAccount.Contains("primarycontactid"))
                    updateContent.Attributes.Add("primarycontactid", new EntityReference("contact", subOrdinateAccount.GetAttributeValue<EntityReference>("primarycontactid").Id));

                if (!masterAccount.Contains("websiteurl") && subOrdinateAccount.Contains("websiteurl"))
                    updateContent.Attributes.Add("websiteurl", subOrdinateAccount.Attributes["websiteurl"]);

                if (!masterAccount.Contains("telephone1") && subOrdinateAccount.Contains("telephone1"))
                    updateContent.Attributes.Add("telephone1", subOrdinateAccount.Attributes["telephone1"]);

                if (!masterAccount.Contains("fax") && subOrdinateAccount.Contains("fax"))
                    updateContent.Attributes.Add("fax", subOrdinateAccount.Attributes["fax"]);

                if (!masterAccount.Contains("emailaddress1") && subOrdinateAccount.Contains("emailaddress1"))
                    updateContent.Attributes.Add("emailaddress1", subOrdinateAccount.Attributes["emailaddress1"]);

                merge.UpdateContent = updateContent;

                // Execute the request.   
                MergeResponse mergeRes = (MergeResponse)crmConnection.Execute(merge);
            }
            catch (Exception ex)
            {
                //Throw the exception        
                throw ex;
            }
        }

‘Entity role must be specified for reflexive relationship’ exception in Dynamics CRM 2011

This exception comes when you are trying to associate a record (account) to another record (account) having N:N relationship using C#. In my case account can have multiple payer accounts, got this exception when I was trying to associate payer account.

If you get this exception you need to mention relationship PrimaryEntityRole is EntityRole.Referenced or EntityRole.Referencing. Here is sample code


   public static void CreateAccountPayerRelationship(IOrganizationService service, Guid accountId, Guid payerAccountId)
        {
            Relationship relation = new Relationship("ls_account_account_payer");

            relation.PrimaryEntityRole = EntityRole.Referenced;

            EntityReference erPayerAccount = new EntityReference("account", payerAccountId);

            service.Associate("account", accountId, relation, new EntityReferenceCollection() { erPayerAccount });
        }

Create a custom ribbon button on entity Homepage ribbon and get the selected record in Dynamics CRM 2011

Download CRM 2011 Visual Ribbon Editor tool from the following codeplex URL.

http://crmvisualribbonedit.codeplex.com/

1. Establish CRM connection

1

2. Select the entity to customize and click ‘OK’ to load entity ribbon customizations

2

3. Select ‘Homepage’ from Ribbon type dropdown list.

3

4. Select any ribbon button and click on ‘New Button’

5. Update the Ribbon button options in ‘Details’ tab.

4

6. Update the JavaScript options in ‘Action’ tab.

5

a. Add JavaScript function
Function Name: JavaScript function calling in ribbon button click
Library: JavaScript web resource name

b. Add CRM parameter and select the value as ‘SelectedControlSelectedItemReferences’

JavaScript code to get the selected record


function GetContacts(selectedEntityRefs)
{
alert("Id: " + selectedEntityRefs[0].Id +
"\n Name: " + selectedEntityRefs[0].Name +
"\n TypeCode: " + selectedEntityRefs[0].TypeCode +
"\n TypeName: " + selectedEntityRefs[0].TypeName);
}

6

Improve performance for bulk inserts/updates using C# in Dynamics CRM 2011

Most of the time we use normal for/foreach loops to loop the record for bulk inserts/updates

Instead of using simple for/foreach loops, if we use parallel for/foreach loop, it will improve lot of performance.

The syntax of a parallel loop is very similar to the for and foreach loops you already know, but the parallel loop runs faster on a computer that has available cores. Another difference is that, unlike a sequential loop, the order of execution isn’t defined for a parallel loop. Steps often take place at the same time, in parallel. Sometimes, two steps take place in the opposite order than they would if the loop were sequential. The only guarantee is that all of the loop’s iterations will have run by the time the loop finishes.

The Task Parallel Library (TPL) supports data parallelism through the System.Threading.Tasks.Parallel class. This class provides method-based parallel implementations of for and foreach loops (For and For Each in Visual Basic). You write the loop logic for a Parallel.For or Parallel.ForEach loop much as you would write a sequential loop. You do not have to create threads or queue work items. In basic loops, you do not have to take locks. The TPL handles all the low-level work for you. For in-depth information about the use of Parallel.For and Parallel.ForEach , download the document Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4

Log error details from the plugins in Dynamics CRM 2011

Create a custom entity to save all exception details occurs in Plugins and other CRM related applications.

Entity Name: ls_errordetails

Attributes: Name, Exception Type, Exception Details, Exception Message

Call below method LogErrorInCRM() in your catch blocks to save exception details.

        // This method is used to log error details into CRM entity(ls_errordetails)
        private void LogErrorInCRM(IOrganizationService service, Exception ex)
        {
            try
            {
                Entity ls_errorDetails = new Entity("ls_errordetails");

                ls_errorDetails["ls_name"] = Assembly.GetExecutingAssembly().GetName().Name;

                ls_errorDetails["ls_exceptiontype"] = ex.GetType().Name;

                if (ex.InnerException != null)
                    ls_errorDetails["ls_exceptiondetails"] = ex.StackTrace + "\n" + ex.InnerException.ToString();
                else
                    ls_errorDetails["ls_exceptiondetails"] = ex.StackTrace;

                ls_errorDetails["ls_exceptionmessage"] = ex.Message;

                Guid errorID = service.Create(ls_errorDetails);

                if (errorID == Guid.Empty)
                {
                    throw new InvalidPluginExecutionException("Unable to save Error details in CRM " + ex.Message, ex);
                }
            }
            catch (Exception excn)
            {
                //Log error details into text file,if there is any exception while saving error details into CRM
                LogErrorInTextFile(ex);
                LogErrorInTextFile(excn);
            }
        }

        // This method is used to log error details into Text file
        private static bool LogErrorInTextFile(Exception objException)
        {
            string strException = string.Empty;
            StreamWriter sw;
            string strPathName;
            bool bReturn = false;
            try
            {
                strPathName = GetLogFilePath();

                sw = new StreamWriter(strPathName, true);
                sw.WriteLine("Source        : " + objException.Source.ToString().Trim());
                sw.WriteLine("Method        : " + objException.TargetSite.Name.ToString());
                sw.WriteLine("Date        : " + DateTime.Now.ToLongTimeString());
                sw.WriteLine("Time        : " + DateTime.Now.ToShortDateString());
                sw.WriteLine("Computer    : " + Dns.GetHostName().ToString());
                sw.WriteLine("Error        : " + objException.Message.ToString().Trim());
                sw.WriteLine("Stack Trace    : " + objException.StackTrace.ToString().Trim());
                sw.WriteLine("^^-------------------------------------------------------------------^^");
                sw.Flush();
                sw.Close();
                bReturn = true;
            }
            catch (Exception)
            {
                bReturn = false;
            }
            return bReturn;
        }

        private static string GetLogFilePath()
        {
            String strLogFilePath = "C:\\ErrorLog\\PluginErrorLog.txt";
            try
            {
                // if exists, return the path
                if (File.Exists(strLogFilePath) == true)
                    return strLogFilePath;
                //create a text file
                else
                {
                    if (CheckDirectory(strLogFilePath) == false)
                        return string.Empty;

                    FileStream fs = new FileStream(strLogFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                    fs.Close();
                }

                return strLogFilePath;
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        private static bool CheckDirectory(string strLogPath)
        {
            try
            {
                int nFindSlashPos = strLogPath.Trim().LastIndexOf("\\");
                string strDirectoryname = strLogPath.Trim().Substring(0, nFindSlashPos);

                if (false == Directory.Exists(strDirectoryname))
                    Directory.CreateDirectory(strDirectoryname);
                return true;
            }
            catch (Exception)
            {
                return false;

            }
        }

Share records using C# in Dynamics CRM 2011

These examples shows how to grant/modify/revoke access privileges for a record with team.


        //Share a record Read,Write and Append privileges with Team
        private void SharePrivileges(string targetEntityName, Guid targetRecordID, Guid teamID, bool read_Access, bool write_Access, bool append_Access, IOrganizationService orgService)
        {
            try
            {
                //Get User or Team reference and Target Entity and record ID that needs to be shared.      
                var recordRef = new EntityReference(targetEntityName, targetRecordID);
                var teamRef = new EntityReference("team", teamID);
                AccessRights Access_Rights = new AccessRights();
                Access_Rights = AccessRights.None;
                //Read Access           
                if (read_Access == true)
                    Access_Rights = AccessRights.ReadAccess;
                //Write Access (or) Read, Write Access        
                if (write_Access == true)
                    if (Access_Rights == AccessRights.None)
                        Access_Rights = AccessRights.WriteAccess;
                    else
                        Access_Rights = Access_Rights | AccessRights.WriteAccess;
                //Append Access or all or any two accesses         
                if (append_Access == true)
                    if (Access_Rights == AccessRights.None)
                        Access_Rights = AccessRights.AppendToAccess | AccessRights.AppendAccess;
                    else
                        Access_Rights = Access_Rights | AccessRights.AppendToAccess | AccessRights.AppendAccess;
                var grantAccess = new GrantAccessRequest
                {
                    PrincipalAccess = new PrincipalAccess
                    {
                        AccessMask = Access_Rights,
                        Principal = teamRef
                    },
                    Target = recordRef
                };
                // Execute the Request      
                orgService.Execute(grantAccess);
            }
            catch (Exception ex)
            {
                throw new Exception("An error occured while applying Sharing rules for the record." + ex.Message);
            }
        }
        //Code to modify privileges for the target record and team   
        private void ModifyAccess(string targetEntityName, Guid targetRecordID, Guid teamID, IOrganizationService orgService)
        {
            try
            {
                //Get User or Team reference and Target Entity and record ID that needs to be shared.          
                var RecordReference = new EntityReference(targetEntityName, targetRecordID);
                var teamRef = new EntityReference("team", teamID);
                AccessRights accessRights = new AccessRights();
                accessRights = AccessRights.DeleteAccess;
                var modifyAcess = new ModifyAccessRequest
                {
                    PrincipalAccess = new PrincipalAccess
                    {
                        AccessMask = accessRights,
                        Principal = teamRef
                    },
                    Target = RecordReference
                };
                // Execute the Request   
                orgService.Execute(modifyAcess);
            }
            catch (Exception ex)
            {
                throw new Exception("An error occured in Modifying access." + ex.Message);
            }
        }
        //Code to remvove the sharing privileges for the target record and team    
        private void RevokeAccess(string targetEntityName, Guid targetRecordID, Guid teamID, IOrganizationService orgService)
        {
            try
            {
                //Get User or Team reference and Target Entity and record ID that needs to be shared.        
                var recordRef = new EntityReference(targetEntityName, targetRecordID);
                var teamRef = new EntityReference("team", teamID);
                var revokeAcess = new RevokeAccessRequest
                {
                    Revokee = teamRef,
                    Target = recordRef
                };
                // Execute the Request        
                orgService.Execute(revokeAcess);
            }
            catch (Exception ex)
            {
                throw new Exception("An error occured in Revoking access." + ex.Message);
            }
        }

Assign a record to a Team using C# in Dynamics CRM 2011

Here is sample C# code how to assign a record to a team.

       //Assign a record to a team
        private void AssignRecord(Entity TargetEntity, Guid TargetRecordID, Guid OwningTeamID, IOrganizationService orgService)
        {
            try
            {
                // Create the Request Object and Set the Request Object's Properties
                AssignRequest assign = new AssignRequest
                {
                    Assignee = new EntityReference("team", OwningTeamID),
                    Target = new EntityReference(TargetEntity.LogicalName, TargetRecordID)
                };

                // Execute the Request
                orgService.Execute(assign);
            }
            catch (Exception ex)
            {
                throw new Exception("An error occured while assinging Team to a record." + ex.Message);
            }
        }

Call WCF service from plugin in Dynamics CRM 2011

Create a custom entity to maintain all configuration values, In my case I created entity like below and added caseservice details(Name: CaseServiceURL, Column: CaseService, Value: http://localhost/CaseService/Case.svc)

Entity Name: ls_settings

Attributes: Name, Column, Value

After you have created a service, generate the service proxy file using svcutil.exe tool from visual studio command line.

Svcutil.exe http://localhost/CaseService/Case.svc?wsdl

This will generate a configuration file and a code file that contains the client class. Add proxy code file to your plugin project and use the generated client class to call the Service.

Here CaseProxy is ServiceProxy class, ICase is Service interface.

        // send CaseId to other application
        private void SendCase(IOrganizationService service, Guid caseId)
        {
            //Use same binding used in WCF service config
            var binding = new BasicHttpBinding();

            //Get Case service URL
            string endPointAddress = GetEndpointAddress(service);

            if (!string.IsNullOrEmpty(endPointAddress))
            {
                //specify EndpointURL
                var endPoint = new EndpointAddress(endPointAddress);

                //ChannelFactory to specify binding and EndpointURL

                //Here CaseProxy is my ServiceProxy class, ICase is my service interface
                var channelFactory = new ChannelFactory<CaseProxy.ICase>(binding, endPoint);

                //create instance of CaseService
                CaseProxy.ICase caseClient = null;

                try
                {
                    //Create a channel
                    caseClient = channelFactory.CreateChannel();

                    //Call Service Method to send the case
                    caseClient.SendCase(caseId);

                    //Close WCF service
                    ((ICommunicationObject)caseClient).Close();
                }
                catch (Exception ex)
                {
                    if (caseClient != null)
                    {
                        ((ICommunicationObject)caseClient).Abort();
                    }
                    throw new Exception(string.Format("Error occured while sending case request to other application: {0}", ex.Message), ex);
                }
            }
        }

        // Get Case service URL from Settings entity
        private string GetEndpointAddress(IOrganizationService service)
        {
            string endPointAddress = string.Empty;

            string fetchXML = "<fetch version='1.0' output-format='xml-platform' mapping='logical' no-lock='true' distinct='false'>
                                                    <entity name='ls_settings'>
                                                        <attribute name='ls_value' />
                                                        <filter type='and'>
                                                            <condition attribute='ls_column' operator='eq' value='CaseServiceURL'/>
                                                        </filter>
                                                    </entity>
                                                </fetch>";

            var req = new FetchExpression(fetchXML);

            EntityCollection results = service.RetrieveMultiple(req);

            if (results.Entities.Count > 0)
            {
                endPointAddress = results.Entities[0].GetAttributeValue<string>("value");
            }
            return endPointAddress;
        }