Tag Cloud

CRM 2011 (161) CRM 4.0 (144) C# (116) JScript (109) Plugin (92) Registry (90) Techpedia (77) PyS60 (68) WScript (43) Plugin Message (31) Exploit (27) ShellCode (26) FAQ (22) JavaScript (21) Killer Codes (21) Hax (18) VB 6.0 (17) Commands (16) VBScript (16) Quotes (15) Turbo C++ (13) WMI (13) Security (11) 1337 (10) Tutorials (10) Asp.Net (9) Safe Boot (9) Python (8) Interview Questions (6) video (6) Ajax (5) VC++ (5) WebService (5) Workflow (5) Bat (4) Dorks (4) Sql Server (4) Aptitude (3) Picklist (3) Tweak (3) WCF (3) regex (3) Config (2) LINQ (2) PHP (2) Shell (2) Silverlight (2) TSql (2) flowchart (2) serialize (2) ASHX (1) CRM 4.0 Videos (1) Debug (1) FetchXml (1) GAC (1) General (1) Generics (1) HttpWebRequest (1) InputParameters (1) Lookup (1) Offline Plug-ins (1) OutputParameters (1) Plug-in Constructor (1) Protocol (1) RIA (1) Sharepoint (1) Walkthrough (1) Web.config (1) design patterns (1) generic (1) iframe (1) secure config (1) unsecure config (1) url (1)

Pages

Sunday, April 21, 2013

CRM 4.0 Relationships Explained

Relationships in CRM 4.0 are quite powerful but hard to understand at first. There are so many moving parts tied to so many places that it is sometimes difficult to predict what the actual outcome is. So here is my attempt to explain a bit further what the relationships enhancements were in CRM 4.0. To start with let’s explain with a diagram what relationship types are available. Note that to determine the actual feasibility of a relationship (e.g. it may be the case that a specific relationship is not possible between an entity pair) you can use our APIs.

One-to-Many System-System (1:N)

In MSCRM 4.0 you can link system entity to another system entity. The child system entity has a lookup field to associate it with a parent system entity.

Self-Referential

In CRM 4.0 you can link An entity to itself. For example a Case can be linked to a master Case.

Multiple Relationships Between Entities

Many of you know about mulitple lookups issues with same entity, now it can be achieved in MS CRM 4.0 e.g the Account entity might have two relationships with a Contact entity – a primary and secondary Contact.

Many-to-Many System-System, System-Custom and Custom-Custom (N:N)

In MSCRM 4.0 this is major requirement for many to many relationship, now it is possible . Not only will this remove the need to build a “joining” entity, but you have control over how the relationships show up in the CRM UI.

Relationships CRM 4.0

Backend

The major architectural changes were that we introduced the notion of many-to-many relationships and self referential relationships. To enable many-to-many relationships we implement intersect entities under the covers. To enable self referential relationships we added a couple of checks to prevent circular parental references and we also redid portions of several system relationships that were hardcoded to be metadata driven to enable system to system and multiple relationships.

Programmability

Of course we had to provide means for programmers to take advantage of all the niceties that we implemented so we had to introduce a couple of new message and attributes and make some changes in the way fetchXml process relationships. Details are on the SDK but here are a couple of quick examples.

Creating new relationships

Yep, you can create brand new relationships (metadata) programmatically using the metadata API. Here is an example on how to create a Many-to-Many relationship. The CrmUtils class is just a wrapper class that a colleague created to create a label for only one language, 1033-English in this case (as you know CRM 4.0 is Multi Language enabled).

public static void addRelatedTest(TitanMiscTests.CrmSdk.CrmService service)
{
//Links (relates) an account record to a lead record in a manyToMany relationship
Moniker moniker1 = new Moniker();
moniker1.Name = "account";
moniker1.Id = new Guid("4BD77CC1-8D6B-DC11-B026-0017A41E8C1D");
Moniker moniker2 = new Moniker();
moniker2.Name = "lead";
moniker2.Id = new Guid("D1CAB380-C56B-DC11-B026-0017A41E8C1D");
AssociateEntitiesRequest request = new AssociateEntitiesRequest();
request.Moniker1 = moniker1;
request.Moniker2 = moniker2;
request.RelationshipName = "new_account_lead_custom";
service.Execute(request);
}

Adding/Removing records for a relationship

To add a new record to a many-to-many relationship you can use the following code. A similar code can be used to remove a record, just use DisassociateEntities message request/response instead of AssociateEntities.

