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.



Welcome to the blog world David!

One of my fellow CRM MVP's has just decided to start blogging as well. Please welcome David Jennaway in this world by paying a visit to his already valueable blog:

http://mscrmuk.blogspot.com/



Convergence 2007 Copenhagen

It was a last minute call, but I'm attending the Convergence as well. Please leave a message or send an email if you wish to drink a beer :)

If there's something interesting to share, then I'll make sure it's online as soon as possible.

Also I'll be standing at the Microsoft Product Groups Booth for the MVP Program at the following times:
- Monday 22nd 18:00 - 19:00
- Tuesday 23rd 12:00 - 13:00
- Thursday 25th 10:30 - 11:30

Se you there!



Importing Customizations: Invalid Attribute

Here's an approach for finding a solution to a new error which I haven't discussed before. The error that I will discuss is the message "Invalid Attribute" while importing the customizations. This only appears when there are already customizations imported and you try to import a new version of the customizations file.

I wasn't able to find out information on Google or Partner Source, but managed to find out what was going wrong. The way I found this out, is by using the CRMDiagTurbo tool. You can find this tool on the blog of Benjamin Lecoq: http://blogs.msdn.com/benlec. This tool allows you to do quite some things of which one is to easily enable the tracing for CRM. The way to find the error is to start tracing, perform the import, wait for the error, stop the tracing. You now can go and search in the file c:\CrmDiagPlatformTrace[ddMMyyyy]\w3wp-MSCRMServices-[yyyyMMddhh].log for the string "SOAP Request failed".

This will show you an entry which says:


>SOAP Request failed:
>Url:http://ukauctioncrm/MSCRMServices/ImportXml.asmx
>MethodName:Import
>Response:
><?xml version="1.0" encoding="utf-8"?>
><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
> <soap:Body>
> <soap:Fault>
> <faultcode>soap:Server</faultcode>
> <faultstring>System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> Microsoft.Crm.CrmArgumentOutOfRangeException: MaxLength is out of range for 'new_footertext' field. MaxLength: 5000, StringMinLength: 1, StringMaxLength: 4000 ---> System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
>Parameter name: MaxLength is out of range for 'new_footertext' field. MaxLength: 5000, StringMinLength: 1, StringMaxLength: 4000
> --- End of inner exception stack trace ---
> at Microsoft.Crm.Metadata.StringAttributeUIData..ctor(AttributeMetadata metadata, XmlNode xmlProperties)
> at Microsoft.Crm.Metadata.AttributeMetadata.CreateUIData(XmlNode propertiesXml, Organization settings)
> at Microsoft.Crm.Metadata.EntityMetadata.InsertFieldXml(XmlDocument entityXml, Boolean ignoreFieldXmlErrors, Organization settings)
> at Microsoft.Crm.Metadata.EntityMetadata.InsertEntityXml(DataRow reader, Boolean ignoreFieldXmlErrors, Organization settings)
> at Microsoft.Crm.Metadata.DynamicMetadataCache.LoadUIInfo(DataTable OrganizationUI)
> at Microsoft.Crm.Metadata.DynamicMetadataCache.LoadCache(DataSet ds)
> at Microsoft.Crm.Metadata.DynamicMetadataCache.Flush()
> at Microsoft.Crm.Tools.ImportExportPublish.ImportEntityRelationshipHandler.ImportItem()
> at Microsoft.Crm.Tools.ImportExportPublish.ImportHandler.Import()
> at Microsoft.Crm.Tools.ImportExportPublish.RootImportHandler.RunImport(String[] ImportEntities, ImportMask Mask)
> at Microsoft.Crm.Tools.ImportExportPublish.ImportXml.RunImport(String parameterXml)
> at Microsoft.Crm.WebServices.ImportXmlWebService.Import(String parameterXml, String customizationXml)
> --- End of inner exception stack trace ---</faultstring>
> <detail />
> </soap:Fault>
> </soap:Body>
></soap:Envelope>


When looking to the faultstring in this soap package, we can see that the new_footertext is causing the problem. Apparently this attribute has been removed in the development environment and been added again with a larger length than the original length. To solve this particular error I have just removed the field from the target system and reimported the customizations file, but since the exact message will differ in your system, you will need to find out what you will do with the attribute specified.

Since the trace file is quite hard to understand if you are seeing this for the first time, you can also use the CRM Trace Log Viewer. This is a tool created by Stunnware (Michael Höhne) which you can download here: http://stunnware.com/crm2/topic.aspx?id=TraceLogViewer

I hope this helps somebody who encounters the same error message.



MS CRM Role Utility

Ever since the Sandbox went down, I've received quite some questions around the Role Utility. Luckily I still had a copy available. For the ease of use I've uploaded this one and here's the link: link.

Have fun



Speaker at Dutch Dynamics CRM User Group

Last week at the 4th of October, there was the first session for the Dutch Dynamics CRM User Group. This session was organized by the Dynamics User Group (http://www.dynug.nl) and hosted by Generali. The goal of this User Group is to get customers together and learn from eachother and have a voice within Microsoft.

The agenda for this meeting was:

- Welcome and Introductions
- User Group and Microsoft
- Microsoft Dynamics CRM Migration
- Break
- Microsoft Dynamics CRM Integration
- Open discussion
- Informal end

The organization has asked me to be the speaker for the topic Migration and Leon Krancher, one of my colleagues, was asked for the topic Integration. The both of us have been guiding the open discussion and answering interesting questions regarding training, user adoption and implementation issues. The presentations will come online on the website for the DynUG for members.

Evaluation of this first session showed that it was a great success. The average of all the attendants thought the sessions were good or very good. The organization, Leon and myself are very happy about this event and are looking forward for the next meeting. I hope to see even more customers by then!



Fetch the ObjectTypeCode based on EntityName

Not a very rocket science, but just something again that I don't want to type over and over again. So here's a piece of code that helps you to fetch the ObjectTypeCode based on the EntityName for CRM 3.0:

private int GetObjectTypeCode (string entityName)
{
CrmMetaDataService.MetadataService metadataService = new CrmMetaDataService.MetadataService();
metadataService.Credentials = System.Net.CredentialCache.DefaultCredentials;
EntityMetadata entityData = metadataService.RetrieveEntityMetadata(entityName, EntityFlags.EntityOnly);
return entityData.ObjectTypeCode;
}


For CRM 4.0 this would be (thanks for the comment Pratima):
private int GetObjectTypeCode (string entityName)
{
CrmMetaDataService.MetadataService metadataService = new CrmMetaDataService.MetadataService();
metadataService.Credentials = System.Net.CredentialCache.DefaultCredentials;
EntityMetadata entityData = metadataService.RetrieveEntityMetadata(entityName, EntityFlags.EntityOnly);
return entityData.ObjectTypeCode.Value;
}


Have fun copy pasting :)



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 :)



Change requirement level at runtime

I've made quite some posts around how to dynamically modify the form by using JavaScript. One thing I haven't posted around yet, but what I do use quite often, is to modify the requirement level of attributes on the form at runtime. Based on a selection in the system, another field could become required. Also if the selection changes again, then the field should not be required anymore. Here is the code for making a field optional, recommended or required:


// Set field to not required
crmForm.SetFieldReqLevel("fieldname", 0);

// Set field to business required
crmForm.SetFieldReqLevel("fieldname", 1);

// Set field to business recommended
crmForm.SetFieldReqLevel("fieldname", 2);


Update: new code placed based on the comment by Peter. Before the update I had this piece of code:


// set the field required (i.e. show error message when field is not filled in)
crmForm.all.fieldname.req = 2;

// modify the label to be red
crmForm.all.fieldname_c.className = 'req';



Expert on Avanade.com

