Sunday, February 5, 2017

ILMerge workflows Dynamics CRM

If you are creating plugins or workflow that have dependencies on third party libraries then there are two options:
1. Deploy the third party DLL in the GAC
2. ILMerge the thrid party DLL to your workflow or plugin.

Option 1 might seem much easier at first as you can simply go and install the DLL in the GAC. You have to do it once and forget it. However you'll have to do it in each of your enviornment like DEV, Test and Prod.   This eventually becomes a nightmare when you have a big farm, deploying dll's to each of the server in a farm is tedious and error prone, specially say you start referencing a new version of dll.

Option 2 is better approach. In this option you can merge the third party DLL to your dll and deploy your solution. All your dependencies are then in the solution file and you really don't care how big your server farm is.

To do ILMerge you have to download ILMerge from Microsoft. In your plugin or worfklow project, go to project settings and Build Events and In the Post-Build event put the following code, note you might have to change it according to your project structure. In the below example, I have a SolutionItems folder and I have copied the ILMerge tool to this folder. Note also that I am pointing to the location of .net v4.5.2 as ILMerge will require some of the dependencies of the framework. The libraries I am ILMerging are Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll and I am outputing it to Workflow.dll which is my custom dll contianing workflow activities.

Yes I am merging 2 dlls with my workflow dll.
Note the double quotes as longer file paths and paths with space can break if quotes are not used.
The $(SolutionDir) etc are Visual studio macros, you can google them they are quite handy and using them means that if you have got a build system attached, you will not have to do anything extra.


"$(SolutionDir)$(SolutionName)\SolutionItems\ILMerge.exe"  /keyfile:"$(ProjectDir)key.snk" /ndebug:false /targetplatform:v4,"$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2" /target:"library" /copyattrs /out:Workflow.dll "$(TargetDir)Workflow.dll" "$(SolutionDir)$(SolutionName)\SolutionItems\Microsoft.SharePoint.Client.dll" "$(SolutionDir)$(SolutionName)\SolutionItems\Microsoft.SharePoint.Client.Runtime.dll"

It was quite tedious to work it out, but works beautifully.
Hope it saves someone some pain.

Sunday, December 13, 2015

Dynamics CRM report viewer problem in a load balanced (f5) environment

This is just a post which might help people who have deployed Dynamics CRM in a load balanced environment.

In our case, it was 3 front -ends and a report server also load balanced. CRM web site was up and running but when we tried to run any report we would get the following error. 404 WebResource.axd and ScriptResource.axd not found. On the CRM front end we received the error ": An error occurred processing a web or script resource request. The resource identifier failed to decrypt. "

The reason this was happening was that the CRM front end was not using sticky sessions and the request was not going to the same server every time.

To fix it on the F5 Big-IP we had to configure Persistance-type = Cookie which is aka sticky session.


Tuesday, November 12, 2013

Forefront Identity Manager (FIM) Custom Management Agent for CRM 2011 - Part 1

In this post I will share my experiences in developing a custom management agent for CRM 2011 in FIM. Since there is quite a bit to cover I have split this post into two parts, this being part 1.
Integration with FIM was quite a steep learning curve for me because I had little or not knowledge of how FIM works but after some playing around, reading articles, banging my head, i finally figured out and got something working. So to get started I will explain a little bit of what FIM does and what we are going to achieve by building a Management Agent for CRM 2011 in FIM.
FIM is the identity management solution from microsoft, but it does a whole lot of other stuff as well such as certificate management, self service password resets etc. It used to be called Identity Lifecycle manager and before that Microsoft Identity Integration server. It has four components
        • FIM Syncronization Service
        • FIM Service
        • FIM Portal 
        • FIM Certificate Management
I am not going to go into detail of all the components, I am just going to stick to what we really need for the purpose of creating a management agent for CRM 2011. You can read about the rest on MSDN and blogs etc. The FIM Synchronization Service is responsible for  passing identity information from one source to the other. This could be from a database such as Oracle  to AD, or from AD to other system such as the HR system or something else, in our case CRM 2011. FIM calls them connected data source or CDS. FIM Sync service can run by itself without the need for other components. To consume data from the CDS's FIM uses adapters which it calls Management Agents (MA). Some MA come pre packed with FIM such as for Active Directory, SQL, Flat Files, Oracle, SAP etc. What we don't have is a MA for CRM 2011, but this is quite straight forward to develop once you know how FIM works. FIM allows developers to create what it calls Extensible Connectivity Management Agent (ECMA) 2 which basically is some .net code that implements interfaces that FIM provides.