Note that working with N:N relationships is slightly different than working with a One-to-many relationship (for the later you use SetRelated and RemoveRelated messages instead).

    public static void addRelatedTest(TitanMiscTests.CrmSdk.CrmService service)
{
//Links (relates) an account record to a lead record in a manyToMany relationship
Moniker moniker1 = new Moniker();
moniker1.Name = "account";
moniker1.Id = new Guid("4BD77CC1-8D6B-DC11-B026-0017A41E8C1D");
Moniker moniker2 = new Moniker();
moniker2.Name = "lead";
moniker2.Id = new Guid("D1CAB380-C56B-DC11-B026-0017A41E8C1D");
AssociateEntitiesRequest request = new AssociateEntitiesRequest();
request.Moniker1 = moniker1;
request.Moniker2 = moniker2;
request.RelationshipName = "new_account_lead_custom";
service.Execute(request);
}

Retrieving relationships

The following fetch will retrieve all the leads associated with account with name “Foo” in the custom relationship whose intersect entity is “new_account_lead_custom”.

   public static void retrieveEntitiesViaFetch(TitanMiscTests.CrmSdk.CrmService service)
{
string linkFetch = @"











";
string result = service.Fetch(linkFetch);
Console.WriteLine(result);
}

The same query can be accomplished using QueryExpression as follows, note how the query is constructed from bottom to top when compared with fetchXml.


public static void retrieveEntityListFromManyToMany(TitanMiscTests.CrmSdk.CrmService service)
{
//This code will retrieve a list of "leads" associated with the entity "Foo" on the relationship whose intersect entity is "new_account_lead_custom"
//Filter by the specific record that we are looking for
//(In this example we assume that there are no other accounts with the name Foo, otherwise
// if would be recommended to use the account "id" instead of the name.
ConditionExpression conditionName = new ConditionExpression();
conditionName.AttributeName = "name";
conditionName.Operator = ConditionOperator.Equal;
conditionName.Values = new object[1];
conditionName.Values[0] = "Foo";
FilterExpression selectByName = new FilterExpression();
selectByName.Conditions = new ConditionExpression[] { conditionName };
//Create nested link entity and apply filter criteria
LinkEntity nestedLinkEntity = new LinkEntity();
nestedLinkEntity.LinkToEntityName = "account";
nestedLinkEntity.LinkFromAttributeName = "accountid";
nestedLinkEntity.LinkToAttributeName = "accountid";
nestedLinkEntity.LinkCriteria = selectByName;
//Create the nested link entities
LinkEntity intersectEntity = new LinkEntity();
intersectEntity.LinkToEntityName = "new_account_lead_custom";
intersectEntity.LinkFromAttributeName = "leadid";
intersectEntity.LinkToAttributeName = "leadid";
intersectEntity.LinkEntities = new LinkEntity[] { nestedLinkEntity };
//Create Query expression and set the entity type to lead
QueryExpression expression = new QueryExpression();
expression.EntityName = "lead";
expression.LinkEntities = new LinkEntity[] { intersectEntity };
RetrieveMultipleRequest request = new RetrieveMultipleRequest();
request.Query = expression;
//Execute and examine the response
RetrieveMultipleResponse response = (RetrieveMultipleResponse)service.Execute(request);
BusinessEntity[] entities=response.BusinessEntityCollection.BusinessEntities;
Console.WriteLine("Total related=" + entities.Length);
}

Migration and Upgrade Microsoft Dynamics CRM 4.0 to 2011

Solution Management

The concept is quite powerful as it allows components to be layered on to the base system but also on top of other solutions (i.e. when there are inter-dependencies). It also provides a way to protect the intellectual property of the components in your solution and includes change management and versioning.

Plug-In Transaction Support

In CRM 4.0 you could register a plug-in to run either before (pre-event) or after (post-event) the CRM platform operation. However, you were not able to run as part of the transaction itself, so you had to write your own compensation logic in the event the CRM platform operation failed. CRM 5.0 addresses this limitation, and you can now choose to register you plug-in as part of the platform operation. The CRM 5.0 plug-in registration tool has been modified to support this.

Automatic Plug-In Profiling

CRM 5.0 will keep track of how a plug-in is executing, what resources it consumes, if it is causing unexpected exceptions and whether or not it is violating security constraints. If a particular plug-in fails a number of times it is automatically disabled from executing, helping to maintain system integrity.

ADO.Net Data Services and .Net RIA services support

Enables an easier data access for web applications, AJAX and Silverlight

Workflow Up gradation

Workflows in CRM 4.0 will be migrated automatically to CRM 5.0 as part of the upgrade process.

Running workflow instances in CRM 4.0 will resume at the point they were stopped once the upgrade process has completed.

Custom workflow activities written for CRM 4.0 will continue to run in CRM 5.0 after migration, and will be wrapped by the new WF interop activity.

Also can replace Plug Ins by Workflow since workflows provides Pre and Post Image as wells as Input and Output parameter in Workflow Context.

Claims based Authentication and Federation

