CRM 2011: How to compare CRM fields with Linq

Well, you can’t. As Microsoft states here:

In a where clause, the left side of the clause must be an attribute name and the right side of the clause must be a value. You cannot set the left side to a constant. Both the sides of the clause cannot be constants.

So, you cannot do something like:

from a in context.AccountSet
where a.ModifiedOn.Value > a.CreatedOn.Value
select a.Name

Trying this will get you a nice and sweet error message explaining to you that:

InvalidOperationException: variable ‘<> […] referenced from scope ”, but it is not defined

So if you want to do it, you have to retrieve your data and filter it in memory using the Where extension method:

var resultsToFilter = (from a in context.AccountSet
select a.Name, a.ModifiedOn, a.CreatedOn)
.ToList();

var filteredResults = resultsToFilter
.Where(x => x.ModifiedOn.Value > a.CreatedOn.Value) ;

And there you have it.

 

CRM 2011: Raise Link Entity Limit

When querying CRM data using either QueryExpressions, FetchXML or Linq, you might receive the following error message when joining more that 10 entities:

Number of link entities in query exceeded maximum limit

That is very annoying. Why would they do that? Does Microsoft assume that all CRM consultants are dumb idiots who don’t know what they’re doing and need limits?

Anyway, here’s how to raise the limit from 10 to 20, by adding a row in the DeploymentProperties table of the MSCRM_Config database. This is of course unsupported.

declare @guid uniqueidentifier
set @guid =
(Select top 1 Id From MSCRM_CONFIG.dbo.DeploymentProperties)

— Add row in DeploymentProperties table
INSERT INTO MSCRM_CONFIG.dbo.DeploymentProperties(Id,ColumnName,IntColumn,Encrypted)
VALUES(@guid,’QueryLinkEntityLimit’,20,0)

— Check
select * from MSCRM_CONFIG.dbo.DeploymentProperties where ColumnName = ‘QueryLinkEntityLimit’

CRM 2011: retrieve OptionSetValue Name in Linq

With CRM 2011, when querying data using Linq, you might find it hard to retrieve an OptionSetValue name or label. You might end up thinking about making a call to the metadata service. Don’t. Just be aware of two things:

1. The syntax is as follows:

from a in AccountSet
select new {
myvalue = a.FormattedValues[“crm_myoptionset”]
}

2. But, if it’s null, this code will break, as null is sent instead of an OptionSetValue object.

So, I use this:

from a in AccountSet
select new {
myvalue = (a.crm_myoptionset != null) ?  a.FormattedValues[“crm_myoptionset”] : “”
}

Dynamics CRM and SPNs

Maybe you’ve already heard of SPNs, also known as Service Principal Names. Most CRM technical people have heard of them, but not many can really explain WHY they are necessary. Some can say it is for ensuring “mutual authentication”, but few can really explain how this mechanism works.

Today, let me explain to you my understanding of SPN’s, based on this article.

The Kerberos mechanism by which a client authenticates a service works as follows:

  • When a service is installed, a service installer, running with administrator privileges, registers one or more unique SPNs for each service instance.
  • The names are registered in the Active Directory Domain Controller (DC) on the user or computer account object that the service instance will use to log on.
  • When a client requests a connection to a service, it composes an SPN for a service instance, using known data or data provided by the user.
  • The client then uses the SSPI negotiate package to present the SPN to the Key Distribution Center (KDC) for the client domain account.
  • The KDC searches the forest for a user or computer account on which that SPN is registered.
  • If the SPN is registered on more than one account, the authentication fails.
  • Otherwise, the KDC encrypts a message using the password of the account on which the SPN was registered.
  • The KDC passes this encrypted message to the client, which in turn passes it to the service instance.
  • The service uses the SSPI negotiate package to decrypt the message, which it passes back to the client and on to the client’s KDC.
  • The KDC authenticates the service if the decrypted message matches its original message.

Let me rephrase this in the CRM world:

  • When CRM is installed, SPNs are registered for the identity of the application pool CRMAppPool. It can be Network Service or some other account.
    • That’s why, when changing the identity of the application pool, you need to remove existing SPNs and create new ones.
  • When a client requests a connection to CRM, it composes an SPN for it.
    • That’s why you need to register SPNs for both server the server name and the FQDN, because you can’t be sure how the SPN will be composed by the client.
  • (1) The client presents the SPN to the KDC for the client domain account
  • (2) The KDC searches the domain for a user or computer account on which that SPN is registered
    • Here, the KDC must find the identity of the CRMAppPool
  • If the SPN is registered on more than one account, the authentication will fail
    • Indeed, because the KDC will not know which account to use
  • The KDC encrypts a message with the password of the CRMAppPool identity
  • (3 & 4) The KDC passes the message to the client, which passes it to the service instance, ie to the CRM server
  • (5 & 6) The CRM server can decrypt the message, passes it back to the client which passes it back to the KDC

So that’s why we talk about mutual authentication: not only is the client identified (this aspect is not described here), but also the service.

CRM 2011: how to know when the form is coming back from Save

To put it another way: how can you make the difference between a load when the form is first opened, and a reload after a save for example?

A simple way to achieve this is to use the status or defaultStatus properties of the window object. Both Status and defaultStatus can be used to set the text displayed in the status bar at the bottom of the browser. When setting one of these properties before a save is performed, you can access them upon reload and they will have kept the new value. Example:

window.status = “reload”;

That’s the easiest way I’ve found to achieve this!

CRM 2011: Are you sure you want to leave this page?

in CRM 2011, when closing by calling the Xrm.Page.ui.close() method, if one of the fields on the form is dirty for some reason (and some reasons can be strange), then you will get the following message:

Are you sure you want to leave this page? Message from webpage: Your changes have not been saved. To stay on the page so that you can save your changes, click Cancel.

Using the code below, you can get rid of this sometimes annoying message:

var attributes = Xrm.Page.data.entity.attributes.get();
for (var i in attributes)
{
if (attributes[i].getIsDirty())  {
attributes[i].setSubmitMode(“never”);
}
}

CRM 2011 – Form is always dirty – Trailing spaces

With CRM 2011, whenever you want to automatically refresh or save a form that has supposedly not been modified by a user, and still get that annoying “Are you sure you want to leave” message, you can sometimes start getting a bit upset about it. Xrm.Page.data.entity.getIsDirty() always returns true. But the form was just loaded! And no javascript in there updates a field. What’s happening?

Well, don’t get too upset, it’s not worth it! Well, after all, maybe it is…

It turns out that on one of my forms, one field was sometimes dirty. That field was a lookup. Actually, the field’s dirtyness depended on the referenced record: it was always dirty with this record, and never with another one. After looking carefully at the referenced record, it turned out we were facing the dreaded “blank space” issue, and particularly the “trailing space” one. That record’s name actually had a trailing space in it! For some… dirty reason, CRM thinks it’s a good idea to actually remove trailing spaces from lookup names, and by that to make these lookups dirty when nothing actually changed…

Of course, when opening the guilty record, you just can’t remove the trailing space, as CRM will ignore that change and thus will not save it. You need to change something more to actually save the record, and then reset it…

According to me, this behavior is a bug and should be corrected asap.

CRM 2011 – Render SSRS Report from Plugin

Recently I had to play with SSRS web services. The objective was to render multiple reports in pdf format from within a CRM 2011 plugin, and to upload the files either in notes or in Sharepoint document location. After doing a lot of research, here is the function I came up with to call the SSRS web service. All of it does not completely make sense, and I had a hard time putting this all together. Documentation is not easy to find and understand.

 

private Byte[] RenderReport(string serviceURL, string reportPath, string reportName, ParameterValue[] parameters)
{
byte[] result;
string encoding;
string mimeType;
Warning[] warnings = null;
string[] streamids;
string extension;

BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
binding.Security.Transport.Realm = string.Empty;
binding.MaxReceivedMessageSize = 2147483647;
string deviceInfo = @”<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>”;
EndpointAddress endpoint = new EndpointAddress(serviceURL);

ReportExecutionServiceSoapClient client = new ReportExecutionServiceSoapClient(binding, endpoint);

client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;

ExecutionInfo executionInfo;
ExecutionHeader executionHeader;
ServerInfoHeader serverInfoHeader;

executionHeader = client.LoadReport(null, reportPath, null, out serverInfoHeader, out executionInfo);
executionHeader.ExecutionID = executionInfo.ExecutionID;

if (parameters != null)
{
client.SetExecutionParameters(executionHeader, null, parameters, “en-us”, out executionInfo);
}

ServerInfoHeader Info = client.Render(executionHeader, null, “PDF”, deviceInfo, out result, out extension, out mimeType, out encoding, out warnings, out streamids);

return result;
}

CRM Plugin reset form

Having recently been playing with multiple forms in CRM 2011, I wanted to make sure that when opening a record, the user would always be presented with the main information form. At first, I decided to udpate the UserUIEntitySettings from within the form onload. But I must say I had a few problems with this option:

  1. First, I decided to use a getFormId javascript function. It worked fine until I realized most users were using CRM with a language that was different from the base language. This made the form id’s unaccessible to them, and thus the function always returned nothing.
  2. Second, I decided to use the Xrm.Page.ui.formSelector object. But for some reason, while this worked well in the main form, it turned out that in the other form, the Xrm.Page.ui.formSelector.items collection was always null. I still did not figure out why.

So in the end I decided to go the plugin way, and implemented the below function in a PreRetrieve event. Notice that the mainFormId parameter is passed to the plugin using the secure/unsecure parameters (unsecure in this case), since it is not accessible for retrieve to most users because of their current language.

I also have to retrieve the concerned entity’s object type code. Indeed, you must be careful if you use early binding in your plugin NOT TO use new_entity.EntityTypeCode, as this will be different between environments.

private void ResetMainForm(context)

        {
            RetrieveEntityRequest entityRequest = new RetrieveEntityRequest
            {
                EntityFilters = EntityFilters.All,
                LogicalName = account.EntityLogicalName
            };
            RetrieveEntityResponse entityResponse = (RetrieveEntityResponse)context.OrganizationService.Execute(entityRequest);
            QueryExpression query = new QueryExpression(UserEntityUISettings.EntityLogicalName);
            query.Criteria.AddCondition(“ownerid”, ConditionOperator.Equal, context.PluginExecutionContext.UserId.ToString());
            query.Criteria.AddCondition(“objecttypecode”, ConditionOperator.Equal, entityResponse.EntityMetadata.ObjectTypeCode.Value.ToString());
            query.ColumnSet = new ColumnSet(true);
            EntityCollection UISettingsCollection = context.OrganizationService.RetrieveMultiple(query);
            if (UISettingsCollection.Entities.Count > 0)
            {
                //update the last viewed formId:
                UserEntityUISettings settings = (UserEntityUISettings)UISettingsCollection[0];
                settings.LastViewedFormXml = “<MRUForm><Form Type=”Main” Id=”” + mainFormId + “” /></MRUForm>”;
                context.OrganizationService.Update(settings);
            }
        }

[ad]

CRM 2011: reload parent window

Here’s how to reload the parent window with Dynamics CRM 2011:

window.top.opener.top.documentlocation.reload();

A word of explanation:

  • window.top is the top window the current iframe. Remember that all code in onload CRM javascript runs in an iframe.
  • window.top.opener is the opener window
  • window.top.opener.top is used to make sure the whole page is refreshed and not an iframe
  • window.top.opener.top.document.location.reload() to reload

Pay attention, when opening the new window, to use a relative URL, or to be sure that the server name that you get when calling:

Xrm.Page.context.getServerUrl()

is the same as the one you are currently connected to. By that, I mean that it may happen that you connect to CRM server using localhost, the IP address or the fully qualified domain name, while getServerUrl will give you the base server name. As explained in the SDK:

The URL returned is the standard URL used to access the application. If you access the server locally using http://localhost or use an IP address instead of the actual name of the server, this will not be reflected in the value returned by this function. This means that if you are making web service calls or accessing web resources, the browser will apply security settings that apply to requests that cross domains.

So for example, using window.open, and trying to reach the opener window from the child window, you will get an “access is denied” error. And you will see strange behavior when looking at the Xrm.Page object, with Xrm.Page.ui and Xrm.Page.data being null if I am not mistaken.

By the way, from Update Rollup 8, the new way to open a popup window is to use this function:

Xrm.Utility.openEntityForm (or openWebResource)

http://msdn.microsoft.com/en-us/library/jj602956.aspx