Implementation is good but understanding how FIM Sync service stores and pushes data between systems is in my mind critical to understanding how to create a Management Agent for CRM 2011. For this I refer you to these technet articles 1 , 2, 3 which explain the FIM Sync Service inner workings. After reading them you would know that FIM Sync service stores data in "Connector Space" and then pushes it to the "Metaverse". It is from the Metaverse that data is pushed to external system. In short inbound synchronization is populating authoritative data in the metaverse and outbound synchronization is populating from metaverse to external systems.
The msdn articles above also talk about FIM Service and Portal and use what is called declarative synchronization which is configured through the portal and FIM Service. I am going to show you  non-declarative synchronization in which we will be writing code.

What I wanted to achieve using FIM sync service was to automate the user creation process in CRM 2011, i.e. whenever a new user is created in Active Directory
  • it is added as a user in CRM 2011
  • put in appropriate business unit or team
  • get appropriate security roles
  • is disabled as soon as the the user is deleted in AD or account is disabled
This can be extended to whatever degree one wishes to, for example rules can be applied which can add / remove user to teams within CRM based on group membership in AD or SQL or some other system. The whole goal is automation and keeping all the systems in sync all the time without manual intervention.

Now that we have some background knowledge on FIM and our end goal, lets break down the steps that are involved.

  1. Create MA for Active Directory (this is out of box)
  2. Import Data (i.e. populate metaverse)
  3. Create custom MA for CRM
    1. Map attributes
  4. Create metaverse Rules extension


The first thing we have to do is create an MA for Active Directory. This will allow us to pull data from AD into the FIM sync service data base (i.e. the metaverse). Once the data is there we will be ready to export it to our external system aka CRM 2011.
To create a MA for AD, on the MA tab click "Create", This will bring up the "Create Management Agent" box.

Next type in the active directory details, in my case it is contoso.com.
Next select the AD containers, in my case, it is ECMA2, which is an OU that I created specifically for testing purposes, you could leave it empty and that would select everything.
In the next screen, select the AD objects, ensuring that you select "user".

In the next screen, you need to select the AD attributes as shown below. It is an extensive list of attributes so you really should know which ones are needed, in my case since it was just an example, I am selecting only a few attributes such as the sAMAccountName which corresponds to your AD login.

In the screen "Connector Filter" just hit next as we are not going to specify any filters. In the "Join and Projection Rules" project "user" as shown below.
In the "Configure Attribute flow" map the attributes, here you will be mapping the AD attributes to the metaverse attributes. Since we are only interested in users we will be mapping "user" attributes to metaverse's "person" attributes as shown below.
Click next and in the Deprovisioning just select "make them disconnectors". Finally click okay.
This will create your AD MA. Now we will move onto creating the custom (aka ECMA 2) MA for CRM 2011.

To create the CRM MA we need to create a c# project, assuming that you have installed the FIM syncronization service, all you need to do is, go to  "Actions" -> Create Extension Projects ->  Extensible Connectivity 2.0 Extension

This will bring up a box to provide name of the project and select the type of project and Visual Studio version that you want to use, just select 2010.  The project created will contain a class with all the interfaces commented out as shown below:
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections.Specialized;
using Microsoft.MetadirectoryServices;

namespace FimSync_Ezma
{
    public class EzmaExtension :
    //IMAExtensible2CallExport,
    //IMAExtensible2CallImport,
    //IMAExtensible2FileImport,
    //IMAExtensible2FileExport,
    //IMAExtensible2GetHierarchy,
    //IMAExtensible2GetSchema,
    //IMAExtensible2GetCapabilities,
    //IMAExtensible2GetParameters,
    //IMAExtensible2GetPartitions
    {
        //
        // Constructor
        //
        public EzmaExtension()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    };
}


We will need to uncomment IMAExtensible2CallExport, IMAExtensible2GetSchema, IMAExtensible2GetCapabilities, IMAExtensible2GetParameters, next we will have to right click and get Visual Studio to implement these interfaces for us.
The remainder will be covered in part 2 where we will writing the acutal code to create users and also implement the metaverse rules extension to populate the metaverse.
Till next time, Happy CRMing!



Thursday, November 7, 2013

Embedding images in email, Dynamics CRM 2011 - Part 1