CRM 5.0 will enable us to integrate transparently authentication with other applications either On-Premise or on the Cloud.

Unstructured Relationships

CRM 5.0 allows you to define ad-hoc relationships between any two entities.

Team Ownership

Entities in CRM 4.0 were either User Owned or Organization Owned. Now Team Owned entities are added in CRM5, and integrated into the role-based security model.

Native SharePoint Integration

Integration with Windows SharePoint Services for document management, which includes site and document library provisioning, document metadata, item security, and check-in/check-out capabilities.

Others

User friendly analytical tool which includes new dashboards.

Tighter integration with Outlook

Download an Attachment

The following code example demonstrates how to download an attachment programmatically. This technique is demonstrated for a note (annotation), an e-mail attachment (activitymimeattachment), and for a sales literature item. You can use the same technique for downloading an e-mail template.


//CRM4.0: Download an Attachment
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using Microsoft.Crm.Sdk.Utility;
using System.Web.Services.Protocols;

namespace Microsoft.Crm.Sdk.HowTo
{
// Use the following Microsoft Dynamics CRM namespaces in the sample.
using CrmSdk;

class DownloadAttachment
{
static void Main(string[] args)
{
bool success = false;
try
{
// TODO: Change the service URL and organization to match your Microsoft Dynamics
// CRM server installation. A value of String.Empty for the service URL indicates
// to use the default service URL defined in the WSDL.
success = DownloadAttachment.Run("http://localhost:5555", "CRM_Organization");
}
catch (SoapException ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine(ex.Message);
Console.WriteLine(ex.Detail.InnerText);
}
catch (System.Exception ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine(ex.Message);

// Display the details of the inner exception.
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.Message);

SoapException se = ex.InnerException as SoapException;
if (se != null)
Console.WriteLine(se.Detail.InnerText);
}
}
finally
{
Console.WriteLine("Completed successfully? {0}", success);
Console.WriteLine("Press to exit.");
Console.ReadLine();
}
}

