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:

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:

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


Anyways... Happy hacking!

J

Comments (21)

  1. Gary P says:

    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

  2. Hi Gary!

    I have following setup for the folder:

    Script source access: No

    Read: Yes

    Write: No

    Directory browsing: No

    Application name: –

    Execute permissions: Scripts only

    You can modify "DevErrors=On" to the CRMWeb Web.config. It gives you much more information if you put it on. Remember… don’t put it to true at the production env πŸ™‚

    Hopefully this helps… if not.. re-post question.

    Anyways… Happy hacking!

    J

  3. Gary P says:

    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

  4. Andrew says:

    Good artical! Hope to have more.

    Andrew

  5. Andrew N says:

    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

  6. Have you tried your web.config loader in an IFD setting? Its not pretty!

  7. Nick Galler says:

    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?

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

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

  10. Nick Galler says:

    Got it – that worked great, thank you so much!

  11. john code says:

    Regarding the Microsoft.Crm.Sdk.Query.ConditionOperator-Topic:

    If you check the definition of the Enum (by typing it into the code editor, right-clicking and choosing "Go to Defintion") you’ll see a few value definitions with the tag [XmlIgnore] included; these are the ones that you can not use via the WebService. Just thought i’d share the info…

    And, oh: i would’ve scratched my head bloody, if it hadn’t been for your post. THX!

    Keep coding,

    jc

  12. Mira Fahmy says:

    i used your method to read values from my application web.config and it worked just fine…But i still has an error from using ajax ..javascript error ‘Sys’ is undefined , i could manage it by adding a line to the CRM web.config under HttpHandlers but the problem is that i want to get rid of the javascript error withoud editing the CRM web.config

  13. merlin says:

    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?

  14. Match says:

    Thank you for the article. It is very helpful and has lots of good tips!

  15. Nishant Rana says:

    Thanks for such a nice article, this would really help people working in CRM !

  16. Annita Veneti says:

    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??

  17. Hans says:

    For VirtualPathProvider. Wouldn’t it be better to develop from the ISV folder, and deploy it as a separate package elsewhere?

    Everything that screws up the productive dev cycle is a no go for me.

  18. Pano says:

    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…

  19. Dean says:

    Thank you very much for this. This should be in the SDK! πŸ™‚

  20. Greg says:

    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"

Skip to main content