Today I am going to show you a way you can send emails with embedded images in CRM 2011 /2013 and for that matter Crm 4.0.
This is nothing new, you might say... images can be added by just copying the image which is available on a public URL and pasting it in the CRM email form and the image will appear.
If you thought this then you are definitely correct. However what if you had a requirement where the images could not be put on a public URL ( or CDN), how would you allow images then.
Well as a smart CRM developer you would say, mail merge? or have the email contain a document as attachment with the image, etc. etc. There are many ways it can be done.

Okay enough of the daddy talk, let me tell you another way which many of you probably already knew. Lets revisit emails and more importantly the MIME content disposition specification which details how email attachments are constructed and which governs how the email clients deal with your email. Essentially what this will tell you is that you can embed images inside the email if you create the attachment as "inline" and reference it is your email body.
To achieve in CRM, we will somehow need to interact with our outgoing email just before it is submitted to the SMTP server for delivery. We will have to convert our images to "inline" attachments and reference them in the email body so that on the other side they come out as email which contain images.

Now the fun part, To implement this we won't be doing adding anything extra to our CRM email form, we will still put the image in the email body as described above - copy -paste, but this time our image is no longer required to be on the a public URL or CDN, it can remain in our internal network. Our code will grab it form our internal network, convert it into a MIME inline attachment and reference it in the email body.

But where and how will we do this you ask? Well the plain old router, yes its the same router that we are all too used to, this time we will be extending the SMTP provider, specifically we will be creating a class that implements  SmtpPollingSendEmailProvider. We have to do this inorder to construct the email ourselves, replace the img tag with the equivalent mime attachment reference and also convert the image to an inline mime attachment.
We will override the Run method, grab the email that needs to be sent and use System.Net.Mail to construct an email, along with the attachment which we will read from the <img src=url>  and then send it through our configured smtp server.

That's it voila! email with embedded image.
In the next part I will share the code along with steps to extend the router to achieve this.

Happy CRMing.

Disclaimer: This method of embedding images is probably not the best performing option, your first choice should always be images that are on a CDN.


Tuesday, May 21, 2013

CRM 2011 Installation files for Windows server 2012

Installation of Crm 2011 on windows server 2012 is now fully supported after the release of rollup 13. The only thing you need is the updated installation files and patch for Windows Server 2012.
The updated installation file can be downloaded from http://www.microsoft.com/en-us/download/details.aspx?id=27822 this was published on 1/20/2012.
Next you will need the patch for Windows Server 2012,This will be downloaded automatically if you select Get Updates during the installation, however in case your server does not have internet access then you will have to manually download the files. These can be hard to find as I discovered but looking hard enough you can figure it out.
Just to save people time  here is the link to the updated installation files. Go to the Microsoft Update catalogue site http://catalog.update.microsoft.com/v7/site/home.aspx
Do a search for CRM.

After downloading the update patch files, you will need to copy them to a location on your server.  Note you only need to copy the file relevant to you language in English's case this is 1033. After than you will need to start the installer using command line and specify the location of the patch files in the xml element.
<Patch update="true">\\vmware-host\Shared Folders\Downloads\crm patch\Server_KB2434455_amd64_1033.msp</Patch>

Now when you run the installation from the command line using the XML config file, you will be easily able to install CRM on Windows server 2012 and SQL Server 2012.
Hope this saves some time.

In the next post I will shed some light on using CRM 2011 on windows server 2012

Happy CRMing!

Tuesday, April 17, 2012

Changing the reply email address to the queue's email address when replying from a queue in crm 2011

