RSS

Category Archives: CRM Plug-in

Use of Shared Variables in Plugin

Introduction

Shared Variables will be useful for sharing the data during complex Plug-in development by sharing the data between Plugins registered on both the pre and post events.

Any value stored in the Plug-in context of Pre-event will be available in the Post-event of the Plug-in. This way, we can avoid storing the values in a custom attribute. Certain data validation which needs to be done at post-event can be achieved by passing the value from Pre-event of the Plug-in. This also helps for performing business logic like updating related records asynchronously.

Using the Code

The below code snippet illustrates the usage of Shared Variable. This sample explains the data validation done at Post-event operation based on the value received from the Pre-event operation where a flag is updated with a value at the Pre-event level and sent to the Post-event through shared variable.

//Pre-Create (Entity Name) Plug-in
if (pluginContext.MessageName == "Create")
{
    if (pluginContext.InputParameters.ContainsKey("Target")
                             && 
        pluginContext.InputParameters["Target"] is Entity)
    {
        Entity targetEntity = context.InputParameters["Target"] as Entity;
        if (targetEntity != null)
        {
            bool setFlag = false;

            //Check data validation for attributes
            string country = targetEntity.Attributes["address1_country"];
            OptionSetValue industryCode = (OptionSetValue)target.Attributes["industrycode"];

            // Country is US and Industry Type is Accounting are Preferred
            if (country.ToLower() == "us" && industryCode.Value == 1)
            {
                setFlag = true;
            }
            
            pluginContext.SharedVariables.Add("flag", setFlag);
        }
    }
}

//Post-Create (Entity Name) Plug-in
if (pluginContext.MessageName == "Create")
{
    if (pluginContext.InputParameters.ContainsKey("Target")
                           && 
        pluginContext.InputParameters["Target"] is Entity)
    {
        Entity targetEntity = pluginContext.InputParameters["Target"] as Entity;
        if (targetEntity != null)
        {
            if (pluginContext.SharedVariables.ContainsKey("flag"))
            {
                bool recievedFlag = (bool)pluginContext.SharedVariables["flag"];
                if (recievedFlag)
                {
                    // Do whatever you want to do
                }
             }
         }
    }
}

This method lets you to pass data between Plug-ins without having to customize the system by creating hidden fields on entities.

Note: SharedVariables (collection of key/value pairs) property is available in the IPluginExecutionContext of the execution pipeline.

By Gitanjali
Senior Software Engineer @Team DynamicsCRM.
Mindfire Solutions

 

Tags: , , , , , , , , , , , , , , , , , ,

Retrieve N:N Relationship Records using CrmFetchKit

Introduction

CrmFetchKit is a Cross Browser library that allows the execution of fecthXml queries via JavaScript for Dynamics CRM 2011 and CRM 2013 (using the new WCF Endpoints).

Background

In one of my recent projects, the requirement was to retrieve all Contacts present in a Campaign and to display all those in a Grid onload of Campaign record. As this process would fetch many records, so it should be Asynchronous.

Steps

Here are the steps how to use CrmFetchKit for retrieving records.

Step:1 Get the fetchXml using Advanced Find in Crm.

In my case, I used Advanced Find on Account Entity. Download the fecthXml and save it. It will be like below.

<?xml version="1.0"?>
<fetch distinct="true" mapping="logical" output-format="xml-platform" version="1.0"> 
    <entity name="contact"> 
        <attribute name="contactid"/>
        <attribute name="ownerid"/>
        <attribute name="fullname"/>
        <attribute name="lastname"/>
        <attribute name="telephone2"/>
        <attribute name="firstname"/>
        <attribute name="emailaddress1"/>
        <attribute name="createdon"/>
        <attribute name="new_city"/> 
        <order descending="false" attribute="ownerid"/> 
        <link-entity name="new_campaign_contact" intersect="true" visible="false" to="contactid" from="contactid"> 
        <link-entity name="campaign" to="campaignid" from="campaignid" alias="ab"> 
            <filter type="and">
                <condition attribute="campaignid" value="  campaignId  " uitype="campaign" 
 value="{8BE16D9E-8A7A-E211-B46D-00155D641D0E}"  operator="eq"/> 
            </filter> 
        </link-entity>
        </link-entity>
    </entity> 
</fetch>

Step:2 Add reference of fecthKit in the page where you are going to retrieve records.

<script src="../Scripts/jquery.1.7.1.min.js" type="text/javascript"></script>
<script src="../Scripts/CrmFetchKit.js" type="text/javascript"></script>

NB:The jQuery sholud be jquery.1.7.1.min.js (or higher).

Step:3 Add the code for retrieving Contacts present in a Campaign.

