Tuesday, 25 June 2013

CRM 2011: Deploying More Than One Steps using Developer toolkit

Part One - Preparation

The general description of how to create and register a plugin with developer toolkit is discussed quite elaborately in msdn.

This post deals with how to deploy more than one steps of a using developer toolkit. Meaning how to design a plug in that will get triggered by more than one event. I will try to put as much sample codes and screen shot possible. Before going into it let us go through the terminology used throughout the post.

Event – Trigger Request, for example Create, Assign, Set State etc. I will be using create and update
Handler – Code snippet that handles an event.
Package Project – CRM 2011 package project that contains reference of all the other crm 2011 projects added to the solution.

At first open visual studio and open the solution you want to use. In this case I have used a blank solution. When the solution is loaded/created right click the solution in the solution explorer and then select add new project. On the search box type CRM the following list appears. Select “Dynamics CRM 2011 Package”, then name your project and then click OK.

For my understanding I always consider the package project as a register where the deployment information for the workflows and plugins are stored. I am sure there is a genius definition of CRM Package. But hey this blog is not for geniuses.

Secondly, right click the solution and click add project again and in the search box type crm.  But this time click “Dynamics CRM 2011 plug-in Library”. This will be the project where all the plug-in handlers for the solution will reside. Up to now we have not written a single line of code. And our solution explorer looks something like following


Now click on the CRM Explorer and then select the entity on which you want to create the handler. In our case we are using Invoice entity. Once the entity is selected, right click it and select Create Plug-in. A window that looks very similar to the screen for registering plug-in step appears. You need to select the Message as well as the correct pipeline stage. Notice that the name of the class on top right of the screen changes as you select the correct pipeline stage and message.


Since we are going to use the same the class on multiple events I have given a generic name to the class. When we click “Ok” button we will see in our plug-in project, a new class named “InvoiceHandler” got created. Also if we click on “RegisterFile.crmregister” in the package project we will see XML similar to the one given below. Notice that all the id fields are now empty guid. They will be replaced with the actual guid once the solution is deployed.  

<?xml version="1.0"?>
<Register xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/crm/2011/tools/pluginregistration">
  <Solutions>
    <Solution Assembly="TestSolution.Plugins1.dll" Id="00000000-0000-0000-0000-000000000000" IsolationMode="Sandbox" SourceType="Database">
      <PluginTypes>
        <Plugin Description="Plug-in to InvoiceHandler" FriendlyName="InvoiceHandler" Name="TestSolution.Plugins1.InvoiceHandler" Id="00000000-0000-0000-0000-000000000000" TypeName="TestSolution.Plugins1.InvoiceHandler">
          <Steps>
            <clear />
            <Step CustomConfiguration="" Name="InvoiceHandler" Description="Post-Operation of Invoice Create" Id="00000000-0000-0000-0000-000000000000" MessageName="Create" Mode="Synchronous" PrimaryEntityName="invoice" Rank="1" SecureConfiguration="" Stage="PostOutsideTransaction" SupportedDeployment="ServerOnly">
              <Images />
            </Step>
          </Steps>
          
        </Plugin>
      </PluginTypes>
    </Solution>
  </Solutions>
  <XamlWorkflows />
</Register>


This concludes our preparation part for the problem. On the next part I will discuss how to use this architecture to register more than one steps (and plug in if necessary) and deploy the solution in one go.


CRM 2011: To “Sandbox” or not to “Sandbox”

Yes I know - My poetry sucks... Today I came up with one of the most intriguing and at the same time foolish question. What does isolation mode in the plugin registration tool mean? The reason the question is intriguing is explained in the following paragraphs. The question becomes foolish because after few years of hardcore crm development this question never strike as something worth knowing.


Long story short - isolation mode while plugin registration defines if a plug in or a workflow is to be run securely in the server. It is also the only way you can register your plugin in crm online however I haven’t tested it. The two option that isolation mode can have are,

None: Meaning plugin/WF is running in unsecured way.
Sandbox: Plugin is running on high security.

