Tuesday, 2 October 2012

CRM Field Guide - The Review

Ok, so I've pretty much speed read the CRM Field Guide and I have to say, I'm very impressed with it after a first read. For me this is the kind of book that's been missing for a long time - a user guide, an installation guide, a "bet you didn't know CRM did this" guide, a "you're not doing development correctly until you've read this" guide! And want a little confession? 6 or 7 chapters in and I started to realize how much I didn't know about Dynamics CRM!

So a little breakdown on what this book offers. It leads you in with a gentle pat on the bottom by breaking down the interface for you, explaining what each section does or where each link takes you. It gives you just enough information to start on without scaring the living bejaysus out of you, and finishes off chapter 1 with a very solid list of tips from the MVPs themselves.

Next up you learn how to install it. You might say, isn't this the wrong way round? Shouldn't you tell me how to install it before taking me through the interface? Actually no, I think for once somebody has got it right. You get presented with "What CRM is" before you get unleashed with the beast (for lack of a better term). Even though it might seem like I'm pointing out a very simple thing I quite liked this approach, that much that I felt like pointing it out.

After a crash course in installing and using some out of the box features they take you on, well yet again, for lack of a better term, a semi deep-dive. (I really need to learn more terms apparently). They lift the hood a little on how to get the most out of this beast. From optimizing servers to troubleshooting common problems, they've covered it. It's actually quite impressive what they squeezed into this book without going over the top. It seems like they've covered 80% of CRM that most people use and not bothered with the rest. Apart from maybe a brief mention of a few bits if they are worthy of it.

By this stage I'm loving it. I'm getting a little insight into these MVP's heads and I'm not slowing up. I'm reading about processes, solutions and customization's and things are really beginning to hot up. Although, to be honest I'm generally just nodding my head in approval at this stage, because I imagine most CRM developers should already be practicing most of this stuff. But good stuff it is. Cue rapid development and I think to myself this is the bit I've been looking forward too. What approach do you take to customizing those entities and writing web resources? How do you guys organize, not just your CRM solutions, but your Visual Studio solutions and associated plugins and scripts? What coding tips and tricks can you share with us developers? Alas, this is unfortunately the one place that I find this book lacking a little. Granted, they share an abundance of links with you to get any of the information you require. But you left me wanting!

If I could add just 2 more chapters to this book it would be a JavaScript deep-dive and a Developers 101 to CRM.SLN. It didn't need to be overpowering, just some guides on attaching JavaScript events, writing HTML/Silverlight web resources and maybe a little crash course in VS plugins and custom workflow activities. Just a little bit of depth here would have made it a perfect book for me.

But don't let this deter you, as it is a very minor criticism. This is still one awesome book in my opinion. Even if it doesn't go to the technical level I felt it needed to in regard to development, this still is a must read for every developer, implementer and even every user of Dynamics CRM 2011. There's so many little things I have learnt from this book that will improve what I take to each implementation of CRM 2011 I tackle in the future. In short, this book will be a staple in my CRM diet for Dynamics CRM 2011.

All in all good work MVPs!

Monday, 1 October 2012

CRM Field Guide out today!

I started getting tweets thick and fast into my feed today in regards to the CRM Field Guide being released. I first spotted it on Larry Lentz's feed. I snapped up a copy pretty much immediately after spotting it's release, and just when I received the payment verification from Julie Yack www.crmfieldguide.com crashes. That's just about sums up how popular I expect this book to be! I noticed that crmfieldguide.com (without the www) still seems to be working, so you might be able to grab a copy from that link.

This book, in my opinion, is going to be one of the most popular CRM 2011 books released for the Microsoft Dynamics CRM platform bar none. It's a book written by MVPs in the CRM community for the CRM community and should become a staple part of any CRM 2011 developers/integrators/deployers diet. It looks like it will deal with just about everything from admin to development and pretty much everything in between that you can think about.

Anyway, I'm now left in a limbo of anticipation, because I cannot get onto the darn download page to get my new book, shucks! I may have to just be patient and wait until tomorrow! I sure am looking forward to that read though.