var contactData;
var campaignId;

campaignId = parent.Xrm.Page.data.entity.getId();

if(campaignId != null && campaignId != undefined)
{
    var fecthXMLForContact = '<fetch distinct="true" mapping="logical" output-format="xml-platform" version="1.0">'+
                             '<entity name="contact"> <attribute name="contactid"> <attribute name="ownerid">' +
                             '<attribute name="fullname"> <attribute name="lastname">' +
                             '<attribute name="telephone2"> <attribute name="firstname">' +
                             '<attribute name="emailaddress1"> <attribute name="createdon"> <attribute name="new_city">' +
                             '<order descending="false" attribute="ownerid">' +
                             '<link-entity name="new_campaign_contact" intersect="true" visible="false" to="contactid"   from="contactid">' +
                             '<link-entity name="campaign" to="campaignid" from="campaignid" alias="ab">' +
                             '<filter type="and"> <condition attribute="campaignid" value="' + campaignId + '" uitype="campaign" operator="eq">' +
                             '</condition></filter> </link-entity> </link-entity>' +
                             '</order></attribute></attribute></attribute></attribute></attribute>' +
                             '</attribute></attribute></attribute></attribute></entity> </fetch>';

     CrmFetchKit.Fetch(fecthXMLForContact).fail(function (xhr, status, errorThrown) {
         // get the error-message
         var msg = $(xhr.responseXML).find('Message').text();
         alert('Error occured: ' + msg);
     })
     .done(function (resultsContact) {
         contact = true;
         contactData = resultsContact;                
     });
}

In fetchXml, replace value of CampaignId with the Campaign Id for which you need to retrieve records.
The variable contactData will contain all records present in Campaign.
Here you are done!!!

By Gitanjali
Senior Software Engineer @Team DynamicsCRM.
Mindfire Solutions

 

Tags: , , , , , , , , , , , , , , , , , , ,

Avoiding Deadlock in Plugins

Introduction

Multiple operations with the same Entity inside a Plugin, causes Deadlocks in Plugins and might cancel the transaction. Let’s explore the Trick to avoid the Deadlocks.

Background

In one of my projects, there was a situation, when multiple operations had been done on the same Entity records. A Plugin was there, which was simultaneously updating a record lets say “C” and then from record “C” based on some condition record “B” was being updated and another Plugin was there which was updating values from record “B” to record “A”. And after it is done the final amount had to be shown in record “A” immediately as soon as the custom Popup (a Web Resource), that causes the operation in record “C” is closed and the Popup was being invoked from the ribbon of entity “A”.
As the user wanted to see the effect immediately there was no other option than to just make the Plugin Synchrounous. So, Create/Update and Retrieve operation at the same time was causing the transaction to be Deadlocked sometimes and was not allowing the user to Add/Update multiple records.

Code

In order to solve the issue, I tried several ways. Finally I found that in QueryExpression there is a property called “NoLock” while using RetrieveMultiple method.

Its simple to use but its effect is something worthy to be noted. The code snippet is,

QueryExpression queryCItems= new QueryExpression("new_entityc");
queryCItems.NoLock = true;
queryCItems.ColumnSet = new ColumnSet("new_totalamount");
queryCItems.Criteria.AddCondition("new_aid", ConditionOperator.Equal, entC.Id);

This solved the issue. 🙂

By Dibyasingh Tripathy
Senior Software Engineer @Team DynamicsCRM.
Mindfire Solutions

 
Leave a comment

Posted by on November 26, 2013 in CRM Plug-in

 

Tags: , , , , , , , , , , , , , , ,

CRM 2011 Exception “The HTTP request is unauthorized with client authentication scheme ‘Anonymous’. The authentication header received from the server was ‘NTLM'”

Introduction

This is in reference to my previous post Calling external Web Service from a CRM 2011 Plug-in. This blog post is just a modification of the last post.

Background

In one of the project while calling an external Web Service hosted in some other server(other than CRM server), I found an issue like the one below.

” The HTTP request is unauthorized with client authentication scheme ‘Anonymous’. The authentication header received from the server was ‘NTLM’ “

Problem

Clearly its an Authentication Issue. Then I found out that in Authentication Section of IIS, the Authentication Scheme for the service was Ntlm, whereas I was using Default Authentication Scheme in my HTTP Binding inside Configuration Information.

Solution

Instead of using the Default Authentication, I changed my Binding to use Ntlm Authentication.

So, instead of the following code,

BasicHttpBinding myBinding = new BasicHttpBinding();

myBinding.Name = "BasicHttpBinding_Service";
myBinding.Security.Mode = BasicHttpSecurityMode.None;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

I changed my code like below…

