Thursday, February 19, 2009

Internet Facing Deployment (IFD)

Originally when MSCRM 4.0 came out it appeared that in order to set up a site with an Internet Facing Deployment it could only be done through the install. Meaning, once installed you would have to uninstall and then reinstall in order to get the IFD working.
Microsoft has recently come out with a tool to change any existing site from IFD to regular On-Premise setup. And on top of that they even have a Knowledge Base article that steps you through using the tool and changing your site set up. I know there have been some other blog posts about this but Microsoft just updated their documents on February 15, 2008, so there have been some additions/changes to what was originally put out there.
Here is the link to the Knowledge Base article (the download link for the tool is in the Knowledge Base article):
Microsoft Knowledge Base Article 948779

For more information on how to deploy a site as IFD take a look at this document that Microsoft has posted:
Microsoft Dynamics CRM 4.0 Internet Facing Deployment Scenarios

If you aren't sure what IFD is, or what it does for you or your customers, let me give you my understanding of it as there is minimal documentation on what it is, only documentation on how to set it up. Basically the IFD sets up Forms over Active Directory Authentication to the MSCRM pages externally to your deployment. What does that do for you?
Two things:
1. Users logging in externally will now see a sign in page where they don't have to input their domain, just their username and password. This is different from 3.0 in that MSCRM sites that were accessible over the internet would bring up the windows login prompt, requiring your domain and username.
2. It makes is so that you can use your Outlook client without a VPN. The Outlook client will first attempt to connect to the internal site, if it doesn't find it, it will kick over to the external address provided in the configuration wizard. As it tries to connect it will first try Active Directory Authentication, it will tell you that it fails and will attempt to connect using Forms over Active Directory Authentication.
Good luck out there!
Jan

Pop-up reminders in Microsoft Outlook for activities created in CRM

One of the GREAT new features in Microsoft Dynamics CRM 4.0 is the ability to get pop-up reminders in Microsoft Outlook for activities created in CRM. By default, Microsoft CRM will create reminders for every record it syncs into Outlook that has a due date. Consequently, a large number of customer records and a large number of automated activities means a large number of pop-up reminders!
Personally, I have found that sometimes these reminders were a bit too much for me and I found myself wishing I had less pop-ups. This is especially true if you don’t include the customer’s name in the activity subject because otherwise you will see a reminder dialog that looks like this (not terribly useful because all the records look identical).

Fortunately, I picked up a neat little registry setting trick from former CRM Product Manager Michael Lu. By adding the registry setting OutlookSyncDisableTaskReminders to your client computer (not the server) at HKEY_CURRENT_USER/Software/Microsoft/MSCRMClient and setting its value to 1, the Outlook client will not create reminders on activities it syncs into Outlook.

This solution won’t work for everyone, but I find that it works pretty well for me.
WARNING: Using Registry Editor incorrectly can cause serious, system-wide problems that may require you to re-install Windows to correct them. Only administrators will have the necessary permissions to perform this modification. Microsoft cannot guarantee that any problems resulting from the use of Registry Editor can be solved. Use the Registry Editor at your own risk.
Cheers,
Jan

Development and Customization tips for CRM 4.0

If you are working with Microsoft Dynamics CRM 4.0 and your planning to do code customizations then you probably should keep on reading. And why am I talking about code customizations and not just customizations? Well just because in CRM you can do a lot without a single line of code... and this time I want especially to talk about the code customizations.
I have few topics on my mind that aren't related in anyway but I'm going to sum up them into this post:
1) Custom ASPX pages => Weird VirtualPathProvider error
2) RetrieveMultipleRequest and DynamicEntities
3) Microsoft.Crm.Sdk.Query.ConditionOperator vs. ConditionOperator through Web Service
4) Using your own Web.config to store settings
Okay here we go!
Custom ASPX Pages => Weird VirtualPathProvider error
First of all... If you want to create your own custom ASPX pages, then THE location for the files is the ISV folder (any other place would be unsupported):
I personally prefer adding my own ASPX pages to another folder (i.e. C:\MyApp\Pages) and just create virtual folder under IIS ISV-folder. And all your DLLs should go to GAC. I used the _should_ word just because during development time you might want to copy your DLLs to bin folder directly. It makes life a lot easier (and faster!) because you can just drop them over and you don't have to do any IISRESET in order to test your chances. BUT at the production that is definitely no-go approach.
So you have created you nice Page.aspx and everything should be fine... but you get pretty interesting exception:

The VirtualPathProvider returned a VirtualFile object with VirtualPath set to '/CRM/ISV/Page.aspx' instead of the expected '//CRM/ISV/Page.aspx'.
You most likely start blaming CRM for that error... but your wrong :-) This exception happens if you have compilation errors in your file. That can happen easily if you have i.e. typo inside your .aspx file. But that error message doesn't help you much when your trying to sort it out. So open up your Page.aspx and start fixing the error using regular debugging methods (like narrowing down the code to get the location that causes the error).

RetrieveMultipleRequest and DynamicEntities
If you have added some new entities and you used following code to retrieve them:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
// ...
Microsoft.Crm.SdkTypeProxy.CrmService service =
new Microsoft.Crm.SdkTypeProxy.CrmService();
service.CrmAuthenticationTokenValue = token;
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
service.UnsafeAuthenticatedConnectionSharing = true;
Microsoft.Crm.Sdk.Query.QueryExpression queryExpression =
new Microsoft.Crm.Sdk.Query.QueryExpression();
queryExpression.EntityName = "mcs_mycustomentity";
queryExpression.ColumnSet = new Microsoft.Crm.Sdk.Query.AllColumns();
Microsoft.Crm.SdkTypeProxy.RetrieveMultipleRequest request =
new Microsoft.Crm.SdkTypeProxy.RetrieveMultipleRequest();
request.Query = queryExpression;
service.Execute(request);
You'll probably hit this on the line 19:
And it's pretty obvious that the error message...
Exception: System.InvalidOperationException: There is an error in XML document (1, 503). ---> *System.InvalidOperationException: The specified type was not recognized: name='mcs_mycustomentity'
...doesn't help you that much :-) Okay now we need to start debugging and sort this thing out. First I'll take use try Fiddler and see what happens:
And if you look carefully enough you see something interesting: ReturnDynamicEntities=false (marked as red). And of course that rings a bell... your query is trying to return entity that would have been returned as dynamic entity. And if that's not allowed then you get your weird InvalidOperationException. Luckily that's easy to fix... just add this one line of code:
1
request.ReturnDynamicEntities = true;
Put that line of code before Execute call and you'll be fine.
Another way would be using directly the Web Reference to retrieve the CrmService. Then you could use strongly typed classes to implement your queries and you wouldn't have to work with DynamicEntities. If you can choose this option then I strongly recommend it. But if you need to create code that works with multiple tenants and you need to use entities that doesn't exist in all tenants... then you need to use DynamicEntities.

Microsoft.Crm.Sdk.Query.ConditionOperator vs. ConditionOperator through Web Service
If you use Microsoft.Crm.Sdk.Query.ConditionOperator you have 59 operators (I calculated them by hand so let say ~59 :-) and if you use ConditionOperator through the web service you have only 48 operators. And if you use one that doesn't exist in both i.e. Microsoft.Crm.Sdk.Query.ConditionOperator.EndsWith in your queries you'll get this InvalidOperationException:
Like the error message says "InvalidOperationException: Instance validation error: '56' is not a valid value for Microsoft.Crm.Sdk.Query.ConditionOperator" you can't use those values since they haven't been implemented in the other one. I just wanted to give this hint that you don't scratch your head too much if you get this error :-) And of course you can achieve the same functionality using the other operators so this is NOT limiting factor. For example query for word "searchword" with operator ConditionOperator.EndsWith is equal to ConditionOperator.Like with "%" + "searchword" :-)
Using your own Web.config to store settings
You probably know that it's strickly forbidden to touch web.config of CRM installation (file that exists in CRMWeb folder). So if you need to store some custom stuff in your own web.config you may think that it's piece of cake. So let's say that you put your own web.config into "/ISV/MyApp/"-folder. If you then use your CRM with the default organization (your url is http://localhost/loader.aspx and not http://localhost/MyOrg/loader.aspx) you can access your web.config appSettings as you would in any ASP.NET application. But if you use it through "the long" url where your organizations name is at the url too... then you probably notice that you can't access the web.config as you would expect. You'll only get empty parameters from your web.config even if your sure that everything is fine. Reason for that is actually pretty simple: VirtualPathProvider. Since CRM 4.0 now supports multiple tenants you don't actually access your tenants from "physical" urls anymore. They are now virtualized so that you can have any number of tenants at your system. But this makes your life a bit harder since you need to "load" your own web.confg in your code. It's not difficult but you just need to know what to do. Here's is small example:
1
2
3
4
5
6
using System.Configuration;
using System.Web.Configuration;
// ...
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("/ISV/MyApp");
string myParameter = configuration.AppSettings.Settings["MyParameter"].Value;
In line 5 you just specify virtual path of your web.config. After that you can use settings normally