Most of the articles you can find about debugging a plugin deal with how to debug plugin registered in normal isolation (None). I will quickly discuss some key reasons and key points of using sandbox isolation.
  • As I have mentioned before if you are working on CRM online then you can only register your plugin in Sandbox isolation. Also if you are using crm on premises but your crm endpoint is hosted is https I suggest you should use Sandbox.
  • Your sandbox plugin is secured which means you cannot access system, logs etc from a sandboxed plugin.
  • If your sandbox plugin becomes stuck (un-responsive) for any reason, the system (worker process) will be more brutal in chopping it off the queue.
  • Most importantly you can debug a sandbox plugin by attaching it to Microsoft.Crm.Sandbox.WorkerProcess.exe instead of normal w3p.exe or CrmAyncService.exe.

Don’t trust me? then check out Slinog or MSDN for more information.

Saturday, 22 June 2013

CRM 2011 : SOAP Logger

One of the best ways of creating FetchXml query is to use soap logger. It is way better to use stunware’s FetchXml designer if creating query is your objective. It is available in the crm sdk folder in the following folder <SDK folder>\sdk\samplecode\cs\client\soaplogger. when the solution is opened look for the a comments which looks like bellow,
//Add the code you want to test here:
//You must use the SoapLoggerOrganizationService 'slos' proxy rather than the IOrganizationService proxy you would normally use.


Add your code underneath the lines compile and run the program. if you are running the soap logger solution for the first time then the system will take you through the registration process. once the registration process

it will create output.txt in the bin directory which will have the soap action and fetchxml command with it. here's one example that i have created which gets some account contact information from crm,

//Add the code you want to test here:
// You must use the SoapLoggerOrganizationService 'slos' proxy rather than the IOrganizationService proxy you would normally use.
QueryExpression qe = new QueryExpression();
qe.EntityName = "account";
qe.ColumnSet = new ColumnSet();
qe.ColumnSet.Columns.Add("name");
qe.Criteria.AddCondition("accountid", ConditionOperator.Equal, "19C5970E-ABBC-E211-B7AD-3C4A92DBD85C");


qe.LinkEntities.Add(new LinkEntity("account", "contact", "primarycontactid", "contactid", JoinOperator.Inner));
qe.LinkEntities[0].Columns.AddColumns("firstname", "lastname");
qe.LinkEntities[0].EntityAlias = "primarycontact";

slos.RetrieveMultiple(qe);

And here's output of the query in the file.


HTTP REQUEST
--------------------------------------------------
POST https://serveraddress/XRMServices/2011/Organization.svc/web
Content-Type: text/xml; charset=utf-8
SOAPAction: http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/RetrieveMultiple

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <RetrieveMultiple xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <query i:type="a:QueryExpression" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:ColumnSet>
          <a:AllColumns>false</a:AllColumns>
          <a:Columns xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
            <b:string>name</b:string>
          </a:Columns>
        </a:ColumnSet>
        <a:Criteria>
          <a:Conditions>
            <a:ConditionExpression>
              <a:AttributeName>accountid</a:AttributeName>
              <a:Operator>Equal</a:Operator>
              <a:Values xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
                <b:anyType i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">19C5970E-ABBC-E211-B7AD-3C4A92DBD85C</b:anyType>
              </a:Values>
            </a:ConditionExpression>
          </a:Conditions>
          <a:FilterOperator>And</a:FilterOperator>
          <a:Filters />
        </a:Criteria>
        <a:Distinct>false</a:Distinct>
        <a:EntityName>account</a:EntityName>
        <a:LinkEntities>
          <a:LinkEntity>
            <a:Columns>
              <a:AllColumns>false</a:AllColumns>
              <a:Columns xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
                <b:string>firstname</b:string>
                <b:string>lastname</b:string>
              </a:Columns>
            </a:Columns>
            <a:EntityAlias>primarycontact</a:EntityAlias>
            <a:JoinOperator>Inner</a:JoinOperator>
            <a:LinkCriteria>
              <a:Conditions />
              <a:FilterOperator>And</a:FilterOperator>
              <a:Filters />
            </a:LinkCriteria>
            <a:LinkEntities />
            <a:LinkFromAttributeName>primarycontactid</a:LinkFromAttributeName>
            <a:LinkFromEntityName>account</a:LinkFromEntityName>
            <a:LinkToAttributeName>contactid</a:LinkToAttributeName>
            <a:LinkToEntityName>contact</a:LinkToEntityName>
          </a:LinkEntity>
        </a:LinkEntities>
        <a:Orders />
        <a:PageInfo>
          <a:Count>0</a:Count>
          <a:PageNumber>0</a:PageNumber>
          <a:PagingCookie i:nil="true" />
          <a:ReturnTotalRecordCount>false</a:ReturnTotalRecordCount>
        </a:PageInfo>
        <a:NoLock>false</a:NoLock>
      </query>
    </RetrieveMultiple>
  </s:Body>
