October 17, 2011

Strange behavior of LINQ XRM data context – Ghosts

Recently I experienced a lot of problems when creating a complex plugin which played around with thousands of records.

When using good, old T-SQL you would experience the following behavior:

Id Name
1 AAA
2 BBB
3 CCC

SELECT Name FROM Table WHERE Id = 2
Result: AAA

UPDATE Table SET Name = ‘ZZZ’ WHERE Id = 2

SELECT Id FROM Table WHERE Name = ‘ZZZ’
Result: 2

Being naïve and lazy I expected the same behavior from the LINQ 2 CRM data provider – wrong! Remember about caching. This doesn’t happen every time, but from time to time, especially when doing operations one after another querying for a value you just set a moment before, will simply return NULL. This off course caused a lot of strange and hard to find errors.

In CRM it sometimes works like this:
SELECT Name FROM Table WHERE Id = 2
Result: AAA

UPDATE Table SET Name = ‘ZZZ’ WHERE Id = 2

SELECT Id FROM Table WHERE Name = ‘ZZZ’
Result: NULL

Off course when doing a standard FetchXML or QueryExpression everything is OK.

The solution – recreate the Data Context before each query – dataContext = new DataContext(), or as minimum before each query to an entity you used before. This will of course lower the performance a little, but let’s be honest 99,99% of the time is spend inside the query.

To be completely fair I have experienced similar issues with the “Portal Extensions” from CRM 4 but didn’t think Microsoft made CRM 2011 backward compatible in this matter Uśmiech

The SDK makes the impression that the LINQ queries are simply translated into QueryExpressions, apparently they are not.

Differentiating between NULL and NO CHANGE in CRM 2011 plugins

Unfortunately a NULL value of a field in the plugin target can have 2 meanings:

  1. Value has been changed to NULL (new value equals NULL)
  2. Value has not changed (new value equals old/current value)

I’m referring to values of properties when using the strongly typed approach like:

Microsoft.Xrm.Sdk.Entity target = ExecutionContext.InputParameters["Target"] as Entity;
Account account = target.ToEntity<Account>();
string name = account.name;

Thus it could be illustrated something like this:

Plugin target “New” value
NULL ???
VALUE VALUE

Where the plugin target is the value of the attribute taken from the target entity in the plugin and the "new" value is the value that will be saved to the DB.

Shortly speaking we don’t know the “new” value in 50% of cases.

Adding a pre entity image will lower this to 75%.

Pre Entity Image Plugin target “New” value
NULL NULL NULL
NULL VALUE VALUE
VALUE NULL ???
VALUE_1 VALUE_2 VALUE_2

So if the plugin target has any value then that is off course the “new” value.If it’s NULL and the pre image value is also NULL we are sure that the new value is NULL.

The only way to get to 100% I could think of is to get back to the not type safe, property bag approach, because if we just use:
entity.Attributes[“name”]
instead of
account.name
we actually get the information because the value is either NULL if it was set to NULL or it’s not there at all (throwing a value not found exception).

This gives:

Pre Entity Image Plugin target Property bag “New” value
NULL NULL Doesn’t matter NULL
NULL VALUE Doesn’t matter VALUE
VALUE NULL Has attribute NULL
VALUE NULL Doesn’t have attr. VALUE
VALUE_1 VALUE_2 Doesn’t matter VALUE 2

I summed this up in this little piece of code:

private object TrueValue(object preValue, object targetValue, Entity target, string attributeName)
{
    if (preValue == null || targetValue != null)
    {
        return targetValue;
    }
    else
    {              
        if(target.Attributes.ContainsKey(attributeName.ToLower()))
        {
            return null;
        }
        else
        {
            return preValue;
        }               
    }
}

Which could be used like this:

Account preImage = this.ExecutionContext.PreEntityImages["PreImage"].ToEntity<Account>();
string newName = (string) TrueValue(preImage.name, account.name, target, “name”);

It could be simplified so that it doesn’t use the typed account property at all (it’s basically the same as the property bag).

The scenario can get even more complicated when using field level security. In those cases a NULL value can have 3 meaning:

  1. Value has been changed to NULL
  2. Value has not change
  3. User (in whose context the plugin is executed) has no READ privilege to the field

The simplest solution seems to run plugins, which touch secured field, in the context of an administrative user (impersonate).

October 12, 2011

No new posts!?

I am aware that this blog probably doesn’t have too many readers (apart from web crawlers). But if there are any, rest assured there will be a lot of new posts soon.

Currently I’m engaged in studying for my private pilot license, but that will end in a few days. I have a list of subjects I would like to blog about and hopefully will do so soon enough.

Cheers

September 27, 2011

Isn't XRM enough?

I was wondering why Microsoft doesn’t release a pure XRM version of “CRM”. By pure XRM I mean releasing a product containing only the platform, without the added sales, marketing and service functionality. Better – why not sell the selected functionalities as an add-on package through the Marketplace?

I’m not sure about your experiences but I have worked on several projects where we didn’t use ANY of the build in business entities (understood as those not related to core platform operations like systemuser etc.). Those truly XRM solutions just leverage on the wonderful, generic platform that Microsoft has created. They have nothing to do with the build in sales model.

In such deployments all the “system entities” are only cluttering the system. Not really the KISS principle, is it?

Maybe someone would like to share a thought on this subject? Please comment!

SSRS – CRMAF auto filter on multiple datasets

Not sure if this method is well known or not, but I discovered it out myself and would like to share.

As we know the CRMAF_ type autofilters do not support filtering on multiple datasets. Meaning that if you create

  1. DataSet1 – select * from FilteredAccount As CRMAF_FilteredAccount
  2. DataSet2 – select * from FilteredAccount As CRMAF_FilteredAccount

then create a report and publish it into CRM, the filters on row selection, executing reports from single forms etc. will not work.

Fortunately there is a simple way around this limitation. The trick is to create a hidden parameter, fill it in with ID’s from a filtered set and then use that in an IN filter inside the other queries.

Example:

  1. Launch Business Intelligence Development Studio and create a new report.
  2. Add a dummy filter dataset, and query the ID parameter of the entity you like to filter on.

    image

  3. Create a filter parameter, to be used in the other datasets. Remember to make it hidden and allow multiple valuesimage

  4. Specify the “Available Values” to be taken from the filter dataset. Do the same with “Default Values”
    image

  5. Create the datasets depending on the filter. Probably at least 2, else this method doesn’t make sense. Inside the query filter using the parameter created in point 3.

    image
    Notice you shouldn’t use the CRMAF_ filter inside this query.

    You can see how I created the datasets in BIDS:
    image
    image
    Remember to set the default values of the @AccountFilter parameter, else the environment will not be able to parse the query.

    Repeat this with all needed datasets. In this example I created a second dataset from the second query.

  6. Use the dataset in the report
    image
    Here I have simply used them in two separate tables.

  7. Deploy report to CRM & enjoy fully working CRM auto filtering on multiple datasets

Cheers Puszczam oczko