Avanade has create a profile for me on their public website in the experts section. It's quite a honer to be listed within the same list as Matt Joe, Dennis Mulder and Matt Parks. http://www.avanade.com/people/expert_detail.aspx?expertid=98

If you want to become an Avanade expert as well, feel free to contact me (ronaldl at avanade dot com) and I'll make sure that you get in contact with the correct HR person.



Reawarded MVP

Hi Guys,

As a reward for community work last year, Microsoft has reawarded me the Microsoft CRM MVP!

My community work includes:
- Around 1000 newsgroup and forum posts.
- Around 75 blog posts, of which 35 self written articles. The blog had more than 75k visitors.
- Writing the article "Many to many relationships" for the CRM Team blog and the article "The power of choice" for CRN.
- Working as 'Ask the Expert' at the Convergence both in San Diego and Munich.
- Other tasks like participating in the Titan beta, answering hunderds of direct mails and delivering product team feedback.

This year again I plan on continuing my community work. Also, if you see a question somewhere and you have the answer? Please take the time to answer the question and help this person out. We all started with questions, including myself, and helping people with your knowledge is one of the greatest things you can do. And one day you might even get awarded an MVP award as well :)

See you in the community!



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!



Change username of user in CRM

Microsoft has released KB article #935220 some time ago. This article describes how to change the username of a user in Dynamics CRM. See the article here:
How to change the user name and the logon name for a user record in Microsoft Dynamics CRM 3.0

This worked for me, I hope this also might help one of you!



Add New Button on Lookup

Hi Guys,

I’ve been looking into how to enable the ‘new’ button on custom entities. Here is what I have found. O yeah, For the direct solution scroll to the end of the mail.

- You can set test some things when using this url:
http://localhost:5555/_controls/lookup/lookupsingle.aspx?class=null&objecttypes=10001&browse=0&DefaultType=0&ShowNewButton=1&ShowPropButton=1
Ofcourse modify the servername and the objecttypes to match your situation

- The properties and new button can be set by using the querystring parameters as given above.

- There is one additional piece of hard coding in the class Microsoft.Crm.Web.Controls.Lookup.LookupPageBase. This particular piece of code is this:


if (this._showNewButton)
{
for (int j = 0; j < this._objectTypes.Length; j++)
{
switch (int.Parse(this._objectTypes[j], CultureInfo.InvariantCulture))
{
case 1:
this._canCreateAccount = base.CurrentUser.GetPrivilege(Privileges.CreateAccount);
break;

case 2:
this._canCreateContact = base.CurrentUser.GetPrivilege(Privileges.CreateContact);
break;

case 3:
this._canCreateOpportunity = base.CurrentUser.GetPrivilege(Privileges.CreateOpportunity);
break;

case 4:
this._canCreateLead = base.CurrentUser.GetPrivilege(Privileges.CreateLead);
break;

case 0x10cc:
this._canCreateList = base.CurrentUser.GetPrivilege(Privileges.CreateList);
break;
}
}
this._showNewButton = ((this._canCreateAccount || this._canCreateContact) || (this._canCreateLead || this._canCreateOpportunity)) || this._canCreateList;
}

What it does, is looking to the specified list of objecttypecodes and looks if any of these match the id of account, contact, lead, opportunity or (marketing)list. If it does not match one of those, then the new button is hidden.

- You can run the javascript code "createNew();”. This is the same function as that will be executed by the button. Running this javascript will open the quickcreate window which will work.

- You can also modify the file ‘lookupsingle.aspx’. In this file there is the function window.onload() function. Modify this function to add these lines to the end:

//enable new button
btnNew.style.display = 'inline';

This will cause the new button to be visible.

That’s about it. Just add the line above and you’ll be ready to go. The new button will now be available for all entities. I haven’t tested this through so that will need to be done in your situation. Also note that this is unsupported and will not be migrated to a potential upgrade to titan or might be lost after an install of a hotfix.

Hope this helps,

Ronald



My current project

Hi Guys,

Last weeks I've been working very hard for my current customer. Together with a team in the Londen, UK and a team in Hyderabad, India I'm working on a very interesting project. We're creating a CRM system for the London office of my client which will connect to a global CRM implementation and which will also have a self service web interface. This project does take a lot of time and that's the reason why I have not been posting much.

Right now I am in India at our offshore development team. They are doing a great job and it is a pleasure to work with these guys. They make my stay here a real pleasure and an unforgettable time. Here are some pictures.







I hope to have a little bit more time now to post more interesting posts. I have some interesting topics ready, just need to make them real posts :)



Fetching the selected records in a grid

Make sure that you read this page if you want to use the selected values in a grid:
http://msdn2.microsoft.com/en-us/library/bb267367.aspx

Until some time ago I was using one of the functions that CRM uses, but since that is unsupported and the method as described in the article as stated above is supported, I am using the new approach.

In short it comes down to this:


// window.dialogArguments contains an array of the IDs for the
// selected records.
var sSelectedRows = window.dialogArguments;

// If sSelectedRows is empty, do not execute the update.
if (sSelectedRows == "" sSelectedRows.length == 0)
{
alert("You must select records in order to use this feature.");
window.close();
}
else
{
//Do something;
}


Happy coding!



CRM Blog search

My fellow MVP Matt Wittemann has created a custom search which searches through all of the blogs listed on the blog list. You can search through all of these blogs by going to this page:

http://google.com/coop/cse?cx=007820608918962702080%3Ao4ydxw03wpa

or go to Matt's blog and perform a search from there.

Thanks Matt!

Update:
Not only Matt has created such a list, also Mitch Milam has created such a seach:
http://www.crmaccelerators.net/searchcrm.aspx

And also Maarten Dominicus has created that:
http://www.google.com/custom?cx=006153342194408151232%3Aji9yuqn0bms

The choice is up to you :)



Microsoft Office SharePoint Web Part for Microsoft Dynamics CRM

Copied from Joris Kalz blog:


We just released the localized version of the list web part for Microsoft Dynamics CRM to support Microsoft Office Sharepoint Server (MOSS 2007). The great thing about is that you can use Sharepoint to create a dashboard to display essential Microsoft Dynamics CRM data for your users. E.g. this can be used to create a central portal or dashboard for your sales team with all relevant information and reports coming from Microsoft CRM and Sharepoint.

The List Web Part is now available in the following languages: English, Brazilian, Danish, Dutch, French, German, Italian, Spanish.

Download: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=bc9b3526-decf-4057-a530-91840c0d5401



Clone a record

There are some interesting things in the May 2006 Demo VPC that you might not have seen before. One of them is cloning a record. There's an addon written and supplied with the VPC that does take of cloning a contact. I've used it in some other projects and will be using it in my current project again. Ofcourse you can modify the code to match any entity.

I'll add the code here so that you dont have to download the complete VPC. There will be added a piece of code to the isv.config file and an additional file will need to be placed on the server.

Here is the piece that needs to be added in to the isv.config file:


<configuration version="3.0.0000.0">
<Root />
<Entities>
<Entity name="contact">
<ToolBar ValidForCreate="0" ValidForUpdate="1">
<Button Title="Clone Contact" ToolTip="Create a Copy of This Contact" Icon="/CloneContact/contactclone.gif" Url="/CloneContact/CloneContact.htm" WinMode="1" WinParams="dialogHeight:100px;dialogWidth:300px;"/>
</ToolBar>
</Entity>
</Entities>
</configuration>


This is the code that needs to be stored in the file as indicated in the isv.config file:

<html>
<title>Clone Contact</title>

<style>

BODY, TD
{
font-family: arial;
font-size: 12px;
}

TD.body
{
border-bottom: solid 1px #cccccc;
text-align: center;
}

</style>