</s:Envelope>
--------------------------------------------------

HTTP RESPONSE
--------------------------------------------------
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <RetrieveMultipleResponse xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <RetrieveMultipleResult xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:Entities>
          <a:Entity>
            <a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
              <a:KeyValuePairOfstringanyType>
                <b:key>name</b:key>
                <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">Adventure Works (sample)</b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>accountid</b:key>
                <b:value i:type="c:guid" xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/">19c5970e-abbc-e211-b7ad-3c4a92dbd85c</b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>primarycontact.firstname</b:key>
                <b:value i:type="a:AliasedValue">
                  <a:AttributeLogicalName>firstname</a:AttributeLogicalName>
                  <a:EntityLogicalName>contact</a:EntityLogicalName>
                  <a:Value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">Nancy</a:Value>
                </b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>primarycontact.lastname</b:key>
                <b:value i:type="a:AliasedValue">
                  <a:AttributeLogicalName>lastname</a:AttributeLogicalName>
                  <a:EntityLogicalName>contact</a:EntityLogicalName>
                  <a:Value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">Anderson (sample)</a:Value>
                </b:value>
              </a:KeyValuePairOfstringanyType>
            </a:Attributes>
            <a:EntityState i:nil="true" />
            <a:FormattedValues xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
            <a:Id>19c5970e-abbc-e211-b7ad-3c4a92dbd85c</a:Id>
            <a:LogicalName>account</a:LogicalName>
            <a:RelatedEntities xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
          </a:Entity>
        </a:Entities>
        <a:EntityName>account</a:EntityName>
        <a:MinActiveRowVersion>-1</a:MinActiveRowVersion>
        <a:MoreRecords>false</a:MoreRecords>
        <a:PagingCookie>&lt;cookie page="1"&gt;&lt;accountid last="{19C5970E-ABBC-E211-B7AD-3C4A92DBD85C}" first="{19C5970E-ABBC-E211-B7AD-3C4A92DBD85C}" /&gt;&lt;/cookie&gt;</a:PagingCookie>
        <a:TotalRecordCount>-1</a:TotalRecordCount>
        <a:TotalRecordCountLimitExceeded>false</a:TotalRecordCountLimitExceeded>
      </RetrieveMultipleResult>
    </RetrieveMultipleResponse>
  </s:Body>
</s:Envelope>
--------------------------------------------------



As i have mentioned before there are tools available online which can do all the above and some more. CRM itself has some sdk message that converts fetch xml to query expression and vise versa. One example of it can be found here.

Introduction to Fetch XML in CRM (Part 2)

A simple FetchXml

I did not know until recently that you can actually view your FetchXml through advance find. To do it you have to go to advance find of any record. Once in you need to select a new query. And as soon as you do that you’ll see ‘Download FetchXml’ button in top ribbon.

Now for just to get you guys started let’s do a very simple FetchXml. The following is a sample FetchXml which gets all the contacts in CRM,

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="contact">
    <attribute name="fullname" />
    <attribute name="telephone1" />
    <attribute name="contactid" />
    <order attribute="fullname" descending="false" />
  </entity>
</fetch>

The code itself is very much self explanatory. Entity name is the entity you are selecting. The attribute identifies the columns you are selecting. “Order” notes defines order by statement. The order by is default and not required. Here's almost similar query with 'and' and 'or' condition.