The problem was that we have multiple queues with email addresses in crm 2011. When an email arrives it goes to a particular queue depending on the address that was used. Now what happens when you try to reply to that email? Well nothing much only that the "FROM address" lookup defaults to the currently logged on user instead of defaulting to the queue's email address.
There are various ways you can solve this issue by writing plugin or workflow custom activity etc, but the crux of the matter is that the crm user replying from a queue needs to the see what the from email address is.
This can be best achieved by using Java script.
Please note that the code below is an unsupported customization, but it works.
function CheckEnquiryReplyAddress() {
 // Only complete this validate on Create Form
 var formType = Xrm.Page.ui.getFormType();
 var emailStatus = GetAttributeValue("statecode");
                    var emailDirection = GetAttributeValue("directioncode");



 if (formType == 1 || (formType == 2 && emailStatus == "Open")) {  
                                             
                                       
  if (emailDirection == "1"){
                                                              var previousEmailId=getExtraqsParam("_InReplyToId", window.parent.location.search);

   //getting context from the parent window
   var context = Xrm.Page.context;

   try {
    var serverUrl = context.getServerUrl();
    //The XRM OData end-point
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
                                                                                     var query="/EmailSet?$select=ActivityId,ActivityTypeCode,DirectionCode,";
                                                                                     query=query+"ToRecipients,Email_QueueItem/QueueId&$expand=Email_QueueItem&$filter=ActivityId eq guid'" + previousEmailId +"'";
    query =serverUrl+ODATA_ENDPOINT+ query;

    var request= new XMLHttpRequest();
    request.open("GET", query, false);
    request.setRequestHeader("Accept", "application/json");
    request.setRequestHeader("Content-Type", "application/json; charset=utf-8"); 
    request.onreadystatechange=function(){ CompleteEnquiryReplyCheck(request,serverUrl);}
    request.send(null);
   }
   catch(e) {
    alert(e.Description);
   }
  }
 }

          
}
function CompleteEnquiryReplyCheck(request,url)
{
 if (request.readyState==4) {
  if(request.status==200) {
   var queue=JSON.parse(request.responseText).d.results[0];
   
   if (queue != null) {
    var queueId = queue.Email_QueueItem.results[0].QueueId.Id;
    var lookup = new Array();
      var lookupItem = new Object();
  
    lookupItem.id = queueId;
    lookupItem.name = queue.Email_QueueItem.results[0].QueueId.Name;
    lookupItem.typename = "queue";
     
    lookup[0] = lookupItem;
  
    Xrm.Page.getAttribute("from").setValue(lookup);
   }
  }
    }
}
The key here is the _InReplyToId query string parameter from here we are able to query the oData service and get the "To" email address of the original email and then it is simply a matter of putting the "To" address in the "From" lookup. Also note that I am checking that this is an outgoing email. Find the code for getExtraqsParam here. Happy Crming!.

Monday, April 16, 2012

Executing CRM 2011 workflow via Javascript

To execute a workflow we have to use the soap web service. We cannot use oData service. So we have to create a soap envelope and post it to the organization web service. The following function will execute the workflow.
function ExecuteWorkFlow(entityId,workflowId,url)
{

    var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
    url = url + OrgServicePath;
    var request;
    request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
  "<s:Body>" +
    "<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">"+
      "<request i:type=\"b:ExecuteWorkflowRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">"+
        "<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">"+
          "<a:KeyValuePairOfstringanyType>"+
            "<c:key>EntityId</c:key>"+
            "<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">"+ entityId+"</c:value>"+
          "</a:KeyValuePairOfstringanyType>"+
          "<a:KeyValuePairOfstringanyType>"+
            "<c:key>WorkflowId</c:key>"+
            "<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">"+ workflowId +"</c:value>"+
          "</a:KeyValuePairOfstringanyType>"+
        "</a:Parameters>"+
        "<a:RequestId i:nil=\"true\" />"+
        "<a:RequestName>ExecuteWorkflow</a:RequestName>"+
      "</request>"+
    "</Execute>"+
  "</s:Body>"+
"</s:Envelope>";

  var req = new XMLHttpRequest();
  req.open("POST", url, true)
  // Responses will return XML. It isn't possible to return JSON.
  req.setRequestHeader("Accept", "application/xml, text/xml, */*");
  req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
  req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
  req.onreadystatechange = function () { assignResponse(req); };
  req.send(request);
  

}

function assignResponse(req) {
if (req.readyState == 4) {
    if (req.status == 200) {
        alert('successfully executed the workflow');
   }
   }
}

Happying Coding!

Accessing HTML web resource funcitons from entity Form in CRM 2011

I found this piece of code really handy. If you have a web resource that you place inside a form and if you want to manipulate the html within that html web resource you can do it.
Xrm.Page.getControl("WebResource_YourWebResource").getObject().contentWindow.window.WebResourceFunction
The function WebResourceFunction sits inside the HTML web resource and can do anything.
An example of how I used it was to call the HTML web resource function on the onchange event of a lookup and display something different for different values in the lookup box. Oh and yes you can pass parameters to the function as well.
To be fair, I found this technique on some other blog, but I can reference it since I lost the address and can't find it any more.
Happy Coding!

Tuesday, April 3, 2012

Parsing query string values in the extraqs parameter

