Thursday, June 30, 2011

YUI Compessor .Net - My MSBuild

For integrating JavaScript or CSS compression in to your .net projects YUI Compressor for .Net is a great find. It is both easy to integrate and simple to start the build process.

To integrate I simply unzipped the download (MSBuild folder containing EcmaScript.NET.modified.dll, MSBuild.xml, New BSD License.txt, and Yahoo.Yui.Compressor.dll) into a solution folder in my project.

One thing I didn't like about the default MSBuild.xml was that it assumes you want all javascript or css files to end up in a single, respective output.  For instance, minifying/compressing one or more scripts into a single script for deployment.  However, in my case I need each script to be seperated.  If you work with Microsoft Dynamics CRM you know what I mean... a form script for each customized entity. What I end up doing is 

  1. Add my script to my project (in source control).  For example: ( myscript.js)
  2. Add a second script in the same folder.  For example: (myscript.min.js)
  3. Selecting the second script in visual studio, go to File, Source Control, Exclude from source control.
What this does is lets my second script appear in my project (it is now included in the vsproj file list) but keep a second copy out of source safe.  Then we can take a look at my modified MSBuild.xml (original msbuild.xml).  What I did here was just use the simple properties of MSBuild.xml to allow me to build a single input to a single output file from the command line, and make it re-usable so I don't have to create another MSBuild.xml for every one of these!


<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
  <UsingTask
     TaskName="CompressorTask"
     AssemblyFile="..\MSBuild\Yahoo.Yui.Compressor.dll" />

  <PropertyGroup>
    <LineBreakPosition Condition=" '$(LineBreakPosition)'=='' ">-1</LineBreakPosition>
  </PropertyGroup>

  <Target Name="MyTaskTarget">   
    <ItemGroup>   
      <JavaScriptFiles Include="$(JavaScriptInputFile)"/>
    </ItemGroup>
    <CompressorTask
       CssFiles="@(CssFiles)"
       DeleteCssFiles="false"
       CssOutputFile="$(CssOutputFile)"
       CssCompressionType="YuiStockCompression"
       JavaScriptFiles="@(JavaScriptFiles)"
       ObfuscateJavaScript="True"
       PreserveAllSemicolons="False"
       DisableOptimizations="Nope"
       EncodingType="Default"
       DeleteJavaScriptFiles="false"
       LineBreakPosition="$(LineBreakPosition)"
       JavaScriptOutputFile="$(JavaScriptOutputFile)"
       LoggingType="ALittleBit"
       ThreadCulture="en-us"
       IsEvalIgnored="false"
           />
  </Target>
</Project>

Changes to the original:

  1. Commented out JavaScriptOutputFile in //PropertyGroup
  2. Added LineBreakPosition in //PropertyGroup with my default value (-1 is off)
  3. //ItemGroup/JavaScriptFiles only includes the property $(JavaScriptInputFile)
  4. In //CompressorTask, LineBreakPosition now references the property $(LineBreakPosition) instead of the static value -1.
  5. Note that comments have been removed to keep the post sane, see the original above.
In my project pre-build I then build single scripts like so:   (in this example MSBuild.xml is in the MSBuild folder of the project I'm building)


$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)MSBuild\MSBuild.xml"
     /p:JavaScriptInputFile=$(ProjectDir)_script\Foobar\foobar1_library.js
     /p:JavaScriptOutputFile=$(ProjectDir)_script\FooBar\foobar1_library.min.js

$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)MSBuild\MSBuild.xml"
     /p:JavaScriptInputFile=$(ProjectDir)_script\foobar_ui.js
     /p:JavaScriptOutputFile=$(ProjectDir)_script\foobar_ui.min.js

In this example MSBuild.xml is in a folder (MSBuild) at the same level as each of my projects.  I keep line breaks as I'm trying to find a bug in the script.  Additionally, the script is being copied to two other files in other projects.  Because this wasn't an automated process before, each of these scripts was in source control.  I've removed the files from source control so we only have one copy in there, but.. the files will be read-only on other developers machines until they do a clean get from source control.  Thus, I remove the read-only attribute so I don't have problems updating those copies.


$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)..\MSBuild\MSBuild.xml"
     /p:JavaScriptInputFile=$(ProjectDir)my_big_library.js
     /p:JavaScriptOutputFile=$(ProjectDir)EmbeddedResources\my_big_library.min.js
     /p:LineBreakPosition=0

attrib -R $(ProjectDir)..\OtherProject1\_static\my_big_library.min.js

copy $(ProjectDir)EmbeddedResources\my_big_library.min.js
     $(ProjectDir)..\OtherProject1\_static\my_big_library.min.js

attrib -R $(ProjectDir)..\OtherProject2\Scripts\my_big_library.min.js

copy $(ProjectDir)EmbeddedResources\my_big_library.min.js
     $(ProjectDir)..\OtherProject2\Scripts\my_big_library.min.js

Friday, June 10, 2011

CRM 2011 RetrieveMultiple (QueryExpression) no longer returning the primary key

As we migrate our project from CRM 4 to CRM 2011 it was apparent that some of our "supported" code wasn't even working.  One such case was using the QueryExpression/RetrieveMultiple, (we're still using the 2007 end points).  The problem was we've always assumed the primary key ALWAYS comes back with a RetrieveMultipleRequest.

As it turns out, in Dynamics CRM 2011 even the 2007 end points behave a little differently.  When setting Distinct to true in the query expression, the primary key won't automatically be returned (if its not in the ColumnSet).

Obviously, this points at a misuse of the distinct keyword.  If I really needed distinct, I wouldn't need the primary key.  If I really needed the primary key, I wouldn't be getting distinct records.  Hope this saves someone else some time.

Another breaking change in the 2007 end points is the RetrieveUserPrivileges request no longer functions.  I don't have a work-around for this other than to upgrade to the 2011 end points.  And I haven't found a way to contact the 2011 end points in the ISV folder like we could in CRM 4.  Unfortunate.  Luckily I'm able to move a lot of the privilege checks to the ribbon.

What breaking CrmService messages have you found when using the 2007 end points in CRM 2011?