<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="contact">
    <attribute name="fullname" />
    <attribute name="telephone1" />
    <attribute name="contactid" />
    <order attribute="fullname" descending="false" />
    <filter type="and">
      <filter type="or">
        <filter type="and">
          <condition attribute="createdon" operator="on" value="2013-06-26" />
          <condition attribute="department" operator="eq" value="IT" />
        </filter>
        <condition attribute="firstname" operator="eq" value="John" />
      </filter>
    </filter>
  </entity>
</fetch>




So there you go, a very simplistic FetchXml from a very novice point of view. For more information you go to MSDN . Also Gareth Tucker’s Blog is ideal place for starters in FetchXml.


Introduction to Fetch XML in CRM


What I am about to discuss will probably be very primitive to most CRM developers. In my defense i can only say that the keyword here is “Introduction”. This topic is really going to be for absolute starters from a starter’s point of view.  If time permits I would like this topic to be extended into some parts in where I would like show how to use FetchXml in both server and client end, a glimpse of a code example based on JavaScript which i recently did .Enough said, now let’s start the party,



FetchXml: What, Why and When
FetchXml is an Xml query structure in MSCRM 2011 to retrieve data. Every data request in crm gets converted into one of these query and gets passed to the CRM services. CRM Service (Data / meta data) runs the query to the database and then returns an Xml response to the request. CRM then parses the XML response and puts the information into its place and displays the page. Since most of this process can be done in client end, the whole FetchXml business is visibly fast. It is also the only way to manipulate data for Crm Online solutions.


FetchXml is widely used in creating reports in CRM. You can also use FetchXml, to use in CRM form properties to to access and manage data.




Benefits:
As I have discussed earlier it is the most faster way to access and manipulate data. Through FetchXml, you can do almost all the things you can do in an average workflow.


You can integrate FetchXml with javascript. So it it is very light weight.




Shortcoming
The only improvement factor that i can think of about FetchXml now, is it involves a lot of coding. complicated queries will take even more lines of code which may lead to programmatic mistakes. There are some FetchXml query builder available in the net. Probably the most famous and acclaimed of them is called SwTools available in this web site . Before you dig into it let me remind you that this is a complete third party software so if you use it you pretty much on your own. The software which initially was build CRM 4.0, claims that it works with CRM 2011 as well. I have tried using it and it really works. However it only do retrieve commands (selects). As much as i know it does not do Non Queries. (Did i forget that you can perform Insert/Update/Delete through FetchXml?).

I prefer one that involves a slight more coding but developed by Microsoft. It is called SoapLogger which is available in CRM 2011 sdk. I will discuss about this in future posts.   

Tuesday, 12 July 2011

CRM 2011 custom workflow: Cancelling an Activity and Adding Note to it