If you want to get values of query string parameters that are inside the extraqs query string field in your crm form then you can call the getExtraqsParam function. It takes two values: they key is the param whose value you want to get to and the query is the actual query string which you can find through window.location.search property. Note that that window.locaiton.search only returns the query string part of the url.
 function getExtraqsParam(key,query)
        {   //Get the any query string parameters and load them
            //into the vals array

            var vals = new Array();
           
                vals = query.substr(1).split("&");
                for (var i in vals)
                {
                    vals[i] = vals[i].replace(/\+/g, " ").split("=");
                }
                //look for the parameter named 'extraqs'
                var found = false;
                var returnVal=null;
                for (var i in vals)
                {

                    if (vals[i][0].toLowerCase() == "extraqs")
                    {
                       
                         
                         returnVal= vals[i][1];

                          break;
                    }
                }

                return parseExtraqs(key,returnVal);                     

        }

function parseExtraqs(key,val)
{

    var returnVal;
    var vals;
    vals=decodeURIComponent(val).split("&");
    for (var i in vals)
     {
         vals[i] = vals[i].replace(/\+/g, " ").split("=");
         if (vals[i][0]==key)
        returnVal=decodeURIComponent(vals[i][1]);
     }
return returnVal;

}
Happy Crming!

Saturday, December 24, 2011

Loading JSON in your CRM ribbon

Add an enable rule in your customizations.xml and put the following
  <EnableRules>
            <EnableRule Id="Atif.new_customentity.WebClient.EnableRule">
              <CrmClientTypeRule Type="Web" />
              <CustomRule Library="$webresource:agd_/scripts/json2.js" FunctionName="isNAN" Default="true"></CustomRule>
            </EnableRule>
          </EnableRules></EnableRules>

Next in your Command Definition you need to put the display rule defined above.
  <CommandDefinition Id="Atif.new_customentity.SubGrid.AddSecondAssessment.Command">
            <EnableRules>
              <EnableRule Id="Atif.new_customentity.WebClient.EnableRule" />
            </EnableRules>
            <DisplayRules>
              <DisplayRule Id="Atif.new_customentity.WebClient.DisplayRule" />
            </DisplayRules>
            <Actions>
              <JavaScriptFunction Library="$webresource:agd_/scripts/Ribbonfunctions.js" FunctionName="AddSecondAssessment">
                <CrmParameter Value="FirstPrimaryItemId" />
                <CrmParameter Value="OrgName" />
              </JavaScriptFunction>
            </Actions>
          </CommandDefinition>
        </CommandDefinitions>
Now when your ribbon loads JSON will be loaded as well and you can use it in any of your custom ribbon javascript functions.

Happy CRMing!

Friday, December 23, 2011

How to stop CRM form from saving

I had to write some validation code on the OnSave event of the form and if the validation code was not successful I needed to cancel the save. To accomplish this you have to use JavaScript and you need to pass context to your javascript function.
If your business logic fails you need to call this function.
ExecutionObj.getEventArgs().preventDefault();
Where ExecutionObj is the first parameter of the function (i.e. context object).

Cheers

Thursday, December 22, 2011

How to get OptionSet value inside a plugin

 OptionSetValue oValue = (OptionSetValue)entity.Attributes["new_myentity"];
                        int optionSetValue = oValue.Value;
 var pOptionMetadata = optionList.Where(c => c.Value == optionSetValue).FirstOrDefault();
 string myOptionSetText=pOptionMetadata .Label.LocalizedLabels[0].Label

    public OptionMetadata[] GetOptionMetadata(string logicalEntityName, string optionSetAttributeName, IOrganizationService service)
        {
            RetrieveAttributeRequest retrieveAttributeRequest =
                                         new RetrieveAttributeRequest
                                         {
                                             EntityLogicalName = logicalEntityName,
                                             LogicalName = optionSetAttributeName,
                                             RetrieveAsIfPublished = true
                                         };

            // Execute the request.
            RetrieveAttributeResponse retrieveAttributeResponse =
                (RetrieveAttributeResponse)service.Execute(
                retrieveAttributeRequest);

            // Access the retrieved attribute.
            PicklistAttributeMetadata retrievedPicklistAttributeMetadata =
                (PicklistAttributeMetadata)
                retrieveAttributeResponse.AttributeMetadata;

            // Get the current options list for the retrieved attribute.
            OptionMetadata[] optionList =
                retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();

            return optionList;
        }

Wednesday, December 21, 2011

Auto number solution for Dynamics CRM 2011

Today I am going to show you how to write an auto number plugin for CRM 2011.
Here are the aims of this plugin
  • First and foremost it should be able to generate auto numbers
  • Can be used for multiple entities
  • Can be ported to multiple organisations
  • Should be configurable from the interface
    • We should be able to change the starting number
    • We should be able to change the increment number
    • We should be able to change the prefix/suffix and add any separators
