Posted on 18. January 2012

iPad killed the Silverlight star?

After the initial Statement of Direction in May and then the interview with Brad Wilson the then general manager of Dynamics CRM, there has been quite some excitement about cross-browser support for Dynamics CRM 2011. The timescale given was "the first half of 2012" being release with UR8. The thought of access to Dynamics CRM from an iPad is quite an attractive prospect I have to admit - and I'm really excited to see how Microsoft are going to structure the CRM user interface when they deliver it through cross browser HTML5.

  • Are we going to get a single cross browser interface, or will there still be a IE specific one with richer functionality?
  • Will the cross browser support be more like the mobile client - with it's own form design - Will IE still be the preferred browser?
  • You will still need IE to use the Outlook Client so will the cross browser support be more of a 'selling point' than something that actually makes a significant difference to business users?

 

Cross browser HTML5 will mean replacing the IE specific .htc 'behaviours' which is quite a considerable piece of work - but a subject closer to my heart is the future of Silverlight in CRM2011. Silverlight is supported on most modern browsers (http://www.microsoft.com/getsilverlight/get-started/install/default.aspx#) but not on iPads. Could this mean an end to Silverlight WebResources just so that we can get iPad support?

Only time will tell, but it might make it harder to choose the developer productivity and user experience gains that come with Silverlight. The iPad is not a desktop replacement. There are limitations with SalesForce.com on the iPad (http://www.crmverse.com/using-salesforce-com-on-ipad/). Unless a client has decided to use iPads as a primary user interface device, I am still recomending developing Silverlight WebResources but structuring them in a way that will make it easier to port them to an HTML5 user interface when the developer tooling support is improved.

Maybe with Windows 8 Tablets the problem will go away...then again...maybe not!

References:

Posted on 6. January 2012

Adding an Advanced Find Query to a Form Sub grid

I've yet to see a clear post on how to add an Advanced Find Query as a subgrid on an entity form for CRM 2011. This is something that was quite common in CRM 4 due to the lack of sub grid support - but with CRM 2011 occationally we come up against the limitation of only showing related records and fields of only a single relationship away from the root entity.

Here are the steps:

1. Find the SavedQueryId of the default advanced find view for the entity you are reporting on. Use the following SQL against the MSCRM database to find it:

Select Name,SavedQueryId,ReturnedTypeCode,FetchXml,LayoutXml from SavedQuery where QueryType=1 and IsDefault=1
Order by ReturnedTypeCode

2. Create a webresource named 'new_SubGridFetchXml.htm' of type 'Web Page (HTML)'

Provide the following html:


<HTML><HEAD><TITLE></TITLE>
<SCRIPT type=text/javascript src="ClientGlobalContext.js.aspx"></SCRIPT>
<SCRIPT type=text/javascript>
    function submitForm() {
        var form = document.forms[0];
        var context = GetGlobalContext();
        form.action = context.getServerUrl() + '/AdvancedFind/fetchData.aspx';
        form.LayoutXml.value ='<LAYOUTXML>';
        form.FetchXml.value = '<FETCHXML>'
        form.submit();
         }
</SCRIPT>
<META charset=utf-8></HEAD>
<BODY onload=submitForm()>
<FORM method=post action="">
<INPUT name=FetchXml type=hidden> 
<INPUT name=LayoutXml type=hidden> 
<INPUT name=EntityName value=contact type=hidden> 
<INPUT name=DefaultAdvFindViewId value=<SavedQueryID> type=hidden> 
<INPUT name=ViewId value=<SavedQueryID> type=hidden> 
<INPUT name=ViewType value=4230 type=hidden> 
<INPUT name=SortCol value=<SortColumnLogicalName>:1; type=hidden> 
<INPUT name=UIProvider type=hidden> <INPUT name=DataProvider type=hidden> </FORM></BODY></HTML>

Adjust the <SavedQueryID> reference to the default advanced find query id found in step 1.

Adjust the <SortColumnLogicalName> to be the logical name of the field you want to sort by.

Replace the LayoutXml and FetchXml strings with the values returned from the query in 1 - you can then adjust them to suit your particular needs.

IMPORTANT:

If you add the web resource to a sub 'virtual path', you must alter the reference to the ClientGlobalContext.js.aspx to include the corresponding number of '../'

Don't worry about the script error when saving the page in the WebResource editor.

Your finished page would look something like


<HTML><HEAD><TITLE></TITLE>
<SCRIPT type=text/javascript src="ClientGlobalContext.js.aspx"></SCRIPT>
<SCRIPT type=text/javascript>
    function submitForm() {
        var form = document.forms[0];
        var context = GetGlobalContext();
        form.action = context.getServerUrl() + '/AdvancedFind/fetchData.aspx';
form.LayoutXml.value ='<grid name="resultset" object="2" jump="lastname" select="1" icon="1" preview="1"><row name="result" id="contactid"><cell name="fullname" width="300" /><cell name="telephone1" width="125" /></row></grid>';
form.FetchXml.value = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"><entity name="contact"><attribute name="fullname"/><attribute name="telephone1"/><attribute name="contactid"/><order attribute="fullname" descending="false"/></entity></fetch>';
            form.submit();
         }
</SCRIPT>
<META charset=utf-8></HEAD>
<BODY onload=submitForm()>
<FORM method=post action="">
<INPUT name=FetchXml type=hidden> 
<INPUT name=LayoutXml type=hidden> 
<INPUT name=EntityName value=contact type=hidden> 
<INPUT name=DefaultAdvFindViewId value={00000000-0000-0000-00AA-000000666400} type=hidden> 
<INPUT name=ViewId value={00000000-0000-0000-00AA-000000666400} type=hidden> 
<INPUT name=ViewType value=4230 type=hidden> 
<INPUT name=SortCol value=fullname:1; type=hidden> 
<INPUT name=UIProvider type=hidden> 
<INPUT name=DataProvider type=hidden> </FORM></BODY></HTML>


3. Add the webresource to the form that you want to show it on.

4. Publish and Go!

Usual disclaimers apply - and this is not a fully supported solution since it uses un-documented functionality

Posted on 5. January 2012

Convert CRM2011 LINQ Query into QueryExpression / FetchXml

With the introduction of Linq within the Dynamics CRM 2011 sdk, I've more or less stopped using QueryExpressions and FetchXml in server side code. This makes it increasingly painful when I've got to convert my queries to fetchXml or QueryExpressions for use in Javascript or Silverlight code. This sample shows how you can convert those linq queries into a QueryExpression or FetchXml by way of a command line utility.

This is a useful tool for debugging your linq queries as well to check that they are going to execute as you expect - however I would highly recommend LinqPad for a more indepth debuger/learning tool (www.linqpad.net).

This sample also demonstrates how to compile a Linq query from a text string - although this is something that shouldn't be done to get round the lack of dynamic query support in Linq - please see :  http://www.develop1.net/public/post/Dynamically-construct-a-where-query-on-a-Dynamics-CRM-2011-Linq-query.aspx  for an example of how to do this.

The main challenge was how to find the Query Expression from a linq query. This is solved by accessing a private method named 'GetQueryExpression'. 

QueryExpression query = (QueryExpression)linq.Provider.GetType().InvokeMember("GetQueryExpression", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, linq.Provider, arguments);

When you run the sample, you are prompted first to enter the details of your CRM server, and then you can enter a linq query. The Context must be named ctx - so a query would look like:

>from c in ctx.CreateQuery<Contact>()
>select c

To convert your query, simply press enter with a blank line.

The query is output to the console as well as saved as a text file for future reference.

Download Sample

Posted on 3. January 2012

Dynamically construct a where query on a Dynamics CRM 2011 Linq query

Linq is an excellent way of homogenising the way we query our data – the CRM 2011 Linq provider is no exception. One thing that we occasionally come up against is the need to query based on a variable number of where clauses.

With FetchXml and QueryExpressions dynamic filters were straightforward as we could simply iteratively construct the query. With Linq however, constructing a dynamic where clause is more complex due to the compile time nature of the queries.

If we had a list of last names that could change a runtime, and we wanted to query contacts that matched theses last names using Linq, the query would look something like:

var contactsNonDynamic = ( 
                        from c in context.CreateQuery() 
                        where c.LastName == lastNames[0] || c.LastName == lastNames[1] || c.LastName == lastNames[2] 
                        select new { Id = c.Id, LastName = c.LastName }); 

If the number of values in the lastNames list is variable, then we are in trouble.

This sample demonstrates how multiple values can be added to the FilterExpression part of a Linq query given a variable number of conditions. It uses the PredicateBuilder and AsExpandable features of LinqKit (http://www.albahari.com/nutshell/linqkit.aspx). LinqKit is included in the sample as a single file to avoid having to GAC or deploy to the bin folder.

Once you've built and deployed the sample (using the Developer Toolkit), update any contacts and the resulting dynamic LINQ query will run the following SQL Query against the MSCRM database:

exec sp_executesql N'select  
top 5001 "contact0".LastName as "lastname" 
, "contact0".ContactId as "contactid"  
from 
 Contact as "contact0"  
where 
 (((("contact0".LastName = @LastName0 or (("contact0".LastName = @LastName1 or "contact0".LastName = @LastName2)))))) order by 
 "contact0".ContactId asc',N'@LastName0 nvarchar(14),@LastName1 nvarchar(16),@LastName2 nvarchar(13)',@LastName0=N'Chand (sample)',@LastName1=N'Francis (sample)',@LastName2=N'Cook (sample)'

Notes:

  1. The sample will throw an exception simply to show your the result of the query when you update a contact.
  2. This will not work with Sandboxed PlugIns due to the code access security policy enforced.
  3. The solution uses the Developer Toolkit that is part of the Dynamics CRM 2011 SDK.
To build the sample, you must install the CRM 2011 developer toolkit that is found within the CRM 2011 SDK at \tools\developertoolkit\crmdevelopertools_installer.msi
 
To deploy to CRM, simply connect to the organisation when promoted with the 'Connect to Dynamics CRM Server' dialog and then select 'Deploy' from the solution menu.