<script language="javascript">

// Set global variable for the cloned contact window
var oClonedContact;

function window.onload()
{
// Open a new contact form
oClonedContact = window.open('/sfa/conts/edit.aspx','','menubar=0, status=1, width=1000, height=600');

// Set a timeout to wait for the new contact form to load
setTimeout('checkPageState()',100);
}

// Checks if the new contact form has completed loading
// When it completes, CloneContact will be called
// If it's not loaded, it will set a timeout and check again.
function checkPageState()
{
if (oClonedContact.document.readyState == 'complete')
{
CloneContact();
return;
}

setTimeout('checkPageState()',100);
}

function CloneContact()
{
// Get a pointer to the parent window
var oParent = window.dialogArguments;
var oSource = oParent.document.crmForm;

// With the target crmForm
with(oClonedContact.document.crmForm)
{
// Name fields
salutation.DataValue = oSource.salutation.DataValue;
firstname.DataValue = oSource.firstname.DataValue;
nickname.DataValue = oSource.nickname.DataValue;
lastname.DataValue = oSource.lastname.DataValue;
jobtitle.DataValue = oSource.jobtitle.DataValue;
parentcustomerid.DataValue = oSource.parentcustomerid.DataValue;

telephone1.DataValue = oSource.telephone1.DataValue;
telephone2.DataValue = oSource.telephone2.DataValue;
mobilephone.DataValue = oSource.mobilephone.DataValue;
fax.DataValue = oSource.fax.DataValue;
pager.DataValue = oSource.pager.DataValue;
emailaddress1.DataValue = oSource.emailaddress1.DataValue;

// Address fields
//address1_name.DataValue = oSource.address1_name.DataValue;
address1_line1.DataValue = oSource.address1_line1.DataValue;
address1_line2.DataValue = oSource.address1_line2.DataValue;
address1_line3.DataValue = oSource.address1_line3.DataValue;
address1_city.DataValue = oSource.address1_city.DataValue;
address1_stateorprovince.DataValue = oSource.address1_stateorprovince.DataValue;

address1_postalcode.DataValue = oSource.address1_postalcode.DataValue;
address1_country.DataValue = oSource.address1_country.DataValue;
//address1_telephone1.DataValue = oSource.address1_telephone1.DataValue;
//address1_addresstypecode.DataValue = oSource.address1_addresstypecode.DataValue;
//address1_shippingmethodcode.DataValue = oSource.address1_shippingmethodcode.DataValue;
//address1_freighttermscode.DataValue = oSource.address1_freighttermscode.DataValue;

// Professional Information fields
//department.DataValue = oSource.department.DataValue;
accountrolecode.DataValue = oSource.accountrolecode.DataValue;
managername.DataValue = oSource.managername.DataValue;
managerphone.DataValue = oSource.managerphone.DataValue;

assistantname.DataValue = oSource.assistantname.DataValue;
assistantphone.DataValue = oSource.assistantphone.DataValue;

// Personal Information fields
gendercode.DataValue = oSource.gendercode.DataValue;
familystatuscode.DataValue = oSource.familystatuscode.DataValue;
spousesname.DataValue = oSource.spousesname.DataValue;

all.birthdate.DataValue = oSource.all.birthdate.DataValue;
all.anniversary.DataValue = oSource.all.anniversary.DataValue;

// Description
//description.DataValue = oSource.description.DataValue;

// Internal Information fields
ownerid.DataValue = oSource.ownerid.DataValue;
originatingleadid.DataValue = oSource.originatingleadid.DataValue;

// Billing Information fields
//creditlimit.DataValue = oSource.creditlimit.DataValue;
//all.creditonhold.DataValue = oSource.all.creditonhold.DataValue;
//paymenttermscode.DataValue = oSource.paymenttermscode.DataValue;
//defaultpricelevelid.DataValue = oSource.defaultpricelevelid.DataValue;

// Contact Methods fields
preferredcontactmethodcode.DataValue = oSource.preferredcontactmethodcode.DataValue;
all.donotemail.DataValue = oSource.all.donotemail.DataValue;
all.donotbulkemail.DataValue = oSource.all.donotbulkemail.DataValue;
all.donotphone.DataValue = oSource.all.donotphone.DataValue;
all.donotfax.DataValue = oSource.all.donotfax.DataValue;
all.donotpostalmail.DataValue = oSource.all.donotpostalmail.DataValue;

// Marketing Information fields
all.donotsendmm.DataValue = oSource.all.donotsendmm.DataValue;
all.lastusedincampaign.DataValue = oSource.all.lastusedincampaign.DataValue;

// Service Preferences fields
preferredappointmenttimecode.DataValue = oSource.preferredappointmenttimecode.DataValue;
preferredappointmentdaycode.DataValue = oSource.preferredappointmentdaycode.DataValue;
preferredserviceid.DataValue = oSource.preferredserviceid.DataValue;
preferredequipmentid.DataValue = oSource.preferredequipmentid.DataValue;
preferredsystemuserid.DataValue = oSource.preferredsystemuserid.DataValue;
}

// Finally, close the dialog
window.close();
}

</script>

<body>

<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0" align>
<tr valign="middle">
<td class="body" align="center">
<div style="font-size= 10pt; font-family= Tahoma;">Cloning Contact...</div>
</td>
</tr>
</table>

</body>

</html>


It's just a free addon from Microsoft, so use it if you can :)



Error when creating a new appointment

Robert Feenstra, working for Atlanticasset, has informed me about an issue that he had when he created a new appointment. He did not only tell me his issue, he also was able to give the solution! To be able to help more people, here is the error and the solution.

When trying to create a new appointment, upon save get the error:

The record that you are requesting is currently unavailable. Either the record was not found or you do not have sufficient security permissions to view it.


Upon clicking "Ok", I get the error:
General failure in scheduling engine


with a "Ignore and Save" buttonwhich will save the appointment, but still marks the appointment as having a General failure in scheduling engine from then on.

This is caused by an orphan record in the database. To find out which one it is, run the following script against the MSCRM database.

select r.ResourceId, r.OrganizationId, r.BusinessUnitId, r.Name, u.FullName from Resource as r left outer join SystemUserBase as u on r.ResourceId = u.SystemUserId where r.ObjectTypeCode = 8


If the 4th and 5th columns do not match, this CRM user record has been deleted from the SystemUserBase table, then we need to remove the orphan record from the Resoursebase table. To do so, please run the following script.

delete from ResourceBase where ResourceId in (select r.ResourceId from Resource as r left outer join SystemUserBase as u on r.ResourceId = u.SystemUserId where r.ObjectTypeCode = 8 and u.FullName is null)


That should solve the issue. Ofcourse you should always make a backup before trying these kind of actions!

Thanks Robert for sharing!



Interesting issues with the CRMDateTime continuum.

This is a guest post by a friend and colleague of mine: Leon Krancher. He has been digging into an issue regarding CRMDateTime and integration. Make sure you read this before you start your own integration!

Well I guess I’d better start off with a little introduction. My name is Leon Krancher and I’m a colleague of Ronald at Avanade. As we both work with CRM we occasionally find time to get together, have a beer and share experiences. Recently I told him about a problem I encountered while trying to create a service that would synchronize my customers Data warehouse with CRM. Ronald thought it was interesting and asked me to write about it for his blog. So here we go.

Let’s start out with a more detailed description of what had to be created.

The customer’s data warehouse contained information about its customers. This information is updated frequently by the customer’s backoffice systems as well as a number connections to third party systems. Whenever the data changed a database trigger would update a datatime field inside the database to indicate the exact moment the customers data was changed.
Because the data warehouse data was also used in CRM I was tasked with creating an application that would update the data stored in CRM whenever the data warehouse was updated. Unfortunately I only had a very limited timeslot in which this application would have to run so I had to find a way to update only those few records that had changed. To solve this problem I decided to do the following:

Ø First I would add an attribute to the entity in CRM that would be used to store the DateTime value used to store the date and time on which the data was originally created in the data warehouse.

Ø Second I wrote a cross-database join that would join the data warehouse on the CRM database, compare both datetime values and return those rows for which the entity in CRM was outdated.

Ø The last step was to use the CRM webservice to update the CRM entities returned by the previous query[1].

Unfortunately whenever I tried to run my tool it would always update all records even though the data warehouse had not changed.
After some frantic debugging[2] I finally figured out what the true problem was.

When storing my data in CRM I used the following code taken from the CRM SDK.


providedContact.new_nawsyncdate.Value = syncDateAccordingToDatabase.ToUniversalTime().ToString("u");

This converts the syncDateAccordingToDatabase DateTime object to a string in the universal time format so it can be stored in CRM. Unfortunately the universal time format omits any milliseconds.

So whenever SQL Server would compare the dateTime stored in the data warehouse to the dateTime stored in CRM the data warehouse DateTime that did contain any milliseconds would be seen as ‘larger’, hence the continuous cycle of updates.

After figuring this out the problem was easily solved by adjusting the database query so it would always remove any milliseconds before making the comparison.

I hope this provides you guys with both a funny story and some help if you ever run into a similar problem.

-Leon

[1] I couldn’t update the CRM database directly because that would prevent the execution of a callout

[2] In my defense, the matters were complicated because the data warehouse and the CRM databases were set to use different time zones making the problem appear like a conversion problem



Dynamic picklists and other samples

Somebody asked me for some advice on how to create dynamic picklists. I have seen this question in the newsgroups several times as well. Now you might expect that I do have a coding sample for you, but I did not create one. And still I do have one which you can download here. Now... If I didn't create it, who did?

Microsoft did create this for you. And made it available. And even better. You probably do have it already! If you have downloaded the SDK, then you have this coding sample already. If not, then download the SDK here. In this SDK is not only the CHM file explaining how to work with the CRM API, but also a lot more information in separate files. I'd suggest you to take a look in the folder "sdk samples". In this folder you will find multiple folders including "fullsample". This fullsample folder does give you the following examples:

- activity events
- callout sample (both vb and c#)
- callout sample 2 (both vb and c#)
- databinding
- duplicate detection
- dynamic picklist
- import export publish (both vb and c#)
- metadata diagram (both vb and c#)
- workflow plugin (both vb and c#)

Next to the fullsample, also take a look at the other folders in the sdk samples directory. This might save you a lot of work!!!



Gezocht: Stukadoor

This post is intentionally in Dutch.

Zoals de titel al aangeeft: ik zoek een stukadoor. Al meer dan een jaar kijk ik uit naar de oplevering van m'n huisje en het is bijna zo ver. Op 22 mei krijg ik de sleutel en kan ik beginnen met de binnenkant. Als eerste zullen de muren van spachtelputz voorzien moeten worden. De persoon die dat voor mij zou gaan doen, die heeft alleen juist dán twee weken vakantie...

Nu is het dé tijd om mij te helpen :) Kent iemand van mijn lezers een stukadoor die voor een vriendelijk prijsje mij wil helpen? Stuur ajb een mailtje naar: ronaldl at avanade dot com.

Dank je!



Tips on using the publishreports executable

Update: see the bottom for a solution to be able to use the filter in the report!

Some days ago I posted an article around how to move reports from a server to another server in the development process. Now I've been working with it, I can share some tips.

The first tip is on how to publish the reports. How does the publishreports.exe know where to find the publish.config and the reports? This wasn't completely clear to me on this page. It appears that you will need to go to the folder that you have created in the first step by using downloadreports.exe. Then you can do two things. Either call the publishreports executable on the server, by default on the location "C:\Program Files\Microsoft CRM\Reports\PublishReports.exe" or you can copy the file publishreports.exe and the file "Microsoft.CRM.Tools.Logging.dll" to the same folder and run publishreports from there. This will allow you to create an installer application because you know for sure that the publishreports executable is in your folder. You cannot be sure that the executable is in the c:\program files folder because thats a choice of the person installing CRM.

Furthermore you will notice that the publishreports will throw this error on your reports:


This error is caused due to the filter that is set in your report (by default set to last modified within the last 30 days). Each report has a layout like this:

<?xml version="1.0" encoding="utf-8"?>
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<DataSources>
<DataSource Name="CRMDEV30_MSCRM">
<ConnectionProperties>
<IntegratedSecurity></IntegratedSecurity>
<ConnectString></ConnectString>
<DataProvider></DataProvider>
</ConnectionProperties>
<rd:DataSourceID></rd:DataSourceID>
</DataSource>
</DataSources>
<PageWidth></PageWidth>
<rd:DrawGrid></rd:DrawGrid>
<InteractiveWidth></InteractiveWidth>
<rd:GridSpacing></rd:GridSpacing>
<rd:SnapToGrid></rd:SnapToGrid>
<Body>
</Body>
<rd:ReportID></rd:ReportID>
<DataSets>
<DataSet Name="CRMDEV30_MSCRM">
<Query>
<rd:UseGenericDesigner></rd:UseGenericDesigner>
<CommandText></CommandText>
<DataSourceName></DataSourceName>
</Query>
<Fields>
</Fields>
</DataSet>
</DataSets>
<Code>
</Code>
<Width></Width>
<InteractiveHeight></InteractiveHeight>
<Language></Language>
<PageHeight></PageHeight>
</Report>

In your report you will find one more node between the pageheight and the end of the report:
<Custom>
<MSCRM xmlns="mscrm"><ReportFilter><ReportEntity paramname="P1" displayname="Contacts"><fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"><entity name="contact"><all-attributes /><filter type="and"><condition attribute="modifiedon" operator="last-x-days" value="30"/></filter></entity></fetch></ReportEntity></ReportFilter></MSCRM> </Custom>

Remove this line from your report and you will be able to import the report. Keep in mind that you will have to set the default filter for your report afterwards!

Update:
One of my colleagues, Philipp Uihlein, has sent me an email regarding this blog post. He has found a way to keep the default filter in the definition! See his text for the details:

Hi Ronald,

I found out that the tool can handle the <Custom> tag when you add a namespace to the xml.

In the Account Summary.rdl for example I replaced the
<Custom> <MSCRM xmlns="mscrm">&lt;ReportFilter&gt;&lt;ReportEntity paramname="CRM_FilteredAccount"&gt;&lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"&gt;&lt;entity name="account"&gt;&lt;all-attributes /&gt;&lt;filter type="and"&gt;&lt;condition attribute="modifiedon" operator="last-x-days" value="30" /&gt;&lt;/filter&gt;&lt;/entity&gt;&lt;/fetch&gt;&lt;/ReportEntity&gt;&lt;/ReportFilter&gt;</MSCRM> </Custom>


node with

<Custom xmlns="http://schemas.microsoft.com/crm/2006/WebServices" xmlns:mscrm="http://schemas.microsoft.com/crm/2006/WebServices"> <MSCRM xmlns="mscrm">&lt;ReportFilter&gt;&lt;ReportEntity paramname="CRM_FilteredAccount"&gt;&lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"&gt;&lt;entity name="account"&gt;&lt;all-attributes /&gt;&lt;filter type="and"&gt;&lt;condition attribute="modifiedon" operator="last-x-days" value="30" /&gt;&lt;/filter&gt;&lt;/entity&gt;&lt;/fetch&gt;&lt;/ReportEntity&gt;&lt;/ReportFilter&gt;</MSCRM> </Custom>


and could publish the report using PublishReports.exe without problems.

Best regards,

Philipp



English version of Column on Channelweb

An article that I wrote some weeks ago for Channelweb was published in Dutch. I have translated the article because of the many requests that I got. Read here the English version of the column 'The power of choice'.

The power of choice

Microsoft will release the newest version of its much discussed product Microsoft Dynamics CRM in 2007. There are big changes to come which will make it possible to use this application in even more organizations.

The market of Customer Relationship Management was entered by Microsoft in the end of 2003. The first version was primarily aimed at small and medium businesses, but also enterprise organizations implemented this application. Microsoft Dynamics CRM 3.0 was released in December 2005 to serve all target groups at best. Better scalability as well as a service oriented architecture (soa) was added for the large organizations and a partner hosted solution has been made available for the smaller organizations.

The successor of Dynamics CRM 3.0, which is still known under the codename Titan, will offer the clients even more choices. This Titan-release will be able to run multiple installations on the same server (multi-tenancy). The hosting partners are not depending on virtual servers anymore because of this multi-tenancy. Therefore they can lower the prices even more. Also Microsoft will offer CRM as a service themselves under the name CRM Live. This offer will be available in North America in the first place. International releases will follow as soon as possible.

International employees of a multinational will be able to work with Dynamics CRM when working with this Titan-release. This release will enable the users to choose their own language (multi-lingual) and own currency (multi-currency) within one installation. This means that large-scale, international operating companies with multiple establishments and business units can use Dynamics CRM even better. And they can do that from one centralized maintainable organization or from a decentralized maintenance organization on a hosted environment. Another large change is the extension of workflow possibilities. Dynamics CRM does now use its own workflow engine, but this will be replaced by Windows Workflow Foundation in Titan. This will further improve the integration with other Microsoft applications.

The new possibilities mentioned above do give the clients many advantages. Not only the SMB but also the enterprise organizations can optimize the use of CRM with these advantages. Microsoft does let the client choose between different offerings of CRM, but the final choice stays with you. What will you choose for?

Ronald Lemmen
Senior Associate Consultant at Avanade Netherlands



Modifying the history (associated) view filter

One of my friends, Michael Höhne, has blogged about how to change the default view of the associated view. This is a very common requested feature, also by my clients. Usually I inform them that it is not possible to change it, but that will change as of today. Take a look at the post at this address be thankful that Michael is willing to share all the codes just for the community :)

http://www.stunnware.com/crm2/topic.aspx?id=JS11



New blogger

As you can see I have changed the blogger settings to match the new blogger style. This does mean that labels are now used, the history browser is optimized and I should have some new features as author. I still have to find that out, but for now the template is a bit changed. I'll work on that soon!



FetchXML into a DataSet

Once upon a time in the Netherlands there was this developer who was working on MSCRM for quite some time. He started to notice that he had to search again and again for the same questions. Luckily he heard about the term 'blogging' and so he started to post the most frequent questions on a blog. The goal was to be able to quickly retrieve the information that he had found earlier. The fact that other people also read his blog and save themselves time to figure everything out themselves is just an added value to the blog.

Now with this knowledge you will understand why I post this piece of code:


private DataSet FetchDataSet(string fetchXml) {
CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
//service.UnsafeAuthenticatedConnectionSharing = true; //enable in secure migration programs

string strResult = service.Fetch(fetchXml);
DataSet ds = new DataSet();
System.IO.StringReader reader = new System.IO.StringReader(strResult);
ds.ReadXml(reader);
return ds;
}

I'm just typing this way to often and I prefer to use copy and paste.

Furthermore a link to a previous post on how to quickly create a fetchXML:
http://ronaldlemmen.blogspot.com/2006/11/using-advanced-find-for-fetchxml.html

And a link to another post on how to make sure that you do fetch all records and not only the first 5000:
http://ronaldlemmen.blogspot.com/2006/08/fetch-all-records.html



Copying CRM Reports

You will need to copy reports from a server to another server when you're working on a CRM project, right? From development to test, from test to staging, from staging to production. Usually I do add the reports to a release package and describe in the deployment manual how to deploy the reports and how to set the filter, categories etc.

Until today. Since today I will do this completely different. For a friend of mine I was searching for information on an error he receives with reporting:
An error has occurred during report processing. (rsProcessingAborted). Query execution failed for data set 'dsContracts'. (rsErrorExecutingCommand)
Unfortunately I did not find the anwer to that error, but I did land on this page: http://www.microsoft.com/dynamics/crm/using/deploy/changesrs.mspx

There is being described how reports can effectively be migrated from one deployment to another deployment! There are two executables which I overlooked: downloadreports.exe and publishreports.exe. By using these two executables you can download the reports and configuration settings for the reports and publish these reports and settings on another machine. That will shorten the deployment time a lot and make it less errorprone!

As you can see, everyday I learn something new. I hope this helps you as well.



This attribute map is invalid

Today I've been working on a nasty error which showed up during importing customizations. The error message was:

This attribute map is invalid. The selected attributes should be of the same type. The length of the source attribute and the formats should match. The targe attribute should not be used in another mapping."

To solve this error I have opened up the customizations.xml file and located the attribute maps ("ctrl + f" and search for AttributeMap). There were many custom build maps between exisiting entities. To find out which of these caused the error, I have removed them one by one until the error did not show up. That was the record that I needed to check and indeed that was the problem. The source entity did have this particular attribute set as a picklist and the destination entity had the type of the attribute set to bit. In the CRM system where I created the customizations file, both attributes were bits.

I hope this approach does help you solve the issue as well. Good luck!



CRM Blog list update

Today I received a comment on the post around CRM Blogs from David Yack. If I wanted to update his entry in the list. Ofcourse I did so. In fact, I have searched around the internet and updated the complete list. I believe all records are up to date. If any MS CRM related blog is still missing, please let me know. Here's a link to the list:

The official MS CRM Blog List:
http://ronaldlemmen.blogspot.com/2006/03/crm-blog-world.html



Import has encountered an error and has been canceled

Another error resolving post.

Here's the error:

Import has encountered an error and has been canceled. Your system has been restored to its original state. Import has been canceled because the ISV.Config file could not be imported. Check that the file format is valid.

Also connecting to the CRM Workflow Manager will probably not work. This error message is thrown:
The specified Microsoft CRM server is not responding. This might happen if it is currently unavailable, it is not a Microsoft CRM server, or you are not a valid user. For more information, contact your system administrator.
This is -probably- being caused by incorrect settings in the srf files. Check this by going to the CRM website folder (C:\inetpub\wwwroot\) and browse to the folder MSCRMServices. In that folder open the file bizusersdl.srf in notepad. In that file you do see a folder. Try to browse to that folder by copying the folder name and paste it in Windows Explorer. You'll see that the name is in short format like: c:\progra~1\micros~3\.... This format does not match the location of the crm folder.

I have been able to solve these errors by repairing CRM. Go to the control panel, click add/remove programs and select Microsoft CRM. Then choose for 'Repair'. Another option would be to follow the steps in kb859798 (partner source required). That KB article describes how to reset the .srf mapping in step 8.



Reducing the transaction log file size in MS SQL 2005

In the past, when using MS SQL 2000, it was fairly easy to reduce the file size used by the transaction log. Just backup the transaction log, perform a shrink command and then the file size has dropped to a minimum.

In MS SQL 2005 this won't work. If you do so, then the file might be shrinked a bit, but it is not very small yet. To do this you would need to do the shrink command (including reorganizing the pages to the beginning of the file) before doing the backup and once again after the backup. If you do so, then the file has been really minimalized.

To do this automatically, you would set up a maintenance plan. In that plan it would be sufficient to do a shrink and then the backup.

The reason that you have to shrink the file first has to do with the active log pages and the order of it. For more information on why you need to shrink the file first, take a look at this page: http://support.microsoft.com/kb/907511



Column on Channelweb

The power of choice. It's one of the powerful marketing slogans Microsoft uses for promoting Microsoft CRM. And it is the title of a column that I wrote for Channelweb. If you go to the website at http://www.channelweb.nl and browse to 'business software', then you'll find my article. Ofcourse you can also go to this link directly: http://www.channelweb.nl/artikel.jsp?id=1906469

The article takes a glimpse in what will be offered as soon as Microsoft released Titan. Warning, it is in Dutch though :)

Update: Here's the English version:
http://ronaldlemmen.blogspot.com/2007/04/english-version-of-column-on-channelweb.html



CRM as Development Platform

One of the questions that Steve Ballmer got after his keynote session was:

Question:
Love where we're going with CRM, it's beautiful. If we look at the CRM platform and generize it, what it really is, is a platform enabling tool. A framework for quickly developing and deploying any type of application. Make it web enabled, webservice enabled, all right out of the box. Do you plan to generize, or let me change that, productize that in a certain way? So... It's not CRM, CRM is the first implementation.

Answer:
Uhm... YES!
But today would not be the day for me to go through any more then that. You were right in understanding the direction and vision and path we need to take. And when we have details, we would certainly be glad to share them. But that is probably not today unfortunately. We're still working some things through.

So:
In the future we'll see what this exactly means...

Update:
Updated this post with the exact question and answer.



CRM Titan Release Date

No I don't have the release date yet, though I'd like to share the update from the Convergence with you. The release date has been set to the end of the year. Altough an exact date is not set, Brad made us believe that the target RTM date will be November, similar to the release of v3.0. I think that they will work as hard as they can to achieve this goal. That could ofcourse mean that features will be removed from scope prior to RTM.



CRM Live screenshots!

Hi all of you,


Just before going to sleep I'd like to share some images with you. They are images I made of the presentation of CRM Live during Steve Ballmers keynote. The presentation was given by Brad Wilson, General Manger for Microsoft CRM. The presentation actually was live from the internet, so these bits are online right at this time in the Microsoft data center! I'll give you some of the pictures that are best and some information around what is on the screen. You can click the images for a detailed image. Unfortunately I didn't had the best place to sit, but these are the best shots I could get off the screen.


After logging in via the Live Passport, Brad got this screen. He was able to select an organisation because he is assigned multiple accounts to multiple organisations. If you only have one organisation to choose from, then this screen will ofcourse be skipped.



This image shows the activity overview screen on the workplace window. The differences with CRM 3.0 as we see on the screen is the button "imports" on the left side of the screen between Calendar and Queues. Another change is the new module "resource center". This is a button under Settings. This resource center allows users to directly access the community and search for answers to their questions.

We can see on the title that the name here is "Microsoft Dynamics CRM 4 Community Technology Preview 1". There has not been any statement on the name yet, but it might be CRM 4. It also could become CRM 2008 because it will be released before the end of the year.




This is the last picture which is quite ok. On this shot you can see Steve Ballmer and Brad Wilson looking to the "Service Calendar" in the Service module.

During his presentation Brad mentioned something about Duplicate Detection as well. On the screens of the contacts and accounts, there has been shown a button which stated "Detect Duplicates". Thats all that has been said about this subject for now, but I think it will be great if the same functionality would be in the system as what is in the addon of c360.

Brad also shown the Outlook client for CRM Live. This worked just as fine as the webclient. Also modifications made to the sitemap are immediately replicated to the Outlook client.

Thats about all I have seen on the presentation, so thats all I was allowed to say. I have been said that I'll get access to these bits, if I do so then I'll post some clearer shots of the same screens that were shown today.

Now we just have to wait until the next releases in the TAP program and for some of you even end of the year... But it will be worth the wait!

Good night,

Ronald



Free Dynamics e-courses training

In the Convergence Update 2 I have mentioned that the E-Courses for Dynamics products are becoming available for free if you're inrolled in customersource or partnersource. Another MVP and Mujo who has replied to that posting have informed me that the e-courses are not available yet. They still get an access denied error message.

Now I've been in direct contact with the person in charge with this. He told me that not all the customer accounts got changed correctly. If you do not get access to the e-courses, then send an email to voice@microsoft. In the email make clear that you wish to get access to the dynamics e-courses, your company name as well as your account number (look in voice, accessible from the customer/partner source website).

Try again after confirmation that your account has been changed and start learning. The courses are great, especially for getting started or exam training!



Convergence Update 3

Hi all,

Just before going to sleep I'd like to share some interesting things which became public today. In Brad Wilsons presentation these facts were mentioned:
- Our Titan release is heading for release this year
- Early Titan code is in the hands of our partners trough our Technology Adoption Program (TAP)
- It's available via software bits and Live CRM instances
- TAP releases will continue throughout the year
- Early customer access via Live CRM will start in Q3
- A customer showcase program for a select number of on-premise customers will kick off in Q3
- Full RTM (Release to Market) and RTW (Release to Web) will occur by end of year

The rest is still a big secret, but tomorrow will be the great day. I'm looking forward to Steve's presentation and so should you!



Convergence MVP foto


Here's a picture just made some seconds ago. From left to right: JaAG, Ronald Lemmen, Frank Lee, Scott Colson. Matt Parks is running around somewhere as well, but he didn't make it to the photoshoot.

I'll add some more pictures of the event later.



Convergence Update 2

Another great day in San Diego.

Not too many announcements today yet, but i'm having a great time anyway. Ever since I've been here I've been in contact with a lot of people I mailed with a lot but never saw before. I met a lot of partners, other MVP's, ISV's, Microsoft employees and above all, people who just want to thank me for my active participation in the community.

A nice thing I'd like to share with you is the fact that the e-courses for dynamics products are now available for free (if you're enrolled in customer or partnersource). Take a look at this site: http://dynamics.microsoftelearning.com/catalog/default.aspx.
Together with another MVP I discovered that it didn't work for him, but it will definately be working soon.

Another announcement I forgot yesterday is around C360. They have acquired the rights to sell many applications of other ISV's. These include:
- ImportManager by CRM Extensions
- CRM Mobile by CWR Mobility
- Many others I do not remember :)