To generate the auto number we are first going to create an auto number entity. This entity will hold all our autonumbers as well as any formatting.
Auto Number Entity
As you can see from the screen shot the auto number entity contains the following fields:
Entity Name: this would hold the name of the entity where you want to apply the auto number to
Entify Autonumber Field: this is the field of the Entity that would show and hold the autonumber.
Prefix, Prefix Separator, Suffix and Suffix Separator are self explanatory.
Increment unit: It is the increment number for the auto number field.
Counter: It is the starting number for the auto number field and it will subsequently show the incremented number as updated in the plugin.
Number formatter: This is a formatter that will be applied to the number field (i.e. counter) so for example the counter is 544 and the formatter is 0000 that will end up being 0544.

Okay so now that we have our auto number entity set up, we need to write the plugin.

public class AutoNumberPlugin:IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)
                serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
            if (context.InputParameters.Contains("Target") &&
            context.InputParameters["Target"] is Entity)
            {
                Entity entity = (Entity)context.InputParameters["Target"];

                IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

                string fetchXml = @"<fetch mapping='logical'> 
                                            <entity name='agd_autonumber'><all-attributes/>
                                                <filter type=""and"">
                                                        <condition attribute=""agd_entityname"" operator=""eq"" value='" + entity.LogicalName + "'" + " /></filter></entity></fetch>";
                System.Threading.Mutex mtx = null;

                try
                {                
                 
                    string mutextName = string.Format("{0}{1}", entity.LogicalName, "Autonumber");
                    mtx = new System.Threading.Mutex(false, mutextName);

                    mtx.WaitOne();
                    //get hold on the mutex and only release it after update was done ot the agd_counter entity
                    // not sure if this will work in a multi server environment

                   EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetchXml));
                    string nextIncrementNumber = string.Empty;
                    if (result.Entities.Count == 1)
                    {
                        //retrieve the counter
                        Entity autoNumber = result.Entities[0];
                        if (!autoNumber.Attributes.Contains("agd_counter"))
                            throw new InvalidPluginExecutionException("agd_counter must contain a value");
                        if (!autoNumber.Attributes.Contains("agd_incrementunit"))
                            throw new InvalidPluginExecutionException("agd_incrementunit must contain a value");

                        int counter = Int32.Parse(autoNumber.Attributes["agd_counter"].ToString());
                        int incrementUnit = Int32.Parse(autoNumber.Attributes["agd_incrementunit"].ToString());
                        string prefix = autoNumber.Attributes.Contains("agd_prefix") ? autoNumber.Attributes["agd_prefix"].ToString() : string.Empty;
                        string prefixSeparator = autoNumber.Attributes.Contains("agd_prefixseparator") ? autoNumber.Attributes["agd_prefixseparator"].ToString() : string.Empty;
                        string suffix = autoNumber.Attributes.Contains("agd_suffix") ? autoNumber.Attributes["agd_suffix"].ToString() : string.Empty;
                        string suffixseparator = autoNumber.Attributes.Contains("agd_suffixseparator") ? autoNumber.Attributes["agd_suffixseparator"].ToString() : string.Empty;
                        string numberFormatter = autoNumber.Attributes.Contains("agd_numberformatter") ? autoNumber.Attributes["agd_numberformatter"].ToString() : string.Empty;
                        string fieldToUpdate;
                        if (autoNumber.Attributes.Contains("agd_entityautonumberfield"))
                            fieldToUpdate = autoNumber.Attributes["agd_entityautonumberfield"].ToString();
                        else
                            throw new InvalidPluginExecutionException("agd_entityautonumberfield should not be emplty");

                        nextIncrementNumber = BuildAutoNumber(prefix, prefixSeparator,
                            suffix, suffixseparator, counter, incrementUnit, numberFormatter);

                        entity.Attributes[fieldToUpdate] = nextIncrementNumber;
                        service.Update(entity);
                        //increment the autonumber entity
                        //and update it to record the counter

                        autoNumber.Attributes["agd_counter"] = counter + incrementUnit;
                        service.Update(autoNumber);
                    }
                }
                catch (Exception ex)
                {
                    if (mtx != null)
                    {
                        mtx.ReleaseMutex();
                        mtx = null;
                    }

                    throw new InvalidPluginExecutionException("An error occured in Autonumber plugin", ex);

                }
                finally
                {
                    if (mtx != null)
                        mtx.ReleaseMutex();
                }





            }
        }

        private string BuildAutoNumber(string prefix, string prefixSeparator, string suffix, string suffixSeparator, int counter, int incrementUnit, string numberFormatter)
        {
            bool hasPrefix = false, hasSuffix = false;

            string returnNumber = string.Empty;

            if (!string.IsNullOrEmpty(prefix))
            {
                hasPrefix = true;
            }
            if (!string.IsNullOrEmpty(suffix))
            {
                hasSuffix = true;
            }
            counter = counter + incrementUnit;
            returnNumber = (hasPrefix ? prefix + prefixSeparator : "") + counter.ToString(numberFormatter) + (hasSuffix ? suffix + suffixSeparator : "");


            return returnNumber;


        }