After a lot of hardship i have come up with workflow which does a very straight forward thing. it looks for an activity with a certain subject line. when it finds the activity in question, if the activity has not been cancelled, it cancels it after adding a note to it. i have done it for all the activity types. to avoid duplication i am just sticking with the first two (task and email). Since i'm copy-pasting, there is a chance that there will be unmatched parenthesis.

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//my usings
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using System.Collections.ObjectModel;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Deployment;
using Microsoft.Xrm.Sdk.XmlNamespaces;
using System.Activities.Debugger;
using Microsoft.IdentityModel.Protocols.WSTrust;
namespace Don_Det_onUpdate
{
public sealed class DonarDetailsOnApproved : CodeActivity
{
[Input("Type of Activity")]
[RequiredArgument]
[Default("4212")]
[AttributeTarget("activitypointer","activitytypecode")]
public InArgument TypeOfActivity { get; set; }
[Input("Subject Line")]
[RequiredArgument]
public InArgument SubjectLine { get; set; }
protected override void Execute(CodeActivityContext context)
{
IWorkflowContext con = context.GetExtension();
IOrganizationServiceFactory objIOrganizationServiceFactory = context.GetExtension();
IOrganizationService service = objIOrganizationServiceFactory.CreateOrganizationService(con.UserId);
OptionSetValue ActType = TypeOfActivity.Get(context);
try
{
var crm = new XrmServiceContext(service);
Int16? ActivityId = Convert.ToInt16(ActType.Value);
// 07 july by imran
if (ActivityId.HasValue)
{
switch (ActivityId)
{
// this is a task...
case 4212:
var TaskList = crm.TaskSet.Where(t => t.Subject == SubjectLine.Get(context).ToString() && t.StateCode != TaskState.Canceled);
foreach (Task tsk in TaskList)
{
var note = new Annotation
{
Subject = "System generated note",
NoteText = "This activity is cancelled and no longer required.",
ObjectId = new EntityReference(tsk.LogicalName, tsk.Id),
ObjectTypeCode = tsk.GetType().ToString().ToLower()
};
crm.AddObject(note);
crm.UpdateObject(tsk);
crm.SaveChanges();
var k = new SetStateRequest
{
EntityMoniker = new EntityReference(tsk.LogicalName, tsk.Id),
State = new OptionSetValue(2),
Status = new OptionSetValue(-1)
};
crm.Execute(k);
break;
}
break;
case 4202: // email
var EmailList = crm.EmailSet.Where(t => t.Subject == SubjectLine.Get(context).ToString() && t.StateCode != EmailState.Canceled);
foreach (Email eml in EmailList)
{
var note = new Annotation
{
Subject = "System generated note",
NoteText = "This activity is cancelled and no longer required.",
ObjectId = new EntityReference(eml.LogicalName, eml.Id),
ObjectTypeCode = eml.GetType().ToString().ToLower()
};
crm.AddObject(note);
crm.UpdateObject(eml);
crm.SaveChanges();
var k = new SetStateRequest
{
EntityMoniker = new EntityReference(eml.LogicalName, eml.Id),
State = new OptionSetValue(2),
Status = new OptionSetValue(-1)
};
crm.Execute(k);
break;
}
    break;
   default:
break;
}
}
}
catch (Exception ex)
{
}
}
}
}         



      

the question that pops up as soon as you come across to the code. that how the hell you come across to the activitytype (which i used in switch). i am damn sure there are lots of smart ways to find them. but i debugged and looked for the value. (silly me...)

Sunday, 10 July 2011

System.Configuration.ConfigurationSettings.AppSettings’ is obsolete: ‘This method is obsolete, it has been replaced by System.Configuration!System.Con

one of the great ways to save machine/server specific info into application is to save it in app.config or web.config file. these config files traditionally stores connection informations to the database. But how about if you want to store some other things like, the location of input and output folder for files.
below is a code snippet of two file locations that i am storing into app.config file
as you can probably understand i have created two key value pairs named "Source" and "Assembly" which stores a file location of my computer. now question is how to get them in your .cs file? answer is, use "ConfigurationSettings" class under system. so if you want to the value "source" in .cs just type in this
string k = ConfigurationSettings.AppSettings["Source"].ToString();
but as soon as you type that in, if you are using .net 4.0 you are going to encounter the second problem. the line will be underlined in green which will say some thing like,
System.Configuration.ConfigurationSettings.AppSettings’ is obsolete: ‘This method is obsolete, it has been replaced by System.Configuration!System.Configuration.ConfigurationManager.AppSettings
so then you will likely be using "ConfigurationManager" instead. now you face the third problem. it will say, "ConfigurationManager does not exists in current context" [i do not have clue why this sentence cannot be some thing like this "We cannot find the damn thing. check your references"]
Since "ConfigurationSettings" came under intelli-sense without referencing anything, logical deduction was i will see "ConfigurationManager" in the same list since both of them belong to System.Configuration. i googled it a lot and found out that you have to manually reference System.ConfigurationManager into the application. "why on earth you have to do this for Manager and do not have to do it for settings" is something i yet to discover.
So once you reference "System.ConfigurationManager" all will become cake walk to you. and the final code for getting the value of "Source" will be,
source = ConfigurationManager.AppSettings["Source"].ToString();