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

ISV-Folder

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:

VirtualPathProvider

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:

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

DynamicEntity

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:

Fiddler

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

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  https://localhost/loader.aspx and not https://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:

 123456
 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.

Anyways... Happy hacking!

J

Comments

  • Anonymous
    January 20, 2008
    PingBack from http://cake.musicnewsandviews.com/2008/01/21/few-development-tips-for-crm-40/

  • Anonymous
    January 23, 2008
    Question for you..   the directory under ISV.   Is it an Application or just a folder?     I can't seem to get my aspx page to work under CRM (works stand alone but not under CRM.. I'm thinking it has to do with the virtual path but I can't determine the actual error... (just get an "unhandled access exception")... Thanks in advance, Gary

  • Anonymous
    January 23, 2008
    The comment has been removed

  • Anonymous
    January 23, 2008
    Thanks Janne.    The DevErrors pointed me to the issue..  I'm trying to use WebParts and it seems it's not picking up my DB connection from my web.config..  So the error was an access error trying to create the DB in the app_data directory.    So I'll do some digging... Again appreciate the suggestion! Gary

  • Anonymous
    February 05, 2008
    Good artical! Hope to have more. Andrew

  • Anonymous
    February 29, 2008
    I touched upon your article through a google search, but I was hoping you can provide some assistance. I'm trying to build a custom aspx page and made a project in the ISV folder as you suggested.  I have code-behing on page_load to impersonate for active directory users.  In IIS, under the CRM website and the ISV folder, I've made my project and application. I export my ISV.config.xml file from CRM 4.0 to insert a new button with reference to the aspx project as "/ISV/MyApp/default.aspx", and proceeded to import the modified ISV.config.xml file.  I then click on the new button and get "Server Error in '/ISV/MyApp' Application' HTTP status 401: Unauthorized. Any clue on what steps i'm missing? Thanks

  • Anonymous
    April 15, 2008
    Have you tried your web.config loader in an IFD setting? Its not pretty!

  • Anonymous
    April 28, 2008
    Do you have to copy the assemblies from the main CRM web directory bin folder into the bin folder of the custom application under ISV?  Mine errored out with a "Microsoft.Crm.WebServices.Crm2007.CookieAndSoapHeaderAuthenticationProvider doesn't exist" until I copied the Microsoft.Crm.WebServices.dll over.  Or does this mean I need to install that one in the GAC?

  • Anonymous
    April 28, 2008
    Hi Nick! Just copy your own dll files to CRM web directory bin folder. And if you have created application folder under IIS then you have to change it to normal vfolder (and vfolder doesn't have bin underneath it). Then you should be fine. Anyways... Happy hacking! J

  • Anonymous
    April 28, 2008
    Hi Garry! I haven't tried "web.config loader" with IFD.... so most likely it will explode (if I understood your comment correctly) :-) But fortunately it is easy to fix. And most likely everyone has managed to get the idea that I had in mind anyway. And that is the most important thing for me. Anyways... Happy hacking! J

  • Anonymous
    April 28, 2008
    Got it - that worked great, thank you so much!

  • Anonymous
    June 30, 2008
    The comment has been removed

  • Anonymous
    November 18, 2008
    The comment has been removed

  • Anonymous
    February 21, 2009
    Thank you, I spend 2 days trying to figure out why web.config is not working. Could you please share with us, what's the best practice to store and retrieve ConnectionStrings, hostname, port other than web.config?

  • Anonymous
    March 06, 2009
    Thank you for the article. It is very helpful and has lots of good tips!

  • Anonymous
    June 08, 2009
    Thanks for such a nice article, this would really help people working in CRM !

  • Anonymous
    September 29, 2009
    Hello one question: I have put my page under ISV/Myapp and the dll under bin. I have change the ISVConfig to call /ISV/Myapp/... it only shows me a white window NOTHING else...ANY IDEAS???? please??

  • Anonymous
    September 30, 2009
    The comment has been removed

  • Anonymous
    December 08, 2009
    WHOOHOOOO!! This rocked my world: "For example query for word "searchword" with operator ConditionOperator.EndsWith is equal to ConditionOperator.Like with "%" + "searchword" :-)" thankyouthankyouthankyouthankyouthankyou!!! been trying every other operator in the hope that this was the case...

  • Anonymous
    August 11, 2011
    Thank you very much for this. This should be in the SDK! :)

  • Anonymous
    August 20, 2012
    Still useful 4 and a half years later. Thanks for this: "Microsoft.Crm.Sdk.Query.ConditionOperator vs. ConditionOperator through Web Service" I was unaware of that being the cause of the error message "InvalidOperationException: Instance validation error: '56' is not a valid value for Microsoft.Crm.Sdk.Query.ConditionOperator"