Saturday, December 17, 2011

CRM 2011 Diagnosing "Your Changes have not been saved" warning

Every one in a while, I will encounter a form where I open the form but don't change anything.  When I close the form, I get a pop-up that "Your changes have not been saved.  To stay on the page so that you can save you changes, click Cancel." (CRM 4)  In CRM 2011 you will get a CRM-styled Yes/No dialog instead.  Either way, you must address the pop-up to continue.  If you're getting a lot of these, your CRM experience will quickly degrade.

I have mainly seen this on forms which we've added customization to (JavaScript or IFRAMEs which reach in to the parent).  Luckily, it is fairly easy to diagnose using the IE Developer Tools.  Simply press F12 and go to the script tab.

Then paste in the following script to figure out the field in question:

CRM 2011:
// Get all attributes
var y = frames[0].Xrm.Page.getAttribute();
for(var x in y){
  var z = y[x];
  // If we can get is dirty on them, and they are dirty, print the name
  if (z.getIsDirty && z.getIsDirty()){
    console.log(z.getName());
  }
}

CRM 4:
// Not really an easy way to enumerate fields in CRM 4
for(var x in document.all){
  var y = document.all[x];
  // Check for existence of IsDirty method
  if (typeof(y.IsDirty) != "undefined" && y.IsDirty &&
      y.tagName != 'FORM')
    console.log(y.id);
}

And now we have our dirty field(s):

If you have any weird scenarios which cause a seemingly "faux" is dirty, post them here so we can keep a list.  So far I've found that:
  1. Entity reference fields in CRM 2011 wrongly report as being dirty if the committed data has untrimmed white-space OR has consecutive white-space.


Monday, July 18, 2011

CRM 2011 - Remove Pending E-mail Warning on Login

Want to get rid of that annoying Pending e-mail warming you see every time you log in to your Microsoft CRM 2011 system?  


Modify your url to open directly to page main.aspx with the skipNotification query string parameter set to 1.


For example, if you are on-premise and use a URL like so:
http://contoso:5555/{Organization Name}


use the following instead:
http://contoso:5555/{Organization Name}/main.aspx?skipNotification=1


If you are claims-based (IFD) and use a URL like so:
https://{Organization Name}.contoso.com:5556


use the following instead:
https://{Organization Name}.contoso.com:5556/main.aspx?skipNotification=1


For on-line customers using a url like:
https://{Organization Name}.crm.dynamics.com


use the following instead:
https://{Organization Name}.crm.dynamics.com/main.aspx?skipNotification=1