public static bool Run(string crmServerUrl, string orgName)
{

bool success = false;

try
{
// Set up the CRM Service.
CrmService service = CrmServiceUtility.GetCrmService(crmServerUrl, orgName);
// Cache credentials so each request does not have to be authenticated again.
service.PreAuthenticate = true;

#region Setup Data Required for this Sample

// Create the root account.
Guid setupAnnotationId = Guid.Empty;
Guid setupEmailAttachmentId = Guid.Empty;
Guid setupEmailId = Guid.Empty;
Guid setupSalesLiteratureId = Guid.Empty;
Guid setupSalesLiteratureItemId = Guid.Empty;

// Create the annotation record.
annotation setupAnnotation = new annotation();
setupAnnotation.subject = "Example Annotation Attachment";
setupAnnotation.filename = "ExampleAnnotationAttachment.txt";
setupAnnotation.documentbody = "Sample Annotation Text";
setupAnnotationId = service.Create(setupAnnotation);

// Create the e-mail record.
email setupEmail = new email();
setupEmail.subject = "Example email";
setupEmail.torecipients = "unknown@example.com";
setupEmail.description = "This is an example email.";
setupEmailId = service.Create(setupEmail);

// Create the activitymimeattachment record for an e-mail attachment.
activitymimeattachment setupEmailAttachment = new activitymimeattachment();
setupEmailAttachment.subject = "Example Email Attachement";
setupEmailAttachment.filename = "ExampleEmailAttachment.txt";
setupEmailAttachment.body = "Some Text";
setupEmailAttachment.mimetype = "text/plain";
setupEmailAttachment.attachmentnumber = new CrmNumber();
setupEmailAttachment.attachmentnumber.Value = 1;
setupEmailAttachment.activityid = new Lookup();
setupEmailAttachment.activityid.type = EntityName.email.ToString();
setupEmailAttachment.activityid.Value = setupEmailId;
setupEmailAttachmentId = service.Create(setupEmailAttachment);

// Create the salesliterature record.
salesliterature attachment3 = new salesliterature();
attachment3.name = "Example SalesLiterature";
attachment3.hasattachments = new CrmBoolean();
attachment3.hasattachments.Value = true;
setupSalesLiteratureId = service.Create(attachment3);

// Create the salesliteratureitem record for an attachment to salesliterature.
salesliteratureitem attachementItem3 = new salesliteratureitem();
attachementItem3.title = "Example sales literature attachment";
attachementItem3.filename = "ExampleSalesLiteratureAttachment.txt";
attachementItem3.documentbody = "Example sales literature text.";
attachementItem3.mimetype = "text/plain";
attachementItem3.salesliteratureid = new Lookup();
attachementItem3.salesliteratureid.type = EntityName.salesliterature.ToString();
attachementItem3.salesliteratureid.Value = setupSalesLiteratureId;
setupSalesLiteratureItemId = service.Create(attachementItem3);

#endregion

#region How to download attachment from annotation record

// SDK: annotationId = new Guid("{bee08735-09d3-de11-9d71-00155da4c706}");
Guid annotationId = setupAnnotationId;

// Define the columns to retrieve from the annotation record.
ColumnSet cols1 = new ColumnSet();
cols1.Attributes = new string[] { "filename", "documentbody" };

// Retrieve the annotation record.
annotation annotationAttachment = (annotation)service.Retrieve(EntityName.annotation.ToString(), annotationId, cols1);

// Download the attachment in the current execution folder.
using (FileStream fileStream = new FileStream(annotationAttachment.filename, FileMode.OpenOrCreate))
{
byte[] fileContent = new UTF8Encoding(true).GetBytes(annotationAttachment.documentbody);
fileStream.Write(fileContent, 0, fileContent.Length);
}

#endregion

#region How to download attachment from activitymimeattachment record

// SDK: emailAttachmentId = new Guid("{c1e08735-09d3-de11-9d71-00155da4c706}");
Guid emailAttachmentId = setupEmailAttachmentId;

// Define the columns to retrieve from the activitymimeattachment record.
ColumnSet cols2 = new ColumnSet();
cols2.Attributes = new string[] { "filename", "body" };

// Retrieve the activitymimeattachment record.
activitymimeattachment emailAttachment = (activitymimeattachment)service.Retrieve(EntityName.activitymimeattachment.ToString(), emailAttachmentId, cols2);

// Download the attachment in the current execution folder.
using (FileStream fileStream = new FileStream(emailAttachment.filename, FileMode.OpenOrCreate))
{
// byte[] fileContent = Convert.FromBase64String(emailAttachment.body);
byte[] fileContent = new UTF8Encoding(true).GetBytes(emailAttachment.body);
fileStream.Write(fileContent, 0, fileContent.Length);
}

#endregion

#region How to download attachment from salesliterature record

// SDK: salesLiteratureId = new Guid("{a836993e-09d3-de11-9d71-00155da4c706}");
Guid salesLiteratureId = setupSalesLiteratureId;

// Search for all the salesliteratureitem records that belong
// to the specified salesliterature record.
BusinessEntityCollection entities = null;
salesliteratureitem entity = null;

// Create the query for search.
QueryExpression query = new QueryExpression();
query.EntityName = EntityName.salesliteratureitem.ToString();

// Define the columns to retrieve from the salesliteratureitem record.
ColumnSet cols3 = new ColumnSet();
cols3.Attributes = new string[] { "filename", "documentbody", "title" };

// Set the filter condition to query.
ConditionExpression condition = new ConditionExpression();
condition.AttributeName = "salesliteratureid";
condition.Values = new string[1] { salesLiteratureId.ToString() };
condition.Operator = ConditionOperator.Equal;
FilterExpression filter = new FilterExpression();
filter.Conditions = new ConditionExpression[] { condition };

query.ColumnSet = cols3;
query.Criteria = filter;

// Retrieve the salesliteratureitem records.
entities = service.RetrieveMultiple(query);

// Check for the retrieved salesliteratureitem count.
if (entities.BusinessEntities.Length != 0)
{
for (int i = 0; i < entities.BusinessEntities.Length; i++)
{
entity = (salesliteratureitem)entities.BusinessEntities[i];

// Download the attachment in the current execution folder.
using (FileStream fileStream = new FileStream(entity.filename, FileMode.OpenOrCreate))
{
byte[] fileContent = new UTF8Encoding(true).GetBytes(entity.documentbody);
fileStream.Write(fileContent, 0, fileContent.Length);
}
}
}

#endregion

#region check success

// Verify that there are attachments.
if (annotationAttachment.filename != null && emailAttachment.filename != null
&& entity.filename != null)
{
success = true;
Console.WriteLine(
"The following files were downloaded to the executable folder:\n {0}\n {1}\n {2}\n",
annotationAttachment.filename, emailAttachment.filename, entity.filename);
}
#endregion

#region Remove Data Required for this Sample

service.Delete(EntityName.annotation.ToString(), setupAnnotationId);
service.Delete(EntityName.activitymimeattachment.ToString(), setupEmailAttachmentId);
service.Delete(EntityName.email.ToString(), setupEmailId);
service.Delete(EntityName.salesliteratureitem.ToString(), setupSalesLiteratureItemId);
service.Delete(EntityName.salesliterature.ToString(), setupSalesLiteratureId);

#endregion

}
catch
{
// You can handle an exception here or pass it back to the calling method.
throw;
}

return success;
}

}
}