BasicHttpBinding myBinding = new BasicHttpBinding();

myBinding.Name = "BasicHttpBinding_Service";
myBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

And to my utter surprise, it started to work :). !!!

By Dibyasingh Tripathy
Senior Software Engineer @Team DynamicsCRM.
Mindfire Solutions

 

Tags: , , , , , , , , , , , , , , , ,

Calling external Web Service from a CRM 2011 Plug-in

Introduction

This blog explains how to call external Web Service from a CRM 2011 Plug-in.

Background

Sometimes we need to call a Web Service from a Plug-in. We had a similar requirement, but while trying to access the service, we got the following error.

“Could not find default endpoint element that references contract ‘ServiceReference.Service′ in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.”

Problem

The reason for this is because the configuration information for the Web Service from the client side is missing.

Using the Code

So, now in case of our plugin, we need to define the binding and endpoint information programmatically. Something like this…

using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using TestPluginConnectWebService.TestService;

namespace TestPluginConnectWebService
{
    public class TestConnectWebService : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            //Context = Info passed to the plugin at runtime
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            //Service = access to data for modification
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);

            // Adding Basic Http Binding and its properties.
            BasicHttpBinding myBinding = new BasicHttpBinding();
            myBinding.Name = "BasicHttpBinding_Service";
            myBinding.Security.Mode = BasicHttpSecurityMode.None;
            myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
            myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

            // Endpoint Address defining the asmx Service to be called.
            EndpointAddress endPointAddress = new EndpointAddress(@"http:///ReturnRecords/GetRecords.asmx");

            // Call to the Web Service using the Binding and End Point Address.
            GetGrantRecordsSoapClient myClient = new GetGrantRecordsSoapClient(myBinding, endPointAddress);

            var ab = myClient.GetGrants();

            if (ab != null)
            {
                 throw new InvalidPluginExecutionException("Success");
            }
            else
            {
                 throw new InvalidPluginExecutionException("Failure");
            }
        }
    }
}

This way we would be able to access our Web Service inside Plug-in.

Note

The following are some of the points that needs special attention.

Sandboxed Plug-ins can access network through the HTTP and HTTPS protocols. This capability provides support for accessing popular Web resources like Social Sites, News Feeds, Web services, and more.
The following Web access restrictions apply to this Sandbox capability.

  • Only the HTTP and HTTPS protocols are allowed.
  • Access to localhost (loopback) is not permitted.
  • IP addresses cannot be used. You must use a named Web address that requires DNS name resolution.

Reference – [MSDN] Plug-in Isolation, Trusts, and Statistics

By Dibyasingh Tripathy
Senior Software Engineer @Team DynamicsCRM.
Mindfire Solutions

 

Tags: , , , , , , , , , , , , , , , , , , , , , ,

Get the Created Records When Lead is Qualified Using Plug-in

Introduction

This blog explains how to register a plug-in in “QualifyLead” message and get the created record when lead is qualified.

Background

Let in some requirement, when a lead is qualified then if you need some custom logic to work then in this scenario you can register you plug-in on the “QualifyLead” message and “Post Operation” Stage.

Code

We can easily get the newly created account, contact and opportunity records and can do any required changes in them.

Codes that will be used inside the plug-in :

// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

// Getting the service from the Organisation Service.
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

// Get the qualified lead.
EntityReference leadid = (EntityReference)context.InputParameters["LeadId"];
Entity lead = service.Retrieve("lead", leadid.Id, new ColumnSet(true));

foreach (EntityReference created in (IEnumerable<object>)context.OutputParameters["CreatedEntities"])
{
    //Check if Account record is created when lead qualified.
    if (created.LogicalName == "account")
    {
        Entity account = service.Retrieve("account", created.Id, new ColumnSet(true));
    }
    
    //Check if Contact record is created when lead qualified.
    if (created.LogicalName == "contact")
    {
        Entity contact = service.Retrieve("contact", created.Id, new ColumnSet(true));
    }

    //Check if Opportunity record is created when lead qualified.
    if (created.LogicalName == "opportunity")
    {
        Entity opportunity = service.Retrieve("opportunity", created.Id, new ColumnSet(true));
    }
}

To update any of the created account, contact and opportunity we can use the retrieved data as per below :

Entity entAccount = new Entity("account");
entAccount["accountid"] = account.Attributes["accountid"];
entAccount["name"] = "Test Account Name";
service.Update(entAccount);

By Abhisek Sriramka
Senior Software Engineer @Team DynamicsCRM.
Mindfire Solutions

 
3 Comments

Posted by on April 29, 2013 in CRM Plug-in

 

Tags: , , , , , , , , , , , , , , ,

 
%d bloggers like this: