Introduction
In this artcle i will try to explain the basic concepts to a novice MS CRM developer who wants to customize Microsoft CRM 3.0. This article consists of external referances and code samples those are provided with the permission of the copyright holders' permission. If you will get quotations from this article please provide the original link as the referance info in your material. Outline is as follows:
o What is a callout? Where do them used?
o Which events trigger callouts?
o A simple case example: Validator For Turkish Citizen Number (=TCKN)
Background
Having a strong C# programming knowledge is an asset to benefit from this article. First i will try to explain basic concepts for callout programming for MS CRM 3.0.
What are callouts?
Callouts are simply class libraries those are compiled using .NET Framework (=fw) 1.1.
First question in our minds is: Can we write callouts using fw2.0 or fw3.0 ?
Unfortunately NO! Since MS CRM 3.0 is written using fw 1.1 and callout assemblies are loaded in the same application pool with crm, there is a limitation in executing only one framework in one application pool (=app pool), crm application pool uses fw1.1 and you cannot load an assembly that requires fw2.0 or above in that app pool. So we have to compile our callout code in fw1.1.
How to generate fw1.1 code?
There are two ways to generate fw1.1 code.
First is the conventional way, we use Visual Studio.NET (=VSNET) 2003, we write our code in the IDE and hit F5 to generate the compiled code.
Second is a bit compilcated, you may utilize VSNET 2005 and fw1.1 to generate fw1.1 assemblies. However this technique requires msbee setup and having a proper config file. Then you only need to write an msbuild command in the VSNET command prompt to generate fw1.1 assembly.
Hence this is a bit more complicated i will explain the issue in another article.
Callouts are used to extend MS CRM 3.0 features. Mostly in integration projects callouts are used to provide data integrity, and consistance. For instance you dont want to save two people having the same employee number, however by default ms crm allows having two records with the same employee number. Just at this point you may write a callout and it checks the existance of the employee number, if that number exists aborts the save process, otherwise makes the process continue the usual way.
Importance of CrmCalloutBase Class
This class resides in the microsoft.crm.platform.callout.base.dll file which is located in the system folder of the Microsoft Crm 3.0 installation directory. This is the main starting point for the callout developer, that is:
<formulas /></formulas />
All the callouts must be derived from the CrmCalloutBase class, thus you have to reference microsoft.crm.platform.callout.base.dll file all of your callout projects. This dll file may be found at the MS CRM 3.0 installation CD's following path:
Microsoft CRM 3.0 Professional Edition Disk
\Bin\Assembly\Microsoft.Crm.Platform.Callout.Base.dll
Here we see the events that fire callouts. Using these events MS CRM custom checks and additional functionalities are achieved.
|
|
Table describes the types of operations can be handled using callouts. We may fully customize Create/Update /Delete/Assign operations by writing pre and post callout methods. Since names describe the usages of the methods some of them need explanation.
Assign event occurs when an entity's owner is changed.
SetState event occurs when an entity's state is changed to active or inactive. See Apprendix A1 for a sample code about this event.
Merge event occurs when
|
By deriving from CrmCalloutBase class and limplementing one of the above methods we write our callout code, then we compile it to a DLL file. Now it's time to deploy the resultant assembly to the MS CRM server.
.NET assembly (the class library) is put in the MS CRM server's installation directory's sub folder bin\assembly. i.e: If you installed the server to C:\Program Files\Microsoft CRM then the callout assembly must be put to C:\Program Files\Microsoft CRM\Server\bin\assembly.
Assuming the CRM installation directory is C:\Program Files\Microsoft CRM\
All the callouts must reside at
C:\Program Files\Microsoft CRM\Server\bin\assembly\
After putting the callout DLL to the assembly directory callout declaration must be made to the CRM server, otherwise server cannot know when to call the callout assembly. Notifying the server about callouts is done using the callout.config.xml file that is found in the assembly directory of the CRM.
<?xml version="1.0" encoding="utf-8" ?>
<callout.config version="2.0">
<callout entity="contact" event="PreCreate">
<subscription assembly="DontPermitDuplicates.dll" class="DontPermitDuplicates.NoDblNames" />
</callout>
</callout.config>
Above we see a callout assembly notification to CRM. Callout class library's namespace is called DontPermitDuplicates. Just before a Lead entity is created in the CRM this callout is called.
CRM instantiates an object of DontPermitDuplicates.NoDblNames class. Then executes the PreCreate method in that class. That method may check things or do the required custom business operations then object may either abort the creation operation by raising an error message, or makes the operation continue so the entity is created in the CRM.
Step by Step Simple Callout Example
1. Create a Class Library project in VSNET 2003.
2. Add referance to microsoft.crm.platform.callout.base.dll file
3. Derive a class from CrmCalloutBase.
4. Write PreCreate and then CTRL+SPACE to see the method signature find PreCreate method and press enter.
Use the following sample code to achieve your first callout code.
<p style="LINE-HEIGHT: normal">public override PreCalloutReturnValue PreCreate(
</p>
<p style="LINE-HEIGHT: normal"> CalloutUserContext userContext, CalloutEntityContext entityContext,
ref string entityXml, ref string errorMessage
</p>
<p style="LINE-HEIGHT: normal">) {
PreCalloutReturnValue calloutRetVal = PreCalloutReturnValue.Continue;
</p>
<p style="LINE-HEIGHT: normal"> // error message is not set at the begining
errorMessage = string.Empty;
</p>
<p style="LINE-HEIGHT: normal"> XmlDocument xd = new XmlDocument();
xd.LoadXml(entityXml);
</p>
<p style="LINE-HEIGHT: normal">/* all the elements in the XML file is iterated, and only desired ones are added
to the nvPostedInfo collection */
NameValueCollection nvPostedInfo = new NameValueCollection();
</p>
<p style="LINE-HEIGHT: normal">
</p>
<p style="LINE-HEIGHT: normal"> foreach (XmlElement element in xd.GetElementsByTagName("Property"))
{
</p>
<p style="LINE-HEIGHT: normal"> string strAttributeName = element.Attributes.GetNamedItem("Name").Value;
</p>
<p style="LINE-HEIGHT: normal"> switch (strAttributeName) {
case "tbs_tckn":
nvPostedInfo.Add("tbs_tckn", element.InnerText);
break;
}
}
</p>
<p style="LINE-HEIGHT: normal"> if ( ! IsTcknValid( nvPostedInfo["tbs_tckn"] ) ) {
errorMessage = "The TCKN value is not valid!";
calloutRetVal = PreCalloutReturnValue.Abort;// abort operation
}
return calloutRetVal;
}
</p>
Above code contains a method called IsTcknValid this method returns a boolean value, weather the parameter of the method is a valid TCKN (=Republic of Turkey Citizen Number) value.
Below we may see the TCKN checker code block. This is very useful to preserve the data validity. (The code presented is a port of the SQL function that is located at following URL:
http://www.verivizyon.com/detail.asp?catid=17&cid=317 )
<p style="LINE-HEIGHT: normal">private bool IsTcknValid(string TcknToValidate)
</p>
<p style="LINE-HEIGHT: normal">{
</p>
<p style="LINE-HEIGHT: normal"> bool isValid = false;
</p>
<p style="LINE-HEIGHT: normal"> TcknToValidate = TcknToValidate.Trim();
</p>
<p style="LINE-HEIGHT: normal"> if (TcknToValidate.Length != 11) return isValid;
</p>
<p style="LINE-HEIGHT: normal"> short C1, C2, C3, C4, C5, C6, C7, C8, C9;
</p>
<p style="LINE-HEIGHT: normal"> int Q1, Q2;
</p>
<p style="LINE-HEIGHT: normal"> Int64 TCNO = Convert.ToInt64(TcknToValidate);
</p>
<p style="LINE-HEIGHT: normal"> Int64 ATCNO = TCNO / 100;
</p>
<p style="LINE-HEIGHT: normal"> Int64 BTCNO = TCNO / 100;
</p>
<p style="LINE-HEIGHT: normal"> C1 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C2 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C3 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C4 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C5 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C6 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C7 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C8 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> C9 = Convert.ToInt16( ATCNO % 10 ); ATCNO /= 10;
</p>
<p style="LINE-HEIGHT: normal"> Q1 = ((10 - ((((C1 + C3 + C5 + C7 + C9) * 3) + (C2 + C4 + C6 + C8)) % 10)) % 10);
</p>
<p style="LINE-HEIGHT: normal"> Q2 = ((10 - (((((C2 + C4 + C6 + C8) + Q1) * 3) + (C1 + C3 + C5 + C7 + C9)) % 10)) % 10);
</p>
<p style="LINE-HEIGHT: normal"> if ( TCNO == ((BTCNO * 100) + (Q1 * 10) + Q2) ) {
</p>
<p style="LINE-HEIGHT: normal"> isValid = true;
</p>
<p style="LINE-HEIGHT: normal"> }
</p>
<p style="LINE-HEIGHT: normal"> return isValid;
}
</p>
After writing above code to a Class Library project we have to compile it and then the resultant DLL file msut be copied to the MS CRM's Callout directory that is:
"C:\Program Files\Microsoft CRM 3.0\Server\bin\assembly" This directory has a callout.config file which contains the executed callout DLLs for every Entity those are needed to be checked.
We will add the following line of codes in the callout.config.xml file in order to test our callout.
|
<?xml version="1.0" encoding="utf-8" ?> <callout.config version="2.0"> <callout entity="contact" event="PreCreate"> <subscription assembly="DontPermitDuplicates.dll" class="DontPermitDuplicates.NoDblNames" /> </callout> </callout.config>
|
ATTENTION! Don't forget to put the XML extension to the callout.canfig file, i.e: callout.config.xml
Now we have to test it using a valid and an invalid TCKN value
Testing the Code
First we have to create an attribute that will hold the citizen number, the attribute will be called "new_tckn". After creation, field will be placed on the contact's form.
1) Create the attribute for the contact entity
2) Place the field on the contact form, then publish the changes.
3) Edit the following file, if it does not exists then create it using notepad.
"C:\Program Files\Microsoft CRM\Server\bin\assembly\callout.config.xml"
4) Make iis start again by doing Start > Run > "iisreset"
Now try to enter 12345678901 value for the tckn field of a new contact recrod. This operation will be aborted by the callout since the value is not a valid TCKN number.

