C#, MS CRM, MS CRM4, Dynamics Crm »

[24 Jan 2011 | 1 Comments]

On my current Microsoft Dynamics Crm project we have done a lot of customisations, both creating custom pages and manipulating the existing crm pages via the OnLoad method. This post describes a method of ensuring the load order plus the benefit in increasing performance compared with loading multiple external JavaScript files the more conventional way.

The existing approach

One of the big problems we faced was making sure that the JavaScript files load in a specific order. This is because we have got some common functions in a file which can be re-used in multiple pages. These need to be loaded before the main page JavaScript file which might call one of the common functions.

There are a number of good blog post about this topic so I won’t go over this ground again. Here are a few articles I’ve found:

http://danielcai.blogspot.com/2010/02/another-talk-about-referencing-external.html
http://www.henrycordes.nl/post/2008/05/27/External-js-file-and-CRM.aspx

What is different with this new approach

The solution that I propose here was the brainchild of @njwatkins who came up with the idea of implementing at generic .net http handler as part of our custom webpages proejct that would read in the various external JavaScript files, in the correct order, compress them and then stream them back to the browsers as a single JavaScript file.

Unfortunately I cannot post the source code of the handler we use on this project here but a google round on topics like Gzip and StreadReaders and Handlers should enough to get most developers to a solution.

So the is script that goes on the onload of the Crm Enitty:

function loadJavaScript(file, onComplete) {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = file;
        if (onComplete) {
            script.onreadystatechange = function() {
            if (this.readyState == 'complete' || this.readyState == 'loaded') {
                onComplete();
            }
        }
    }
    document.getElementsByTagName('head')[0].appendChild(script); 
};

loadJavaScript('/ISV/Northwind/ExternalJavaScript/contact.ashx', function() { if (EntityFormOnLoad) { EntityFormOnLoad(); } });

The contact.ashx file is the handler that does all the work and where you would define which JavaScript files to include and does the merging and compressing. It means you only need to load one external JavaScript reference and you can guarantee which order they external files will be loaded.

Other benefits of this approach is that you can control things like caching length which can be a problem when changing and deploying new external files to clients. We achieved a significant saving on load time of the javascript from 900ms down to 200ms using this approach and we have ideas on how to improve it further but this is as far as we have got today!

MS CRM4, MS CRM, C#, programming, Software Developement »

[17 Sep 2010 | 0 Comments]

Now this might seem like something that would be easy to do but I’ve just spent 2 days struggling to do just this because of what I consider a bug in one of the SDK wrappers. I have now found a work around to enable unit testing which I will share with you now.

The Error message

Test method XrmEntityWrappers.Tests.CaseEntity.GetCaseByTicketNumber threw exception:  System.TypeInitializationException: The type initializer for 'Microsoft.Xrm.Client.Caching.Cache' threw an exception. --->  System.IO.DirectoryNotFoundException: Could not find a part of the path 'appDomain=UnitTestAdapterDomain_ForC:\Projects\Thg.Ohov.Crm\SourceCode\Thg.Ohov.Crm\TestResults\dh27_WIN-51UPWVCUQ6V 2010-09-16 18_21_40\Out\XrmEntityWrappers.Tests.dll:key=Microsoft.Xrm.Client.Caching.InMemoryCacheProvider'..