Good luck with the release guys!

Capitalize first letter of a text field in Dynamics CRM

This was asked on a the Dynamics community forum recently, and due to my struggles in posting Javascript as a response I ended up blogging about it instead!

So how do we do this? Firstly, we'll need a webresource that contains our javascript. Create a .JS file (in my example I'm going to call it test.js) and add the following function to it:

function firstToCaps(obj){
    var fieldVal = obj.getEventSource().getValue();

    var newFieldVal = fieldVal.charAt(0).toUpperCase() + fieldVal.slice(1);

Once you have your JS file saved just add that as a new webresource to your solution.

Next, let's discuss how you hook into an onchange event of a field on a form, because this is what we need to do in order to solve this problem. For this example I'm going to use the account form and the name field.

In our solution let's add the account entity to it. Open up the main form of the account and check out the Form Properties:

You'll notice I have added a library called "crm_tests.js" to the list of Form Libraries. You'll also notice that in the Event Handlers I have selected "Account Name" and "OnChange". This allows me to hook into this event and call a function when required. In my case I have added a call to my function "firstToCaps":

The important part of this event is that I have checked the option to pass the execution context as the first parameter. This gives me access to the field that fired the event.

So, let's go back to that function and take a quick look over what it does:

function firstToCaps(obj){
    var fieldVal = obj.getEventSource().getValue();

    var newFieldVal = fieldVal.charAt(0).toUpperCase() + fieldVal.slice(1);

You've probably put 2 and 2 together at this stage and can see I'm using the execution context "obj" and its function getEventSource to allow me to get and set the value of the calling field.

Save and publish all of that and watch your field auto capitalize!

Friday, 17 August 2012

IE Crashes when I close a CRM window

This is probably one of the first things you'll come across when you've upgraded to Internet Exporer 9 and are closing Microsoft Dynamics CRM pages before they've fully loaded. And it's dreadfully annoying!

Microsoft have noticed this problem and published a work around. Which is a simple registry change:

  1. Point to the Start menu, click Run, and then type regedit.
  2. Navigate to HKEY_Current_User\Software\Microsoft\Internet Explorer\Main
  3. Right-click Main, and then select new DWORD (32-bit) Value
  4. Enter HangRecovery as the name.
  5. The default value will be 0.

For full details on the problem see here: http://support.microsoft.com/kb/2698959

Tuesday, 31 July 2012

Advanced find and many-to-many relationships

Here's one that caught me out for a little while today. Earlier today I set up a new entity called Media Code with a many-to-many relationship with Sales Literature. I put in my required settings, saved it, and then went about my daily business.

Later on in the day I want to create a Fetch Query to locate any Sales Literature that is linked to a particular  Media Code. So, in typical developer mode and having forgotten everything I did earlier in the day, I open up an advanced find, select Sales Literature from the list, look to filter by Media Code... but low and behold there's no Media Code option in the related list?!? To double check I haven't gone insane I change my advanced find and select Media Code from the list of entities... and to my complete amazement Sales Literature is there right in front of my eyes, as bold as brass, under related entities.

What on earth is going on at all I hear you ask! Well, today I learnt something new about Microsoft Dynamics CRM and it was this here:

I did not know until today that this option also blocks it from the advanced find. Shock horror!

Hopefully this saves somebody else 30 minutes of their day pulling hair out of their head...

Thursday, 19 July 2012

Hey dynamics, where's my cross browser support?

So, we've hit Q3 and I must have had my head buried in the sand, because I never noticed until recently that the cross browser support for Microsoft Dynamics CRM 2011 never hit in Q2. So what happened? Where is it?!?

Looks like MS hit some complications and this got pulled from the Q2 release. Thankfully, it's still on the agenda but has been delayed until Q4:


Must keep my eye a little closer to the ball!

Wednesday, 18 July 2012

Importing Marketing List Members... the fast way

A common bane in many developers lives is the inability to import marketing list members directly using an asynchronous import. Even in Microsoft Dynamics CRM 2011 this is still an issue. If you're reading this you've probably come across this too. The most common solution I've seen (and read) for this is importing one by one. A piece of code I came across recently did exactly this and (a stripped down version...) looked something like the following:

while (true)
    var record = MarketingListCSVFile.GetNextCSVRecord();

    if (record == null)
        // eof - no more records to process

    Entity contact = GetContact(record[ContactField]);
 AddListMembersListRequest request = new AddListMembersListRequest { ListId = MarketingList.Id, MemberIds = new Guid[] { contact.Id } };
    AddListMembersListResponse response = (AddListMembersListResponse)this.Service.Execute(request);
    // ...

AttachMarketingListToCampaign(MarketingList, MarketingListCSVFile.CampaignActivityCode);

... and so on. In practice this works, but there's a lot wrong with it. Aside from the fact that it's going to be slow due to hitting the server every time for each member, it's going to hammer that server until it get's all those members imported.

A better, and quite possibly the fastest way to get this imported is by using a custom "holding" entity, drive a Dynamic Marketing List off this entity and then convert this Dynamic Marketing List to a static marketing list (if required).

The Holding Entity 

Firstly, we need to create an entity that has all the required fields on it that will allow us to drive a query off. The only requirement here is that it has a relationship to the Cotact entity, because a query for a Dynamic Marketing list must return a list of contacts. In my case I needed to link these contacts back to a campaign activity, which involved importing a code to give me the ability to do this. All in all my new custom entity contains the following:

  • Contact (Lookup to contact) 
  • Campaign Activity Code (string) 

The query

Next we need to make sure we can drive the correct query off this. Pop open an advanced find and select the contact entity type from the list. My query looked like this:

This will bring back all the contacts required for my marketing list. All good so far.

The import

Next up, how do we kick off an asynchronous import via the code? If you haven't done this before it's worthwhile having a read of the following links first. Once you've digested all, or at least the applicable parts of this you're ready to write your import:

Sample: Import Data Using Complex Data Map
Data Import Entities

You'll notice another problem I ran into when researching this, and it's how everyone has gone dog crazy on early bound objects. I'm not saying they're bad, but I'm not as big a fan as most. Or maybe I'm just a freak for late bound objects. Mainly because it saves me the pain of:
  1. Making sure everyone has the latest and greatest definitions in their project
  2. Waiting for some other bloke to create his entity before I can write my "something or other" that  relies on just 1 field in that entity... 

Several ways around the above, but as you may have gathered by now, my favourite is use late binding ;)

So, we can break an import and what needs to happen down to about 7 basic steps (8 if you want to wait for the import to complete):

  1. Create an import map
  2. Create all your column mappings linked to your import map
  3. Create the import
  4. Create the import file linked to the import and import map
  5. Kick off the parse step
  6. Kick off the transform step
  7. Kick off the physical import.

An interesting point to note is that you don't have to wait for the parse to complete before kicking off the transform and import. When MS CRM receives these requests will just queue them up until the others have completed.

When you're done you'll end up with code that looks something like this (You'll notice that this doesn't look exactly like the sites I linked above due to late binding):

var importMap = new Entity("importmap");
importMap.Attributes["name"] = "Import Map Name";
importMap.Attributes["source"] = CsvFileName;
importMap.Attributes["description"] = "Import Description...";
importMap.Attributes["entitiesperfile"] = new OptionSetValue(1); // 1 = Single Entity Per File
Guid importMapId = service.Create(importMap);

// Create a column mapping for the contact lookup field.
var contactColumnMapping = new Entity("columnmapping");
contactColumnMapping.Attributes["sourceattributename"] = "Contact";
contactColumnMapping.Attributes["sourceentityname"] = "Contact_1";
contactColumnMapping.Attributes["targetattributename"] = "new_contact";
contactColumnMapping.Attributes["targetentityname"] = "new_marketinglistcontact";
contactColumnMapping.Attributes["importmapid"] = new EntityReference("importmap", importMapId);
contactColumnMapping.Attributes["processcode"] = new OptionSetValue(1); // 1 = Process
Guid contactColumnMappingId = service.Create(contactColumnMapping);

// If you have special codes you may need a lookup mapping for the contact
var contactLookupMapping = new Entity("lookupmapping");
contactLookupMapping.Attributes["columnmappingid"] = new EntityReference("columnmapping", urnColumnMappingId);
contactLookupMapping.Attributes["processcode"] = new OptionSetValue(1); // 1 = Process
contactLookupMapping.Attributes["lookupentityname"] = "contact";
contactLookupMapping.Attributes["lookupattributename"] = "new_code";
contactLookupMapping.Attributes["lookupsourcecode"] = new OptionSetValue(1); // 1 = Source
Guid contactLookupMappingId = service.Create(contactLookupMapping);

// Create a column mapping for the campaign activity code field.
var campaignActivityColumnMapping = new Entity("columnmapping");
campaignActivityColumnMapping.Attributes["sourceattributename"] = "Campaign Activity Code";
campaignActivityColumnMapping.Attributes["sourceentityname"] = "Contact_1";
campaignActivityColumnMapping.Attributes["targetattributename"] = "new_campaignactivityid";
campaignActivityColumnMapping.Attributes["targetentityname"] = "new_marketinglistcontact";
campaignActivityColumnMapping.Attributes["importmapid"] = new EntityReference("importmap", importMapId);
campaignActivityColumnMapping.Attributes["processcode"] = new OptionSetValue(1); // 1 = Process
Guid campaignActivityColumnMappingId = service.Create(campaignActivityColumnMapping);

// Create a column mapping for the name field.
var nameColumnMapping = new Entity("columnmapping");
nameColumnMapping.Attributes["sourceattributename"] = "Name";
nameColumnMapping.Attributes["sourceentityname"] = "Contact_1";
nameColumnMapping.Attributes["targetattributename"] = "new_name";
nameColumnMapping.Attributes["targetentityname"] = "new_marketinglistcontact";
nameColumnMapping.Attributes["importmapid"] = new EntityReference("importmap", importMapId);
nameColumnMapping.Attributes["processcode"] = new OptionSetValue(1); // 1 = Process
Guid nameColumnMappingId = service.Create(nameColumnMapping);

// Create Import
var import = new Entity("import");
import.Attributes["modecode"] = new OptionSetValue(0);
import.Attributes["name"] = "Importing data";
Guid importId = service.Create(import);

// Create the actual file...
var file = new Entity("importfile");
file.Attributes["content"] = File.ReadAllText(CsvFileName);
file.Attributes["name"] = CsvFileName;
file.Attributes["isfirstrowheader"] = true;
file.Attributes["source"] = CsvFileLocation;
file.Attributes["sourceentityname"] = "Contact_1";
file.Attributes["importmapid"] = new EntityReference("importmap", importMapId);
file.Attributes["importid"] = new EntityReference("import", importId);
file.Attributes["targetentityname"] = "new_marketinglistcontact";
file.Attributes["size"] = ((string)file.Attributes["content"]).Length.ToString();
file.Attributes["fielddelimitercode"] = new OptionSetValue(2); // 2 = Comma
file.Attributes["datadelimitercode"] = new OptionSetValue(1); // 1 = Double Quote
file.Attributes["processcode"] = new OptionSetValue(1); // 1 = Process
file.Attributes["usesystemmap"] = true;
Guid fileId = service.Create(file);

var parseRequest = new ParseImportRequest { ImportId = importId };

var transRequest = new TransformImportRequest { ImportId = importId };

// Assign the request the id of the import we want to begin
var request = new ImportRecordsImportRequest { ImportId = importId };
var response = (ImportRecordsImportResponse)service.Execute(request);

You most likely won't end up with all that code in 1 place like this, or at least I hope not! But that's the general gist of what needs to happen.

Dynamic Marketing Lists

So what the above gives you is a very quick way to get the data imported into MS Dynamics CRM. But how do we use this? Let's grab that fetch xml from the earlier query and inject our campaign code into that:

string fetchXml = string.Format(
    "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true'>" +
    "  <entity name='contact'>" +
    "    <attribute name='fullname' />" +
    "    <attribute name='contactid' />" +
    "    <order attribute='fullname' descending='false' />" +
    "    <link-entity name='new_marketinglistcontact' from='new_contact' to='contactid' alias='aa'>" +
    "      <filter type='and'>" +
    "        <condition attribute='new_campaignactivitycode' operator='eq' value='{0}' />" +
    "      </filter>" +
    "    </link-entity>" +
    "  </entity>" +

Set up a new Marketing List, pop that into the "query" field of a Marketing list, and set the "type" to dynamic (1) and off we go.

Converting from a Dynamic to a Static Marketing List

Final step is to convert this to a Static Marketing List. This is easily achievable using the "CopyDynamicListToStaticRequest":

var copyDynamicListToStaticRequest = new CopyDynamicListToStaticRequest { ListId = ml.Id };
var response = (CopyDynamicListToStaticResponse)ml.Service.Execute(copyDynamicListToStaticRequest);
staticMarketingList = new MarketingList(ml.Service, response.StaticListId) 
    { Name = MarketingListCSVFile.MarketingListName, Locked = false };

Job Done.

I'd like to take this opportunity to thank that Hetfield dude for being frikken awesome and passing on the awesome. More specifically, his idea of using a dynamic marketing list.


Tuesday, 13 March 2012

ExecutionContext.CallerOrigin in Microsoft Dynamics CRM 2011?

I only realised this a couple of days ago... but this property has been made obsolete, supposedly because it was unreliable. My delayed realisation is probably due to my absence from the CRM scene until about 6 months ago!

Anyway, there were a few reasons why we needed the CallerOrigin in MSCRM 4.0, so it's a pity it was removed. But there are ways around each scenario where we needed it:

Prevent infinite loops (over 2 plugins)

This was the reason I was looking for the CallerOrigin and I started to ask this question. I only wanted the plugin to fire if it came from a user on the form, not from another plugin (i.e. asyc process/webservice). In my case the distinction of it being "over 2 plugins" is quite important, because I cannot use InputParameters to solve the problem. My example was similar to the following:

  • Update Plugin for "Parent" Entity. If optionset called "Status" on the parent entity was set to "Approved" I subsequently wanted to set a status on all the child entities to "Approved" as well.
  • Update Plugin for "Child" Entity. If the optionset called "Status" on the child entity was set to "approved", and all other children of the same parent has this set to "Approved" I needed to update the Status on the parent to approved as well.

This causes an infinite loop if you don't protect yourself against it. You also cannot use the InputParameters to solve it. Solution? Use depth checking:


If this is greater than 1 it's been called by another plugin.

Prevent syncing issues from an offline client

We've been given different properties to help us distinguish these ones. Use these instead:


Reacting differently depending on what the origin is

Ok, so this is the only scenario where we really do need the CallerOrigin. The only way I think you'd be able to do this is by checking the type of the PluginExecutionContext itself. I know for async it's the type:


and for plugins it seems to be:


Outside of that you would probably have to check the



The only other method I've come across for detecting where the update came from is using a custom flag on the form. So you could create an OptionSet called "OriginOfChange" (or something similar) with the options

  • CRM Form (Javascript onsave)
  • Workflow
  • Plugin
  • etc.

Then what ever updates the entity sets this field during the update. In this way you could check the Input Parameters each time to see where the update has come from.

Wednesday, 7 March 2012

If broken it isn't, fix it you shouldn't...

From speaking to some old friends of mine I heard about a cataclysmic implosion of a project I used to work on. This was a project that initially started as an extremely badly coded asp.net application (with horrors like functions with 1000+ lines of code). A project that we spent over a year on trying to upgrade to use much better patterns and practices and add tonnes of test coverage on while changing/adding features to the application. It was by no means perfect when we brought it over the finishing line, but it was a very solid and decent bit of code. The damn thing even won an award!

Anyway, it led me on to writing this blog post. And my tip of the day is:

"If broken it isn't, fix it you shouldn't..."

The basic concept I put forward is if something isn't broken then why change it? If you're changing something just because it's not agile enough, or you don't like the code, then you're not really following agile principles, or at least not following one of my favourites: Take the first bullet. Some people call it "fool me once, shame on you, fool me twice, shame on me". Effectively, if a requirement or story "fools" us into not making that abstraction, or into writing code that doesn't protect us from that bug, then that's the first bullet. When we get hit by that bullet it's time to abstract away or protect that code so that we don't get hit again.

Here's a really basic example: Let's say I ask you to write some code to sort a list of contacts by their name. You might write me a sorter class that takes in the list of contacts and returns a list sorted by their name. You might even go as far as passing in a parameter so that you can sort it by any field you want.

Then comes a second requirement - I want to sort it by email address as well as name, because I have 20 joe bloggs. Ok, so we didn't account for the ability to sort by multiple fields. One solution to this problem is add a parameter to allow you to add a second field to sort by. But what if we get 3 fields? Or what if the new field didn't have the same way of sorting as name? A better solution would be to introduce Comparer objects or something of that manner. That way you let the invoker tell you how to compare the objects and that way you've taken the first bullet. What we have in effect done is followed up by closing off a possibility of further sorting issues. (For more information on this look up OCP, or Open/Closed principle).

This is exactly what didn't happen in this "cataclysmic" project. Things were deemed "not agile enough" so changes were being made to modules and pages and classes that weren't taking any "bullets". Next thing you know things stopped working, because the whole project architecture started to change and remould over time. This is probably very much expected. So this (every so slightly) changed the situation into "broken it is (now), so fix it you bloody well should!". Their decision? Rewrite the whole project. My jaw left a dent on the floor when I heard that bit, no word of a lie... Such madness!

Finally, if you're astute you may have recognised that my blog entry title was very much inspired by an excellent blog by Tess Ferrandez! (If broken it is, fix it you should)

Thursday, 1 March 2012

Silverlight MSCRM Dashboards - SoapHelper throwing "Object reference not set..." errors

So, you've created that shiny dashboard using Silverlight. You're using the SoapHelper provided by the SilverCrmSoap library. But any time you try to hit Dynamics you get a big dirty "Object reference not set to an instance of an object" error. What could be going on?

In the associated HTML page that loads the XAP file make sure you've referenced the correct javascript library:

<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>

9 times out of 10 you'll have referenced the Silverlight.js by mistake. I keep falling into this trap. I must have a memory like a sieve!

Tuesday, 28 February 2012

How do you change/get rid of the default "name" field in Dynamics 2011!

There are many situations where the default "Name" field is not applicable to an entity. A couple of examples to explain what I mean:

An order doesn't have a name as such, but an ID. What we can do in this instance is change the name of the default field rather than keep the default "pfx_name". A more appropriate name might be "pfx_OrderId" or maybe "pfx_OrderRef". You do this when creating the entity on this screen:

Using the Primary Field tab we can decide exactly what the name of the field is and other things such as business requirement and length etc.

Order Detail
This is an even more interesting one. For Order Detail we don't really have primary field whatsoever. We have a few options as to what we may consider the primary field:
  1. It's probably a combination of Order ID/Ref and the Order Line Number?
  2. Maybe it's the Product Name we're ordering?

Either way, it's not a text field. So what are our options? Initially from a look at the above screen for editing the Primary Field properties we get some false hope... it looks like you can change the Type of field this is! Unfortunately we only have 1 option in the dropdown: Single Line of Text.

So, the only real option is remove it from the form. If you've ever tried this before you'll know that it's not as straight forward as a custom field because it's locked on the form. How do we do it:
  1. Change the business requirement to "Not Required"
  2. Open up the Main form
  3. Open up the properties of the field
  4. Turn off the "Visible by Default" flag

This has a few further implications. Firstly, any lookups to the Order Line will have blank lookup fields on the form. For example maybe we had a "Guarantee" entity that needed to reference an Order Detail record. On the Guarantee form this lookup will look like it's empty. There are a couple of ways around this, but my personal preference is to add some Javascript to the Order Detail form so that it populates the hidden name field on save with data from another field - E.g. the Product name.

Other implication is some view cleaning up to be done. If you open up the views you will see they will all display this name by default. All of these will have to be customized to allow for this.

Monday, 27 February 2012

Dynamics 2011 - Cannot Delete Relationship!

Recently we ran into a really interesting problem within our company when trying to delete a relationship between 2 entities. We kept getting 1 of 2 errors:
  1. Cannot delete EntityRelationship because one or more components require it.
  3. An error about an attributemap that no longer existed (I couldn't recreate this error to get the exact wording)

Obviously you'll have checked for the obvious like ensuring the field doesn't exist on a form or removing any connections if applicable. None of this solves the problem, so what could it be?

One of the most common reasons I've found is you've edited the site map and changed where the related link is displayed. So let's say you have an entity called Product and one called Component referencing it (i.e. Component has a look up to Product). There will also be a related list/link on the left navigation of Product where you can see all of it's Components. If you use a sitemap editor to reorder or change how this is displayed this can cause you a problem when trying to delete the relationship.

The second problem is quite an unusual, and I'm not sure why dynamics doesn't automatically do this for you. Let's go back to our example above but add a third entity:

1. Supplier
2. Product (Which has a look up to the Supplier entity)
3. Component (Which has a look up to the Product and Supplier entity)

Here's the scenario I came across, I try to delete the relationship between Product and Supplier and it says:

But what else could possibly require this? I click on details within the error and it points me to the relationship between Product and Component... ok, so what's going on here then?

9 times out of 10 you've added a mapping. In my case whenever I created a new Component from the Product screen I had a mapping to automatically use the supplier from Product to populate Component. So, open up that relationship and check for any mappings! Dynamics should really remove this mapping for you, but such is life.

Just a few things to be aware of when working with relationships.

Monday, 13 February 2012

MS CRM Dynamics - Q2 Release

Many of you might be aware of some major updates coming with the Q2 release of Dynamics (see here for a preview of the release). The major part that was originally of interest to me was multi browser support, but the preview has given me a few other surprises!

1. Business Intelligence / Power View.

Power View is a new reporting services Add-in for SharePoint that came with the release of SQL Server 2012. I believe this is the first stepping stone towards far more powerful reporting within dynamics. Looking forward to playing with this to see what exactly we can do with it.

Only Gotcha is that this is only available on-premise with the Q2 release. Fingers crossed for Q3!

2. Custom Workflow Activities on the Cloud!!

Finally! This one is a big one for me. No more thoughts of plugins and custom entities to handle "special" workflow code. I'm not sure why they weren't selling this one more (or maybe I had my head in the sand for a while!), but this is one I've been looking forward to for a while.

Thursday, 2 February 2012

Convention over Configuration - Case sensitivity?

Today I had a debate with a fellow employee about the naming conventions of some fields on a CRM form. It's a slightly off the norm of "convention vs configuration" arguments, instead it was more of a "Convention A vs Convention B" type argument. Take this example, the particular field that was added was a web resource. For other reasons we have some javascript that passes over the form to try locate these web resources. I designed the javascript so that it will always look for web resources that start with the following naming convention:


So, if we follow this convention we don't need to configure anything. Note that DS is a recognised acronym in our organisation. Unfortunately we ran into some problems in our testing environment, some of the controls weren't being detected. I take a look at the controls and they've been named:


Now, my gut reaction is to fix the script to allow for case sensitivity... but after a quick think this feels wrong to me. If we're following convention shouldn't it be case sensitive? For example, if we introduce camel casing on database field names then "productname" is incorrect, it should read "productName".

So instead of changing it I argued that it should follow the convention of _DS including upper case. I argued that if you follow "convention over configuration" it should include case sensitivity. This should be especially the case for known acronyms (if the convention states all upper-case for acronyms). The debate that he put forward is if you've ever read something like "the design of everyday things" everything should be intuitive, so 'DS' or 'ds' shouldn't matter.

I see his point... but I stick by my guns!