Some notes about the code above, I've used fetch XML to get the fields from the auto number entity. You can use LINQ as well if you generated strongly typed clasess via CRMSvcUtil tool in the SDK. I am using late bound entities so that it is easier to change later on without the need to regenerate strongly typed classes.
I am also using a mutex, this is there to ensure that concurrent create requests cannot occur simultaneously, it's not the best solution but it works.

The above plugin code will generate the increment number, but before that can happen we have to register the plugin using the plugin registeration tool. We need to register the assembly and the create a step for the entity where we want to run this auto number plugin on. We have to register it on the post-create step for the "Create" event of  your custom entity. The registeration tool can be found in the SDK\tools directory.
Thats it! your auto number is ready to go. Notice that this plugin can service multiple entities and can be easily ported to any organisation, also it can be customized from the interface and prefixes and suffixes can be added / changed.

Sunday, November 6, 2011

CRM 2011 opening forms using URL

This is a very neat feature in CRM. Basically all entity forms can be accessed by using main.aspx and some pre definied query string parameters.
So for example your organisation is called constoso you can access any entity form url using the syntax
http://<yourserver>/contoso/main.aspx?etn=<yourEntity>&pagetype=entityrecord
note the two query string parameters etn and pagetype. These are pre defined parameters, this is not something I have come up with.
Here are the list of all possible parameters you can pass (from msdn)
  • etn -entity logical name
  • extraqs -optional parameters that can set values in a form.
  • pagetype - one of two values entityrecord or entitylist
  • id- optional for forms when you want to open a specific record
  • viewid - id for saved query or user query.
  • viewtype - 1039 for saved query and  4230 for user query.
For more detailed information refer to msdn
This is a very handy function. You can place button on forms and open up other forms or call it from your web resource such as asp.net or html page or silverlight or even add your custom buttons to the ribbon and open forms with prefilled values.

In my next post I will show you how to populate form values using query string parameters in the url.
The tricky one is populating the lookups.


Happy CRMing!

Thursday, November 3, 2011

Getting attibute value in CRM 2011


This is a generic function which will get you value of any attribute on the form.
Only two things you need to remember, for an optionset it will get the text for the selected option set and for a lookup it will get the first value in the lookup. So it will not get you values for thesystem lookups which can have multiple values.
function GetAttributeValue(attribute) {

    var attrib = Xrm.Page.data.entity.attributes.get(attribute);
    var type = attrib.getAttributeType(); 
    var value;
    switch (type) {
        case "boolean":
        case "decimal":
        case "double":
        case "integer":
        case "memo":
        case "string":
        case "money":
            value = attrib.getValue();
            break;
        case "optionset":
            value = attrib.getSelectionOption().text;
            break;
        case "datetime":
            value = attrib.getValue().toLocaleString();
            break;
        case "lookup":
            var lookupArray = attrib.getValue();
            value = lookupArray[0].name;
            break;
        default:
            value = null;
            break;
    }

    return value;


}
Happy CRMing!

Tuesday, May 31, 2011

CRM 2011 List Component does not work