Conclusion
In this article the basics of callout programming are shown. As an example of a simple callout Turkish Citizen Number validator callout is developed and tested. Since callout assembies are loaded and run by the MS CRM application using reflection, we have to generate .NET Framework 1.1 assemblies for our callouts. That is beacause MS CRM 3.0 is implemented using .NET 1.1 Framework. As a restriction .NET 1.1 processes cannot execute any higher version of the framework assemblies in their application domain.
In our example there were no connection strings in assembly. Since we don't want to keep our connection string hard coded we need to store them in a config file, thus the config file must be the web.Config of the MS CRM application.
For the curious reader it is beneficial to mention that callouts' DataAccessLayer may be implemented using Enterprise Library's Data Application Block. Until next article keep coding! :-)
Apprendix – Additional Remarks
A.1. When your callouts does not not work, check out here!
Well written two trobleshooting articles may be found at the following locations:
"Is your Microsoft CRM callout not working?", http://blog.sonomapartners.com/2007/03/is_your_microso.html
"The functionality that you expect from a callout .dll file does not work as expected in Microsoft Dynamics CRM 3.0"http://support.microsoft.com/kb/933842
A.2 Other Callout resources
Microsoft's sample callout from CRM SDK's 3.0.7 versionhttp://msdn2.microsoft.com/en-us/library/aa680675.aspx
A.3. Enterprise Library for .NET Framework 1.1
Source: http://msdn2.microsoft.com/en-us/library/ms954827.aspx
This page provides an overview of the Enterprise Library Data Access Application Block. This is reusable and extensible source code-based guidance that simplifies development of common data access functionality in .NET-based applications.
A.4 Sample List of files in Callout Assembly Directory