System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
b__0(Object userData)
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
System.Threading.Mutex..ctor(Boolean initiallyOwned, String name, Boolean& createdNew, MutexSecurity mutexSecurity)
System.Threading.Mutex..ctor(Boolean initiallyOwned, String name)
Microsoft.Xrm.Client.Threading.MutexExtensions.Lock(String key, Int32 millisecondsTimeout, Action`1 action)
Microsoft.Xrm.Client.Threading.MutexExtensions.Get[T](String key, Int32 millisecondsTimeout, Func`2 loadFromCache, Func`2 loadFromService)
Microsoft.Xrm.Client.Threading.MutexExtensions.Get[T](String key, Int32 millisecondsTimeout, Func`2 loadFromCache, Func`2 loadFromService, Action`2 addToCache)
Microsoft.Xrm.Client.Threading.MutexExtensions.Get[T](String key, Func`2 loadFromCache, Func`2 loadFromService, Action`2 addToCache)
Microsoft.Xrm.Client.Caching.InMemoryCacheProvider.GetExtendedCache()
Microsoft.Xrm.Client.Caching.CacheManager.GetExtendedCache()
Microsoft.Xrm.Client.Caching.Cache..cctor()
Microsoft.Xrm.Client.Caching.Cache.Get[T](String label, Func`2 load)
Microsoft.Xrm.Client.CrmConnection..ctor(String connectionStringName, String connectionString)
Microsoft.Xrm.Client.CrmConnection.Parse(String connectionString)
Thg.Ohov.Crm.Core.XrmEntityWrappers.XrmAdapter..ctor() in C:\Projects\Thg.Ohov.Crm\SourceCode\Thg.Ohov.Crm\Core\XrmEntityWrappers\XrmAdapter.cs: line 28
Thg.Ohov.Crm.Core.XrmEntityWrappers.incident.get_XrmAdapter() in C:\Projects\Thg.Ohov.Crm\SourceCode\Thg.Ohov.Crm\Core\XrmEntityWrappers\incident.cs: line 25
Thg.Ohov.Crm.Core.XrmEntityWrappers.incident.GetIncident(String caseId) in C:\Projects\Thg.Ohov.Crm\SourceCode\Thg.Ohov.Crm\Core\XrmEntityWrappers\incident.cs: line 44
XrmEntityWrappers.Tests.CaseEntity.GetCaseByTicketNumber() in C:\Projects\Thg.Ohov.Crm\SourceCode\Thg.Ohov.Crm\XrmEntityWrappers.Tests\CaseEntity.cs: line 23

The reason for the error

The Microsoft.Xrm.Client.dll tries to create a Mutex object with the

Thread.GetDomain().FriendlyName;

When running ordinary in a console app or web app this is not a problem as the FriendlyName does not contain any ‘\’ characters. However UnitTest frameworks do put ‘\’ characters in the GetDomain().FriendlyName which then causes the Mutex object to throw a ‘System.IO.DirectoryNotFoundException’.

The fix

The real fix is for Microsoft to update the Microsoft.Xrm.Client.dll so that it doesn’t put any ‘\’ characters into the Mutex constructor. However my work around for this is thanks to Nick Watkins who found this article on how to change the GetDomain().FriendlyName

http://www.timvasil.com/blog14/post/2008/11/Fixing-Instance-names-used-for-writing-to-custom-counters-must-be-127-characters-or-less.aspx

The key bit of code being this if you want to set the FriendlyName to ‘Test’ (which doesn’t have any ‘\’ characters!):

typeof(AppDomain).GetMethod("nSetupFriendlyName", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(AppDomain.CurrentDomain, new object[] { "Test" });

To rename the GetDomain().FriendlyName before calling any of the wrapper code in the unit tests. So the test might look a bit like this:

[TestMethod()]
        public void GetIncidentTest()
        {

typeof(AppDomain).GetMethod("nSetupFriendlyName", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(AppDomain.CurrentDomain, new object[] { "Test" });

            string caseId = "1234"; // TODO: Initialize to an appropriate value
            incident expected = null; // TODO: Initialize to an appropriate value
            incident actual;
            actual = incident.GetIncident(caseId);
            Assert.AreEqual(expected, actual);

}

Summary

I’m happy now I can unit test my custom code that uses the xRM Wrappers and I hope that my support call with Microsoft will result in the SDK dll being updated.

MS CRM4, MS CRM, MSBuild »

[10 Feb 2010 | 0 Comments]

I have just been setting up a new Crm project using the MS Crm4 Developer toolkit. Hats off to the guys at MCS, its a great bit of work and is packed full of amazing features which if used properly will ensure extra quality and best practice in the development of a Crm4 solution.

However I did have a bit of a problem with it which I have managed to fix – the default DevBuild.bat MSBuild script would not register my plugins if I ran my Crm server on any port other than 80!

I had followed the documentation about changing the server names etc. but whenever it tried to register my plugins the build failed.

It turns out that the MSCrm 4 Developer toolkit will only play nicely out of the box if you are running Crm on port 80, I am running on port 5555 though! After More...

debug, Javascript, MS CRM »

[27 Aug 2008 | 0 Comments]

This is a very short but I believe very useful post. How do you step into javascript code that you have written for the onload / onsave event.

Make sure that your IE advanced options has 'Disable Script Debugging' unchecked. More...