More updates to follow soon!



Convergence Update

This event is amazing!

I'm astonished by the set-up of this event, the attendees and all the good news which is being announced. I just attended the keynote session by Satya Nadella. There were made quite some interesting announcements:
- Microsoft Dynamics NAV 5.0 will be available on March 31 2007
- Microsoft Dynamics GP 10.0 will be available in June 2007
- Microsoft Dynamics SL 7 will be available in June 2007
- Microsoft Dynamics Client for Microsoft Office and SharePoint® Server, a new Dynamics client, will be released in May 2007
- Microsoft Dynamics Sure Step, a new tool set for implementation,
- Online finance community, a new online community being introduced today that enables finance professionals

Just a bit more information on some of the new products.

The Microsoft Dynamics Client for Microsoft Office and SharePoint® Server will be available for GP 10.0, AX 4.0 and SL 7 in May 2007. It will become available for Nav with the 5.1 release. For more information about the announcement see: http://www.microsoft.com/presspass/press/2007/mar07/03-12Day1DynamicsOfficePR.mspx

Microsoft Sure Step is an implementation methodology and a set of tools to make the implementation of Dynamics products a lot more predictable. In the keynote session a tool was shown which reflects a company structure on which you can drill down to each department, employee, process etc.

Online finance community is the first community released by Microsoft which includes blogs, rich forums, tagging, syndication and many other interaction methods. I can't wait for them to set up the community for CRM!