Save it to your bookmark and stop growling (if you're like me) every time you open up CRM!


Update 2016/10/03:  Note on-premise customers can implement a registry key to disable this warning.  Please refer to DisablePendingEmailReminder http://social.technet.microsoft.com/wiki/contents/articles/13661.dynamics-crm-2011-quick-optimization-guide.aspx

Sunday, July 17, 2011

Microsoft CRM 4 Plug-in Serialization exception in CRM 2011

This is how one blogger solved a program plug-in after converting from CRM 4 to CRM 2011. The message was below (which you're probably getting if you're landing here).  In my case I was using the Property bags to pass values between plug-in instances (in this case from a syncronous plug-in to an asyncronous plug-in).  The problem was I was using the CRM 4 types (Lookup, CrmNumber, CrmMoney) as the values in the bag.  Apparently those assemblies weren't available where the serialization of the context occurs.  The solution was to switch to using the GUID, integer, decimal, etc.

If you get this for another reason, leave a comment and I'll update the post with other possible solutions.

<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">

  <ErrorCode>-2147220970</ErrorCode>

  <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic">

    <KeyValuePairOfstringanyType>

      <d2p1:key>CallStack</d2p1:key>

      <d2p1:value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)

   at WriteKeyValuePairOfstringanyTypeToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )

   at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)

   at WriteParameterCollectionToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )

   at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)

   at WriteAsyncOperationDataToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )

   at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)

   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)

   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)

   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)

   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)

   at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph)

   at Microsoft.Crm.Extensibility.AsynchronousStep.SerializeAsyncData(PipelineExecutionContext context)

   at Microsoft.Crm.Extensibility.AsynchronousStep.Execute(PipelineExecutionContext context)

   at Microsoft.Crm.Extensibility.Pipeline.Execute(PipelineExecutionContext context)

   at Microsoft.Crm.Extensibility.MessageProcessor.Execute(PipelineExecutionContext context)

   at Microsoft.Crm.Extensibility.InternalMessageDispatcher.Execute(PipelineExecutionContext context)

   at Microsoft.Crm.Extensibility.ExternalMessageDispatcher.ExecuteInternal(IInProcessOrganizationServiceFactory serviceFactory, IPlatformMessageDispatcherFactory dispatcherFactory, String messageName, String requestName, Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, ParameterCollection fields, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId, Guid transactionContextId, Int32 invocationSource, Nullable`1 requestId, Version endpointVersion)

   at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType)

   at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.Execute(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType)</d2p1:value>

    </KeyValuePairOfstringanyType>

  </ErrorDetails>

  <Message>System.Runtime.Serialization.SerializationException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #3B579A6A</Message>

  <Timestamp>2011-07-18T19:23:54.5853968Z</Timestamp>

  <InnerFault i:nil="true" />

  <TraceText i:nil="true" />

</OrganizationServiceFault>

Thursday, July 7, 2011

CRM 2011 Bulk Edit - Scripts Not Running? Fields disabled?

Similar to MSCRM 4, events won't fire by default in CRM 2011.  Also, any field with an onchange event will be rendered disabled!  There is a nasty way to go about it enabling these fields or allowing your scripts to execute.

Export the entity you wish to enable scripted bulk edits for in an unmanaged solution.  Un-zip the package and open up customizations.xml.

To allow an event, follow the below xpath which will take you to the events for a form.  If you have multiple forms, you may repeat for each form (TBD:  not sure if these appear under systemform or not).  If you're enabling for multiple entities, you may repeat for each entity.

\\ImportExportXml\Entities\Entity[@name='new_entityname']\FormXml\forms[@type=’main’]\systemform\form\events\event[@name=’onload’]


This xpath takes us to the onload event for the main form of the new_entityname entity.  To this node, we need to add the BehaviorInBulkEditForm attribute.

<event name="onload" application="false" active="true" BehaviorInBulkEditForm="Enabled">

  • Enabled - If this is an event related to a field (onchange), then the field will be enabled.  The script will run.
  • EnabledButNoRender - If this is an event related to a field (onchange), then the field will be enabled.  However, the script will not run.
  • Disabled - (default) If this is an event related to a field, then that field will be disabled.  The script will not run.


Official knowledge base article:  http://support.microsoft.com/kb/949941

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?

Friday, April 29, 2011

Migration to CRM 2011 - The custom activity does not contain a valid CrmWorkflowActivity attribute.

We've started our migration from Microsoft Dynamics CRM 4 to CRM 2011. We've developed the full suite of customizations, Custom Workflow Activities, Plugins, ISV folder code, lots and lots of JavaScript, custom entities, etc.

So far the upgrade has been going very well. I'm very impressed by solutions in CRM 2011 compared to the quirky things I had to do to get my entities moved across organizations in CRM 4 (We have about 250 custom entities - with many relationships between many of these entities).

While migrating our development instance recently we were hitting the error
"The custom activity does not contain a valid CrmWorkflowActivity attribute."
during the UpgadeWorklowActivity step (near the end of the upgrade). Using Process Monitor from System Internals I was able to track it down to a failure just after trying to load the Microsoft.Crm.Sdk.dll (version 4.0). I had deployed the aseembly to the Server\bin\assembly folder with my CustomWorkflow library assembly.

Seeing this, I tried several things (removing the DLL, gacutil, etc). Each time waiting 1 1/2-2 hours for the migration to fail.

Frustration was kicking in and I finally decided to remove the 4.0 DLL (from everywhere) and restart the machine and try again. Success! I hope this helps someone else, remove the 4.0 assembly, reboot your server, try again.