I had a lot of trouble getting the list component to work on my Dev Machine, I eventually managed to fix the problem, so in the hope that this might help someone please try some of the following steps:
  • Ensure that CRM is running under a domain account with access to the sharepoint site where you plan to install the CRM List Component, see my other post
  • Try creating a new Sharepoint Web application
  • Install the List Component.WSP in the Sharepoint site. Go to site settings -> Galleries -> solutions. Activate the solution. This one was hard to solve. Try the following:
    • Ensure Sharepoint Foundation Sandboxed code service is running ( found user central admin -> System Settings -> Manage servies on the server.
    • In Manage Applications, select your application - select General Settings (ribbon), make sure that Browser file handling is set to permissive.
    • reset iis
    • if you get the error that user code service is too busy, restart the service and try again.
    • Finally and this one was the hardest, edit your hosts file located at system32\etc. add the following entry 127.0.0.1 crl.microsoft.com. I found the soluation here
That's all i can think of now. Good Luck!

Thursday, May 19, 2011

The user Id is invalid. at Microsoft.Crm.BusinessEntities

Make sure that the user which is running the application pool for the CRM application is added to the following groups
 PrivUserGroup
CRM_WPG
also make sure the account has access to sql server in sysadmin role

CRM 2011 Error Sandbox Host - Access Denied

Okay for those of you who have stumbled upon this issue when changing Dynamics CRM to run under a custom account then here is hte solution:
Your account should be a domain user account with the following permissions as recommended by Micrsoft:
  • Domain User membership.

  • That account must be granted the Logon as service permission in the Local Security Policy.

    • If you add the account as a member of local administrator then that should be sufficient.
  • Folder read and write permission on the \Trace, by default located under \Program Files\Microsoft  Dynamics CRM\Trace, and user account %AppData% folders on the local computer.

  • Read permission to the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM subkey in the Windows Registry.

  • The service account may need an SPN for the URL used to access the Web site that is associated with it. To set the SPN for the Sandbox Processing Service account, run the following command at a command prompt on the computer where the service is running.
    SETSPN –a MSCRMSandboxService/<ComputerName> <service account>


  • If you want to change your Dynamics CRM web app to run under this account  then you will need to do some more stuff
  • The account should be in the sysadmin group of the SQL Server where your crm database is located.

  • The account should be in the SQLAccessAGroup

  • Add to CRM_WPG group

  • Add to PrivUserGroup (this is usually missed)


  • Hope this helps someone out there.



    Saturday, April 16, 2011

    What's this XRM?

    Before we go onto what XRM is lets first look at what a CRM is. Straight out of Wikipedia "Customer relationship management (CRM) is a widely-implemented strategy for managing a company’s interactions with customers, clients and sales prospects. It involves using technology to organize, automate, and synchronize business processes—principally sales activities, but also those for marketing, customer service, and technical support. The overall goals are to find, attract, and win new clients, nurture and retain those the company already has, entice former clients back into the fold, and reduce the costs of marketing and client service.Customer relationship management describes a company-wide business strategy including customer-interface departments as well as other departments"
    So now that we know what a CRM is, lets see what Dynamics CRM offers and why Microsoft is promoting the term XRM (Extended Relationship Management).
    Out of the box dynamics offers typical CRM functionality such as marketing, leads, sales, correspondence (emails, phone, fax) etc, but the difference is that you have the ability to customize all this. You can build your own custom entities which can capture anything for example you could create an entity called Application, add a few fields to it and there you go you'll have your own application entity which you can use to enter in details about a particular application (such as application for a job or a tender response), the possibilities are limitless. On top of this you have the ability to create relationships between these entities, so this would mean an create sometime really complex with many entities related to each other. Its just like creating a database within SQL Server of MySQL, but with one difference you straight away get your forms to enter data, you can create reports by a few clicks and you can even secure your entities in quite a number of ways, and to top this off you can create workflows that contain business rules for your entities and if you fancy yourself you can also create .net plugins to extend your xRM further. The XRM also exposes web services so other programs can hook into it, not to mention the Sharepoint and Outlook integration that comes out of the box. So there you are, that's what Microsoft mean by XRM.

    Wednesday, April 13, 2011

    CRM for a .net developer

    Okay so I've always been interested in buzz words as they come out, Sharepoint was once that, then came Dynamics and they jump back and forth with every new version of the software released. To be honest Microsoft did a pretty good job with Sharepoint 2010, specially the support they have provided within visual studio for it.k Anyway comming to the point of this post, I come from a pure developer background c# .net, asp.net etc etc...all the cool microsoft technology stack, but then one day I got a chance to have a look at Dynamics 2011 throught he 30 day online trial. I played around with it and jeez! it was good, really good. The stuff you could do by just pointing and clicking was awesome. Why build an asp.net app, a database and deploy it when you can just do that in 10 mins with dynamics.
    Hence my interest in dynamics, plus the integration with .net and sql server means that it is extendable and you can even write code to do a lot of cool things (i.e. if you want to).
    So this blog is going to be about the things i discover in dynamics as i learn it with the hope that it might benefit someone out there who might also be interested.