Showing posts with label callout. Show all posts
Showing posts with label callout. Show all posts


Improve performance of Microsoft Dynamics CRM

The performance of your CRM implementation is one of the key factors for customer acceptance. There are multiple options available for optimizing your system. Here's a list to get started:

Microsoft - Optimize Microsoft Dynamics CRM 3.0

Microsoft - Optimize Microsoft Dynamics CRM 1.2

Aaron Elder - Optimize the CRM Webservice Calls

Brein Reid - Reduce the amount of useless calls to IIS 6.0 Applications for optimal Performance

Some more tips:
- Do NOT virtualize your SQL Server.
- Run CRM from a computer with a physical network connection instead of wireless.
- In your code use as least webservice calls as posssible.
- Look in the implementation guide on how to cluster your SQL Server and/or load balance your webserver.
- If requirements allow you to do so, run the callout code asynchronous. In the callout just create an MSMQ message and create a windows service which listens to this MSMQ and execute the code.
- In imports and migrations, try to disable as many workflows and callouts as possible.



Creating Environment independent solutions

When developing a solution for CRM, make sure that it runs on all different crm environments. Many implementations have their CRM installation on another port compared to the development environments. A way to handle this, is to look at the location of MS CRM as it is stored in the registery.


// Set default values
private const string CRM_REG_DIRECTORY = @"software\Microsoft\mscrm\";
private const string CRM_SERVICE_PATH = @"/2006/crmservice.asmx";

// Create CrmService instance
service = new CrmService();
service.Url = GetCRMWebServiceRoot() + CRM_SERVICE_PATH;

/// <summary>
/// Returns the recorded web location from the registry
/// </summary>
private static string GetCRMWebServiceRoot()
{
string result = null;
try
{
RegistryKey regKey = Registry.LocalMachine.OpenSubKey(CRM_REG_DIRECTORY, false);
if (regKey != null)
{
result = (string) regKey.GetValue("ServerUrl");
}
else
{
throw new ApplicationException("Microsoft CRM could not be found on this computer.");
}
}
catch (Exception err)
{
throw new ApplicationException("Cannot retrieve registry value for CRM web service. " + err.Message);
}
return result;
}


Thanks to Richard McCormick for providing the codes :)



New reasons added for why your callout won't work

The last weeks I've been adding multiple possible reasons with possible solutions to the checklist as published earlier. If your callout is not working, then make sure you view this list.

Happy coding!



Callout design issue

Last week I noticed some weird behaviour in MS CRM. For a business requirement I created a callout. The callout would perform some business logic as soon as an appointment gets created. The business logic should only run if the activty gets created by a quick campaign. Don't ask me for the reason, but that was requested.

No problem, I created a pre callout which runs on the appointment's OnCreate event. It checks for the regarding field and if this is a quick campaign, then continue, otherwise stop. Fine, I tested the callout by creating an appointment with the regarding field set to an existing quick campaign. After saving the appointment, it appeared that the callout worked.

Unfortunately the testing people did not agree with that test. They tested the callout as it should be used by creating a quickcampaign which finally creates the appointments. In this case it does not work! After some research I have had contact with the product team and they did confirm this behaviour. The quick campaigns do not use the webservice and therefore it will not fire the OnCreate callout. The way to go is to use the MS CRM 1.2 approach which will work. They have promised to work on this for the Titan release, but no feature is in there for sure until it is in the RTM version. Let's just hope that this does gets fixed.

I have updated the list with possible solutions for a callout which is not working. See the list here: Callout not working



Callout not working?

Lets write a bit more about how to get a callout to work and to make debugging available. I already wrote something about this before in this post: Callout debug: Access is Denied.

So your callout is not fired? Here are some thoughts to get started. Make sure that:

- the callout even should fire. Are you using the right callout? Special attention to the email PreSend and PostDeliver callouts: PreSend is for sending email
and PostDeliver is after you received an email

- you have built your callout in .NET Framework 1.1

- the dll file is placed in the correct directory. By default this would be: "C:\Program Files\Microsoft CRM\Server\bin\assembly"

- you created a callout.config.xml in the same folder. Don't forget the extension xml

- the callout is subscribed correctly in the callout.config.xml. It should look like: http://schemas.microsoft.com/crm/2006/callout/;

A correct example would be: http://schemas.microsoft.com/crm/2006/callout/;

- you reset iis (start, run, iisreset) after placing the new callout files


Okay, now you have checked the basics, then let's continue with the harder things to find out based on an error message. Either from the web interface or the event log (don't forget to check there to see if there is an error)

The error messages I'd like to give some hints on are:

Error (in Web UI):

Could not load type YourCallout from assembly YourCallout, Version=1.0.2512.17524, Culture=neutral, PublicKeyToken=null.
Description: An unhandled exception occurred during the execution of the current web request.
Please review the stack trace for more information about the error and where it originated in the code.


Possible solution:
If your code is written in VB, then make sure that you write the subscription correct. You might think that the class in the subscription should be "YourNamespace.YourCalloutClass", but in fact it should be "YourProjectName.YourNamespace.YourCalloutClass".

Error (from event viewer):
Error: ISV code threw exception: assembly: YourCallout.dll; class: YourNamespace.YourCallout; entity: product, event: postcreate, exception: System.IO.FileLoadException: Access is denied: 'YourCallout.dll'.File name: "YourCallout.dll"


Possible solution: Make sure that the security is set to Full for "Network Service" on your dll files.

