Knowledge found and lost while working with Microsoft Dynamics CRM
RSS icon Home icon
  • Shrinking the CRM database transaction log

    Posted on June 27th, 2007 mitch Print Print No comments

    On one of my CRM installations I began to notice that the transaction log for the MSCRM database was not shrinking even though it was being backed up properly.  If you don't keep an eye on the physical files, they can grow to huge sizes.

    After making a full backup of the database and transaction log, the following SQL script will shrink the transaction log file:

       1: BACKUP LOG MYCOMPANY_MSCRM WITH TRUNCATE_ONLY
       2: GO
       3: DBCC SHRINKFILE (MYCOMPANY_MSCRM_log, 1)
       4: GO

     

    Note: Just replace MYCOMPANY with the name of your CRM database name.

    Note 2: The second command does indeed have "_log" appended to the name. That is the device name for the transaction log itself.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5.00 out of 5)
    Loading ... Loading ...
    1,629 views
  • Interesting info on the email.sender Field

    Posted on June 26th, 2007 mitch Print Print No comments

    I was using the SDK to work with inbound CRM email this afternoon and I noticed something interesting with the email.sender field.

    It contains both the display name AND the email address, as you can see below:

    "Administrator" Administrator@mycrmcompany.com

    While ordinarily this would not be a problem, it certainly is if you are expecting only the email address.

    I'm not sure if the other email-related fields are similar, but I would bet they are.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5.00 out of 5)
    Loading ... Loading ...
    1,182 views
  • Using the JavaScript Switch Statement

    Posted on June 25th, 2007 mitch Print Print No comments

    Ben Vollmer posted an article this morning about using the JavaScript Switch statement with a fairly good explanation of how it works and how it can be used as a replacement for the IF statement in certain situations.

    That reminded me of a rather unusual Switch statement implementation that is found in the CRM SDK that I thought deserved a bit more exploration.

    Before we get into that, let's take a quick look at the code Ben used in his article:

       1: var prefix = "";
       2: switch (crmForm.all.customertypecode.SelectedText)
       3: {
       4:      case "Manufacturer":
       5:                prefix="MFG";     
       6:                break;
       7:      case "Investor":
       8:                prefix="INV";
       9:                break;
      10:  
      11:      case "Press":
      12:                prefix="SCUM";
      13:                break;
      14:      default:
      15:                break;
      16: }
      17:  

     

    This code checks the value of the customertypecode field and sets the value of a variable prefix accordingly.

    If you've ever browsed around the CRM SDK, you may have seen the following code demonstrating the CRM Form OnChange event:

       1: // Attempt to auto-format basic US phone numbers.  This method supports
       2: // 7 and 10 digit numbers.  Example: (410) 555-1212
       3:  
       4: // Get the field that fired the event
       5: var oField = event.srcElement;
       6:  
       7: // If we have the field and all is well
       8: if (typeof(oField) != "undefined" && oField != null)
       9: {
      10:     // Remove any non-numeric characters
      11:     var sTmp = oField.DataValue.replace(/[^0-9]/g, "");
      12:  
      13:     // If the number is a length we expect and support, format the number
      14:     switch (sTmp.length)
      15:     {
      16:         case "4105551212".length:
      17:             oField.DataValue = "(" + sTmp.substr(0, 3) + ") " + sTmp.substr(3, 3) + "-" + sTmp.substr(6, 4);
      18:             break;
      19:  
      20:         case "5551212".length:
      21:             oField.DataValue = sTmp.substr(0, 3) + "-" + sTmp.substr(3, 4);
      22:             break;
      23:     }
      24: }

     

    This too shows you how to utilize the Switch statement, but as you can see, it's very strange looking and a bit hard to understand at first. I had to look at it several times to figure out what they are doing and realized that it is a very cool piece of code indeed.

    What this code does:

    This code segment checks the length of a string then formats the string if it meets one of two criteria for length.  In reality, you could just have easily written the code as:

       1: // If the number is a length we expect and support, format the number
       2: switch (sTmp.length)
       3: {
       4:     case 10:
       5:         oField.DataValue = "(" + sTmp.substr(0, 3) + ") " + sTmp.substr(3, 3) + "-" + sTmp.substr(6, 4);
       6:         break;
       7:  
       8:     case 7:
       9:         oField.DataValue = sTmp.substr(0, 3) + "-" + sTmp.substr(3, 4);
      10:         break;
      11: }

     

    Why this is cool code:

    Instead of checking for a hard-coded length, the developer instead used a little JavaScript trick that allows you to work with properties of a literal value ( the stuff inside the quotes ).  This actually makes the code self-documenting.

    At this point you're probably wondering what in the heck I am talking about. Well, hang on for a bit longer and I'll explain everything.

    If the CRM developer had written the OnChange code as shown in my second example, we would be left to wonder, "Just what they heck do the values 10 and 7 have to do with anything?" Unless, of course, they inserted comments that described those values.

    As it stands, what the case statement actually does( in pseudo code ):

    If the length of the variable sTmp is equal to the value of a phone number in the format: "4105551212" then I would like format the Form field as follows….

    If the length of the variable sTmp is equal to the value of a phone number in the format: "5551212" then I would like format the Form field as follows….

    While this may seem a bit odd and confusing, it's actually very practical.  You can view the expected phone number variations while also seeing how the developer wished to handle the formatting for cases that matched their criteria.  To add additional formatting choices, all you must do is copy the case block of code and change the length test and the resultant formatting.

    Clear as mud?

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5.00 out of 5)
    Loading ... Loading ...
    3,284 views
  • CRM Update Rollup 2 for Microsoft Dynamics CRM 3.0 is available

    Posted on June 25th, 2007 mitch Print Print No comments

    Update Rollup 2 for Microsoft Dynamics CRM 3.0 is available.  Download link.

    Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    1,141 views
  • Great Idea: Four hours upfront and then reevaluate

    Posted on June 25th, 2007 mitch Print Print No comments

    The team at 37 Signals posted about a change in their development practices:

    We recently decided to stop diving in too deep on tasks right away. Instead, we’re going for four hour chunks upfront. We start work on a task and then, after the first four hours, come up for air.

    That is an absolutely terrific idea and one that you should consider implementing – I know I will.

    So how does this apply to CRM?

    As I tell my customers and other Microsoft Partners with whom I work, there are almost always two or three different ways to do everything within CRM.  All of which are perfectly viable solutions.  The trick is knowing which particular solution best fits the problem you are trying to solve.

    The really tricky part is that sometimes what you know is the best solution, turns out to actually be the second-best solution.  A fact that you didn't realize until you started work on it.

    Or, if you start off with the phrase, "Well, it shouldn't be that hard," only to find out that while it didn't appear to be that hard, there were hidden factors that you simply could not have known until you actually started the development process.

    It is the latter fact that gets most of us in trouble.  Sometimes CRM just doesn't work the way you think it should, or the way you would like it to, and it can take quite a bit longer to develop a solution that expected.

    So, the next time you jump into CRM solution development, set an alarm for four hours and when it goes off, take a break then reevaluate your progress when you return.  Are we on the right track?  If so, keep going, if not, find the next best course of action and restart the process.

    Development, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    1,119 views
  • Retrieving a list of CRM Queues

    Posted on June 18th, 2007 mitch Print Print No comments

    As I'm putting the finishing touches on my first commercial CRM Utility, I found the need to obtain a list of CRM Queues so that the user can select one from a combo box, like this:

    image

    When working with queues, you need to know two things:

    1) The Queue Name ( for the user to see ).

    2) The Queue ID ( for the CRM system to reference ).

    Both of which can be returned by performing a query against the CRM web service.

     

    The following .NET method will return just such a list:

       1: private ArrayList GetQueueNames(CrmService myCRMService)
       2: {
       3:     string fetchString =
       4:         "<fetch version=\"1.0\" "+
       5:         "output-format=\"xml-platform\" "+ 
       6:         "mapping=\"logical\" distinct=\"false\">" +
       7:         "<entity name=\"queue\">"+
       8:         "<attribute name=\"name\"/>"+
       9:         "<attribute name=\"emailaddress\"/>"+
      10:         "<attribute name=\"businessunitid\"/>"+
      11:         "<attribute name=\"queueid\"/>"+
      12:         "<order attribute=\"name\" descending=\"false\"/>"+
      13:         "<filter type=\"and\">"+
      14:         "<condition attribute=\"name\" operator=\"not-null\"/>"+
      15:         "</filter></entity></fetch>";
      16:  
      17:     XmlDocument xmlDoc = new XmlDocument();
      18:     xmlDoc.LoadXml(myCRMService.Fetch(fetchString));
      19:     XmlNode node = xmlDoc.SelectSingleNode("resultset");
      20:  
      21:     ArrayList queueList = new ArrayList();
      22:     string n1 = string.Empty;
      23:     string n2 = string.Empty;
      24:  
      25:     foreach (XmlNode n in node.ChildNodes)
      26:     {
      27:         n1 = n.SelectSingleNode("name").InnerText.ToString();
      28:         n2 = n.SelectSingleNode("queueid").InnerText.ToString();
      29:         queueList.Add(new ItemData(n2, n1));
      30:     }
      31:  
      32:     return queueList;
      33: }

     

    I created the FetchXML query by following Ronald Lemmen's article on pulling it from the Advanced Find query, which looked like this:

    image

     

    Populating the Combo Box

    In order to use the data within a combo box, I had to create a data structure to hold the data.  Add this class to your application:

       1: public class ItemData
       2: {
       3:  
       4:     private string m_Value;
       5:     private string m_Description;
       6:  
       7:     public object Value 
       8:     {
       9:         get { return m_Value; }
      10:     }
      11:  
      12:     public string Description 
      13:     {
      14:         get { return m_Description; }
      15:     }
      16:  
      17:     public ItemData(string NewValue, string NewDescription)
      18:     {
      19:         m_Value = NewValue;
      20:         m_Description = NewDescription;
      21:     }
      22:  
      23:     public override string ToString()
      24:     {
      25:         return m_Description;
      26:     }
      27:  
      28: }

     

    and use this code to actually populate your combo box:

       1:  
       2: ArrayList queueList = GetQueueNames(myCRMService);
       3:  
       4: if (queueList.Count > 0)
       5: {
       6:     cboQueues.DataSource = queueList;
       7:     cboQueues.DisplayMember = "Description";
       8:     cboQueues.ValueMember = "Value";
       9:  
      10: }

     

    I think the same code will work to fill a list box, but I haven't tested it.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    2,029 views
  • My Guest Post on the Dynamics CRM Team Blog

    Posted on June 18th, 2007 mitch Print Print No comments

    Greetings Everyone,

    I was asked to be one of this month's guest contributors on the Dynamics CRM Team blog.  You may find the article here.

    This is really cool and a great way to start off the week.

    Later, Mitch

    Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    1,691 views
  • Dynamics CRM 3.0 Implementation Strategies

    Posted on June 18th, 2007 mitch Print Print 2 comments

    I'm sure that many of you have heard the old African proverb:

    How do you eat an elephant? One bite at a time.

    Consider treating the design and implementation of Dynamics CRM with that exact phrase in mind.

    In my previous article on driving user adoption, I discussed several issues that I have run up against implementing CRM.  We'll continue that discussion today with a focus on the implementation phase of CRM.

     

    Strategy #1: Take Small Steps

    Dynamics CRM can be quite overwhelming to new users because of the sheer amount of functionality in the product.

    Since CRM is modular, break up your implementation into related phases:

    1) Sales

    2) Marketing

    3) Service

    Completely implement one phase before beginning the next.

     

    Strategy #2: Training, Training, Training

    Instead of sticking your people in a room for a week, consider breaking the training up in to "mini-sessions" that occupy a small block of time ( like a couple of hours ), and make the training topical and related to the current stage of the implementation.  For example, let's take the Sales implementation phase.   You could create a training session that covered the following topics:

    1) Working with Leads

    2) Converting a Lead into an Account and Contact

    3) Working with Accounts

    4) Working with Contacts

    5) Tracking activities and the interaction with customers.

    Again, the idea is to give the new CRM user enough material to allow them to understand the topics relevant to the CRM functions they are using while ( hopefully ) not overwhelming them with too much information.

    If possible, it is also helpful to limit the number of students to four of five to keep the interaction high and the disruption low.

     

    Strategy #3: Periodic Reviews

    We have found that when you first install CRM, users ( and their management ) don't know enough about the Dynamics CRM system to be able to actually answer certain questions that you may ask.  Like, "What information do you need to see on the Active Contacts View?"

    If you give users 3-4 weeks to use the system, you will start to hear questions that begin with: "Can we," "What if", "How about?" 

    For example, "Can we add the Contact's street address and phone number to this view so I can print it off before I leave town to visit those customers?"

    The same thing applies to adding custom fields to forms, creating custom workflows, reports, etc.

    Having a built-in review once per month for 3-4 months after the implementation works pretty well.

     

    In Conclusion:

    I hope this discussion has gotten you to think about your current or next CRM implementation.  You can easily build on these strategies to incorporate your own procedures and processes to make the system work for you.

    Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    1,991 views
  • Launching a Workflow Rule from JavaScript

    Posted on June 15th, 2007 mitch Print Print 24 comments

    One of the Microsoft Partners I work with asked if you could launch a Workflow rule when a CRM user changed a specific field on the Opportunity form.  That topic sounded familiar and after digging around a bit, I found a newsgroup post where Steven G had provided just such a solution.  I've reworked Steven's code a bit to make it more generic and here is what you need to do to make this solution work:

     

    Step 1: Locate the Workflow Process ID

    You will need the ID of the CRM Workflow Process that you will be running.  Open SQL Query Analyzer or SQL Server Management Studio and run the following script against the MSCRM database.

    Note: Replace "my workflow rule" with the name of the rule you are searching for.

       1: select 
       2:     ProcessID,
       3:     [name]
       4: from 
       5:     wfprocess 
       6: where 
       7:     [name] like 'my workflow rule%' 
       8:     and 
       9:     ProcessTypeCode = 1
      10:  

    Copy the ProcessID value that is returned by the script.

     

    Step 2: Add code the the  OnLoad Event of the CRM Entity you will be using

    The following code needs to be added to the OnLoad Event of the Form.

       1: StartWorkflow = function(entityName, entityId, workFlowProcessID)
       2: {
       3: var xml = "" + 
       4: "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + 
       5: "<soap:Envelope xmlns:soap=\"" +
       6: "http://schemas.xmlsoap.org/soap/envelope/\" " +
       7: " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
       8: "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" + 
       9: "<soap:Body>" + 
      10: "<Request xsi:type=\"ExecuteWFProcessRequest\" "+
      11: "xmlns=\"http://schemas.microsoft.com/crm/2006/WebServices\">" + 
      12: "<ProcessId>" +
      13: workFlowProcessID +
      14: "</ProcessId>" + 
      15: "<EntityMoniker>" + 
      16: "<Id xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" +
      17: entityId +
      18: "</Id>" + 
      19: "<Name xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" +
      20: entityName +
      21: "</Name>" + 
      22: "</EntityMoniker>" + 
      23: "</Request>" + 
      24: "</soap:Body>" + 
      25: "</soap:Envelope>" + 
      26: "";
      27:  
      28: var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
      29:  
      30: xmlHttpRequest.Open("POST", 
      31:   "/mscrmservices/2006/CrmService.asmx", 
      32:   false);
      33:  
      34: xmlHttpRequest.setRequestHeader("SOAPAction",
      35:   "http://schemas.microsoft.com/crm/2006/WebServices/Execute");
      36:  
      37: xmlHttpRequest.setRequestHeader("Content-Type", 
      38: "text/xml; charset=utf-8");
      39:  
      40: xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
      41: xmlHttpRequest.send(xml);
      42:  
      43: var resultXml = xmlHttpRequest.responseXML;
      44:  return resultXml.xml;
      45: }

     

    Step 3: Add code to the OnChange Event of the Form Attribute

    This code goes in the OnChange Event for the Form Attribute we're checking. 

    Since we must have an ID assigned to the record, this code will only work if you are Updating the record.  You may also wish to add additional logic to check various conditions before running the Workflow Rule.

    Note: Replace the ID assigned to workflowProcessID with the value you found in step 1.

       1: if (crmForm.FormType == 2)
       2: {
       3: var workflowProcessID = '3B6CFF1A-A117-4243-8134-8DA612CC3A5C'; 
       4: var entityName = crmForm.ObjectTypeName; 
       5: var entityID = crmForm.ObjectId;
       6:  
       7: var retVal = '';
       8:  
       9: retVal = StartWorkflow(entityName, entityID, workflowProcessID);
      10:  
      11: //alert(retVal);
      12: }

     

    That's pretty much it.  I would advise that you remove the comment in line 11 so that you can see the XML that is returned by the CRM web service so that you can ensure that the code is working properly.

     

    Additional Notes:

    Michael Höhne has a tool that allows you to capture the output of a CRM web service call and, among other things, convert that information to JavaScript.  This tool was invaluable while I was recreating Steven's work and debugging my function.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (8 votes, average: 4.88 out of 5)
    Loading ... Loading ...
    9,364 views
  • Book Recommendation: Idea Mapping

    Posted on June 14th, 2007 mitch Print Print No comments

    If you've been reading my blog for very long you know that I use a product from MindJet called MindManager to do everything from product and project planning to brainstorming.

    I'm sure most of you have never heard of Mind Mapping so you're really not sure what I'm talking about.  Here is how Wikipedia defines it:

     

    A mind map is a diagram used to represent words, ideas, tasks or other items linked to and arranged radially around a central key word or idea. It is used to generate, visualize, structure and classify ideas, and as an aid in study, organization, problem solving, and decision making.

    It is an image-centered diagram that represents semantic or other connections between portions of information. By presenting these connections in a radial, non-linear graphical manner, it encourages a brainstorming approach to any given organizational task, eliminating the hurdle of initially establishing an intrinsically appropriate or relevant conceptual framework to work within.

    A mind map is similar to a semantic network or cognitive map but there are no formal restrictions on the kinds of links used.

    Mind Mapping really does work for people like me who are visually oriented.  It allows you to clearly and concisely organize your thoughts into a cohesive picture.

    A couple of months ago I picked up a book called Idea Mapping, by Jamie Nast. In this book she covers how to utilize Idea Mapping ( a form of Mind Mapping ) to do everything from planning to note taking.

    If you are new to mind mapping or you are using mind mapping and you'd like to see how to become more productive with your maps, I would recommend reading this book.  It has some background that is useful and some techniques that come from years of teaching the subject world-wide.

    It also comes with a CD containing the demo version of the MindManager application.

    Book Reviews and Recommendations
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    1,216 views