For more information on Sure Step and the new community see this announcement: http://www.microsoft.com/presspass/press/2007/mar07/03-12ConvergenceDayOnePR.mspx

All these announcements were made today, I cant wait for other announcements to be made. I can tell that the sessions on wednesday will be very interesing for CRM users ;) Make sure you're there!



Convergence 2007 San Diego

Both the MVP Summit as well as the Convergence are on exactly the same dates. Since I don't have a splitten personality, I'll go to just one of those events. This will be the Convergence! Feel free to contact me to make an appointment.

Join Me at Convergence Connect!

See you in San Diego!



Dutch champion Judo


This is my first post not related to CRM, C# or myself. Instead, this post is about my little sister Iris. We do both practice Judo, there’s just this little difference in the level we do play this. I’m doing it just for recreational purposes and she’s putting all effort in being the best.

And she does a great job! Last week she managed to beat all Dutch competition! Now she’s Dutch Champion for the 2nd time. The first time was back in march 2005. I’m very proud of this achievement and believe me, you’ll see her in the Olympics in just some years!

She has her own webpage on http://www.irislemmen.nl

Well done girl!



Set a lookup to NULL

Quite often I see this question in the newsgroup: How do I set a lookup to a NULL value?

Here is the answer for when you're working in server side code:


Entity.Lookup = new Lookup();
Entity.Lookup.IsNull = true;
Entity.Lookup.IsNullSpecified = true;


this also works for customers:


contact.parentcustomerid = new Customer();
contact.parentcustomerid.IsNull = true;
contact.parentcustomerid.IsNullSpecified = true;


If you're working in a client side javascript, then use this approach:


crmForm.all.lookupschemaname.DataValue = null;


Good luck!



A schema validation error has been detected

More and more MS CRM developers are experiencing this error message:


A schema validation error has been detected at line 21087, position 16.

This error shows up if you want to import a customizations file which has been exported from a system which has the Update Rollup package 1 installed.

There may be multiple reasons for this error to show up, but at least one of them is the existence of one of these lines in your code:

<VisibleToPlatform>0</VisibleToPlatform>
<VisibleToPlatform>1</VisibleToPlatform>

To solve the error message, just remove the line from the code or install the rollup package 1. For information and download of the package go here: http://support.microsoft.com/default.aspx/kb/922815



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



Error while importing via Tools -> Import

Today I got a question regarding importing via the CRM Import tool (Menubar -> Tools -> Import). Only a small number of records would import, all the others would not. Even though the source seemed fine, they did throw this error:


0x80040328: Number of columns does not match the header row


This appears to be related to the source anyway. When you save an file in Excel 2003 as csv, it does the saving correct for the first records. Then after about 15 rows it starts to leave away the last splitter signs if the columns are empty! That does indeed end up in the error as shown above. The solution is to move one of the columns which does have data for all records in there, to the last column. Then again export the file to csv and do the import. You should now be able to import the records as expected.



Multiple Avanade clients win Pinnacle award

The Convergence 2007 Pinnacle Awards recognize, honor, and celebrate customers that have achieved outstanding success with their Microsoft solutions as well as Microsoft partners’ solutions. The three categories are Role Based Customer Awards, Excellence Customer Awards and Industry Leadership Awards.

One of Avanade's clients, Wellmark, has won the Industry Leadership Award for Healthcare. Another client, Haldex, has won the Overall Excellence Award. Especially the Overall Excellence Award is special because it recognizes a customer that demonstrates amazing efforts in multiple Pinnacle Award categories. Customers are not nominated in this category. The Pinnacle Award judging panel will choose the Overall Excellence Award recipient from the nominations received in the other categories.

Congratulations to the Wellmark and Haldex teams as well as to all the other winners of Pinnacle awards: http://www.msconvergence.com/public/pinnacleawards.aspx



Guest star on mscrm team blog

Jim Glass e-mailed me some time ago. This was the content:


Ronald, would you like to be the CRM Team blog guest blogger for February? You can select any work day in February.

Let me know what you think,

JaAG


Obviously Jim is a man of less words, but with these words he did make me a very happy person. I was personally invited to write a blog post for the number 1 blog around MS CRM. And today is the day that my article is published. So go ahead and read the article about the many possibilities of many-to-many relationships in MS Dynamics CRM 3.0!

http://blogs.msdn.com/crm/archive/2007/02/15/many-to-many-relationships-in-ms-dynamics-crm-3-0.aspx



CRM with a SQL Server Named Instance

Today I ran into Kale David's blog. He posted a solution for running CRM on a SQL Server Named instance. The main point is adding a DWORD key to the registery.
Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\IgnoreChecks
Value: 1

See his blog for the full article.

Thanks Kale!



Territory manager cannot belong to other territory

If you're working with territories by using the SDK, you might get this error message:


0x80043805: Territory manager cannot belong to other territory


This happens if you want to make a specific user manager of two territories. This is possible, but you will have to make sure that the field 'territory' is set to NULL of this user. When you change the manager field of a territory by using the SDK, then the attribute territory of the user is changed as well. If you want to set this user as manager of the second territory, then you'd first need to clear this field.

Once again, I hope this saves you some time!



How to set the parent account via SDK

Last week I've been working on a migration application. One requirement in the application was to set the parent account of child accounts. It could be that a parent account has a parent account itself as well. So in my application I have followed this approach:
- For each account in the source, create an account in CRM 3.0
- For each account in the source, map the parent account to a crm accout and update the account in CRM 3.0

No big deal. This worked for the first thousands of accounts, but then suddenly an error message showed up:


<detail>
<error>
<code>0x80040237</code>
<description>Operation failed due to a SQL integrity violation.</description>
<type>Platform</type>
</error>
</detail>


After a lot of research and discussing with Luis Mazarío we found out in what situation this error occurres. It appeared that in some cases this error was thrown when the account already has subaccounts and I want to set the parent account. After even more research we found out that the source of this error was the code which I used to update the parent account. I was using a code like this:


Lookup lookup = new Lookup();
lookup.type = EntityName.account.ToString();
lookup.Value = gParentId;

account acc = service.Retrieve(EntityName.account.ToString(), gAccountId, new AllColumns());
acc.parentaccountid = lookup;
service.Update(acc);


Although it works most of the times, it is not the right code. This is the correct code:


Lookup lookup = new Lookup();
lookup.Value = id_parent;
lookup.type = EntityName.account.ToString();

account acc = new account();
acc.accountid = new Key();
acc.accountid.Value = id;
acc.parentaccountid = lookup;
service.Update(acc);


We're still digging a bit more on why the error occurred at all, but I hope this helps you!

Ronald



Uploading office 2007 (docx) files to CRM

On the newsgroups I've been talking to some people about uploading Office 2007 files. This appears to be not working as expected. There is a hotfix available for this issue now. Note: this hotfix is not included the rollup 1 package. Look at this page for more information: http://support.microsoft.com/default.aspx/kb/929488



Food for hungry IT-people

For 3 years I'm working for Avanade, the joint venture between Microsoft and Accenture. Since 2004 we're located in the Netherlands and we have managed to grow from 17 to 100 Microsoft professionals. Now I ask you to help us grow even further!

Worldwide we are an organization of more then 5000 professionals and we're looking for more people who are willing to be part of the worlds largest IT consultancy dedicated to Microsoft solutions. For job openings in an office near you, take a look at: http://www.avanade.com/careers/

So why the title "Food for hungry IT-people"? It's part of our recruitment campaign in the Netherlands. Everybody from the Netherlands or Belgium should definately take a look at this site: http://www.hongerige-iters.nl. It's a movie in which you are the main player! But that's life within Avanade. Here each person is a main player themselves!

