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.