Error (from event viewer):
Error: General config file format error: System.Data.SqlClient.SqlException: Could not find stored procedure 'sp_sdidebug'.


Possible solution: After the iisreset first do all the actions that you should do to run the callout and when the callout should have run, then start the debugger on the w3wp process.

Here's another hint for when working with impersonation. When you're using the userContext.UserId to set the callerid value, make sure that you also set the credentials on the webservice as shown below.


service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
service.CallerIdValue = new CallerId();
service.CallerIdValue.CallerGuid = userContext.UserId;


Error (from event viewer):
Error: ISV code threw exception: assembly: YourCallout.dll; class: Namespace.Callout; entity: account, event: postupdate, exception: wi: Specified cast is not valid.


Possible solution: The file "Microsoft.crm.platform.callout.base.dll" is located in the assembly folder. Remove the file from that folder, perform an IISReset and restart the workflow service.

Error (from event viewer):
Error: ISV code threw exception: assembly: YourCallout.dll; class: Namespace.Callout; entity: account, event: postupdate, exception: System.IO.FileNotFoundException: File or assembly name Microsoft.Crm.Platform.Callout.Base, or one of its dependencies, was not found.


Possible solution: The CRM server is older then the RTM version. Look in the registery (HKLM\software\Microsoft\MSCRM\CRM_Server_Version). The value should be 3.0.5300.0 or above. If it is not, then reinstall the CRM server with the latests bits.

Error (from event viewer):
Precallout event cannot have pre-/post-image subscription. event: preupdate, entity: entityname


Possible solution: This error appears if you are supplying prevalue and postvalue attributes in the callout.config.xml for a precallout. The prevalue and postvalue attributes are only valid for postcallouts. The pre callout does supply all the changed values. If you need more values, then get them via the service.Retrieve method.

No Error info?
If you cannot find any error information and your callout doesn't do anything, then check the registery. There might be a key named "SetupMode" in HKLM\Software\Microsoft\MSCRM\. If it is indeed there, then modify this key to contain the DWORD value "0". After the change, make sure that you perform an "IISReset" (thanks Jevgenij).

Exceptional situation.
There is one situation known in which callouts do not fire. This is when an activity is created by quick campaigns (possibly also regular campaigns). The callout OnCreate will NOT fire for the activities. You'll need to use the CRM 1.2 approach for this situation. Microsoft promised to work on this in the Titan release. If it will be fixed is still a question though.

Well, these were the issues I could think of now. Another approach would be to temporarily remove all complex codes and start with a simple callout that writes something to the eventlog. You then at least know if your callout is fired or not.

Hope this helps you to get your callout working!



Callout debug: Access is Denied

I'm not sure if more people are experiencing the same thing, but at least I'd like to share this information with you.

After creating a debug build of a callout, I do copy the dll and pdb files to the \Microsoft CRM\Server\bin\assembly folder. Now I can normally debug the callout. But after modifying the code and copying the files again, then I cannot step into the code. CRM just steps over the whole callout.

When searching for a reason for this behaviour, I found a message in the event viewer. The message says:
-------------------------------------------------------------------------
Event Type: Error
Event Source: MSCRMCallout
Event Category: None
Event ID: 16912
Date: 6/22/2006
Time: 11:53:00 AM
User: N/A
Computer: CRMTest
Description:
Error: ISV code threw exception: assembly:
Microsoft.Crm.Sdk.FullSample.CalloutSample.dll; class:
Microsoft.Crm.Sdk.FullSample.Callouts.CalloutSample; entity: account,
event: precreate, exception: System.IO.FileLoadException: Access is
denied: 'Microsoft.Crm.Sdk.FullSample.CalloutSample.dll'.
File name: "Microsoft.Crm.Sdk.FullSample.CalloutSample.dll"
at System.Reflection.Assembly.nLoad(AssemblyName fileName, String
codeBase, Boolean isStringized, Evidence assemblySecurity, Boolean
throwOnFileNotFound, Assembly locationHint, StackCrawlMark& stackMark)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef,
Boolean stringized, Evidence assemblySecurity, StackCrawlMark&
stackMark)
at System.Reflection.Assembly.LoadFrom(String assemblyFile, Evidence
securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm
hashAlgorithm)
at System.Activator.CreateInstanceFrom(String assemblyFile, String
typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder,
Object[] args, CultureInfo culture, Object[] activationAttributes,
Evidence securityInfo)
at Microsoft.Crm.Callout.CalloutHost.PreCreate(CalloutUserContext
userContext, CalloutEntityContext entityContext, String& entityXml,
String& errorMessage)


=== Pre-bind state information ===
LOG: Where-ref bind. Location = C:\Program Files\Microsoft
CRM\server\bin\assembly\ Microsoft.Crm.Sdk.FullSample.CalloutSample.dll
LOG: Appbase = c:\windows\system32\inetsrvLOG: Initial PrivatePath = NULL
Calling assembly : (Unknown).
===


LOG: Policy not being applied to reference at this time (private,
custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Program Files/Microsoft
CRM /server/bin/assembly/ Microsoft.Crm.Sdk.FullSample.CalloutSample.dll.


For more information, see Help and Support Center at
http://go.microsoft.com/fwlink/events.asp.
-------------------------------------------------------------------------

Now how to solve this?
The resolution is quite simple. After copying the files to the assembly folder, you should give the user "Network Service" full permissions. The callout will work now and you will be able to debug the code.

Good luck!

Update: Ofcourse you can also give the user "Network Service" full rights on the bin folder of your project. You will not have to modify the file then again and again.