Here are some unique points which makes at Avanade my company:
- Joint venture between Microsoft en Accenture
- Large projects with a pure focus on Microsoft technologies
- Being the first to work with new technologies (Even closer that you can imagine: I'm having contact with the CRM Product Team myself!)
- Both Solution Development and Infrastructure projects
- Extensive focus areas like Dynamics CRM and AX, Information Worker, BI, .NET Development and Enterprise Integration.
- Great development possibilities (You'll need to get MCPD or MCSE certified within a year or two)
- Worldwide knowledge sharing, efficient collaboration
- Positive, collegial and above all a professional mood

Feel free to ask me about working for Avanade. Ofcourse you can also directly contact one of my colleagues in Human Resources. For the Netherlands that will be Noortje: noortjet@avanade.com, for other countries look at: http://www.avanade.com/careers/

While applying you may mention my name. Only real CRM professionals read this blog right? :)

I'm looking forward saying: "Welcome to Avanade!" to all of you!



http://www.ronaldlemmen.com

Now even easier to remember! This blog is accessible from the url: http://www.ronaldlemmen.com

Still running on blogger's engine, but since I received my first ad's paycheck I'm able to serve you even better.

Ronald



The best solution?

Have you ever wondered what the best solution is? For example, what if somebody asks you 'What is the correct regex pattern that would match to any 3 digits from 0-9 but not match if those three digits are all zeros, e.g. 123 is a match but 000 is not a match?'

So what is the best solution? Is it:

([1-9]\d\d|\d[1-9]\d|\d\d[1-9])

This still would allow a string like 1234 to be correct.

Or this one:
^([1-9]{3}|[1-9][0-9][0-9]|[0-9][1-9][0-9]|00[1-9])

It still does allow ambiguity.

Then this regex:
^([0][1-9]{2})?([0]{2}[1-9])?([1-9]{2}[0])?([1-9][0]{2})?([0-9][1-9][0-9])?([1-9]{3})?([1-9][0][1-9])?$

This does work! Assuming that what is tested is a complete line, not a word.

Now is it the best solution? I don't think so. Would the person who's going to follow you up still be able to read that regex line? How long did it take to find out that line? What will you do if the requirements change?

What I want to illustrate is that sometimes it's better to think again about the question. Why not make two lines of code? First check if it contains 3 digits, regex should be something like /d:3, and then immediately check for the occurence of '000'. It is so much easier to read, less fault tolerant, quickly created etc.

So in short: keep in mind what is best for your client instead of what you can do with technique.

I just wanted to share this based on a discussion I had with some colleages and especially Douglas Johnston.



Showing a sorted list of the entities

When you want to show a list of entities, then you'd need the MetadataService. The code is not that hard to fetch a list of these entities. There's even an example in the SDK. The code to get all entities including custom entities would be:


MetadataService.MetadataService service = new MetadataService.MetadataService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
Metadata md = service.RetrieveMetadata(MetadataFlags.EntitiesOnly);
ArrayList alEntities = new ArrayList();

foreach (EntityMetadata em in md.Entities)
{
if (em != null)
alEntities.Add(em);
}


But if you do have this list, then how do you show this in a sorted way? Usually you could just say alEntities.Sort()... Too bad that doesnt work here because the collection exists out of a list of EntityMetadata instances. So how do we do this? Well, just create a EntityMetadataComparer based on the IComparer interface. Here's the code that you'll need:


using System;
using System.Collections;
using MetadataService;

/// <summary>
/// The class EntityMetadataComparer allows a collection of instances of the class EntityMetaData to be compared.
/// Use this Comparer class to sort for instance an ArrayList
///
/// Author: Ronald Lemmen
/// Company: Avanade
/// Date: 19 Jan 2007
/// </summary>

public class EntityMetadataComparer : IComparer
{
public enum SortOrder {
Ascending = 1,
Descending = -1
}

private int _modifier;

/// <summary>
/// EntityMetadataComparer constructor without sorting option. The default sorting option will be used: Ascending
/// </summary>
public EntityMetadataComparer()
{
_modifier = (int)SortOrder.Ascending;
}

/// <summary>
/// EntityMetadataComparer constructor with sorting option.
/// </summary>
/// <param name="order">Sort Order. Set this by using EntityMetadataComparer.SortOrder.[Ascending|Descending]</param>
public EntityMetadataComparer(SortOrder order)
{
_modifier = (int)order;
}

/// <summary>
/// Compare the two EntityMetadata objects based on their DisplayName
/// </summary>
/// <param name="o1">Object 1</param>
/// <param name="o2">Object 2</param>
/// <returns>The int return value of the string.CompareTo function</returns>

public int Compare(Object o1, Object o2)
{
EntityMetadata em1 = (EntityMetadata)o1;
EntityMetadata em2 = (EntityMetadata)o2;

if (em1.DisplayName == null)
em1.DisplayName = "";

if (em2.DisplayName == null)
em2.DisplayName = "";

return em1.DisplayName.CompareTo(em2.DisplayName) * _modifier;
}
}


Now you can sort your ArrayList with the command:

alEntities.Sort(new EntityMetadataComparer());


Or to sort with a sort option set:
alEntities.Sort(new EntityMetadataComparer(EntityMetadataComparer.SortOrder.Descending));


Good luck!



You've been tagged!

Aargh!

I've been tagged two times by now. Once by Nigel van Houten and once by Sander Schutten. It's a game on blogs. If you get tagged, you need to give 5 useless facts about yourself and tag 5 other people. So here are my 5 useless facts:

1) I do know both Sander and Nigel of the 'Hogeschool van Amsterdam'. Thats the school where we all have studied Information Technology.

2) During my studies I have stayed in Finland (Espoo) for half a year and another half a year in Germany (Hannover). Both stays were for my studies, but next to studying I had a great time there as well. I'd definately like to go to Finland once again. Just like Australia, New Zealand and and and... a lot of other countries I still want to visit.

3) Next to traveling and working I do spend a lot of time with my wonderful girlfriend. Her name is Nathalie and she's working as a biologist for a hospital in Amsterdam. Next to working there she's doing a study as well!

4) Next to traveling and working and spending time with Nathalie, I do play Judo and snowboarding. Since about half a year I do have black belt judo. With snowboarding I'm not that good yet. I wish to somewhen learn a 360...

5) Really useless, but if I didn't do computer science, I would be working with plants probably :)


Now its time to tag some others. Hereby I do tag:
- Nathalie : http://enpebe.web-log.nl/
- Aram : http://www.aramsmith.com/blogs/default.aspx
- Erik / Jeffry : http://www.cwrmobility.nl/weblog/
- Imran : http://microsoftcrm3.blogspot.com/
- Philip : http://www.philiprichardson.org/blog/

Happy tagging :)



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!



Virtual PC 2007 Beta: Keyboard Problems (repeating keys)

About everybody who's using the beta of VPC 2007 is facing the same problem: Keys are being repeated again and again. When searching around on the internet, I found that this bug has been submitted and will be fixed in the RTM. Furthermore I found that there is a temporary solution. The workaround is to use the idle_thread. Now how do you set this setting?

- Use Explorer and go to the %root%\Documents and Settings\%username%\Application Data\Microsoft\Virtual PC folder.
- Right-click the Options.xml file, and then click Edit.
- Add the following code to the file.


<virtual_machines>
<enable_idle_thread type="boolean">true</enable_idle_thread>
</virtual_machines>

Note: The <virtual_machines> tags should be at the same level in the code as the <virtual_network> and <virtual_gateway> tags.

The CPU for the host system will indicate 100% usage. Another option is to use RightMark RMClock and activate the "Run HLT commands when OS is idle" option. See Jan Tielen's blog for more info regarding that.