Archive

Archive for October, 2008

Convergence in Copenhagen – Ask the Experts

October 31, 2008 Leave a comment

convergence08

Convergence 2008 Copenhagen begins in three weeks! Microsoft Dynamics CRM will be featured in more than 30 different sessions and we’ll have a great presence in the expo hall with the other Microsoft Dynamics products. 

This year, in the expo hall, you will meet product team members and CRM MVPs. Don’t miss session IDCRM05: “Microsoft Dynamics CRM: Ask the Community Experts”. Two of the panel members are already blogging asking for your questions up front. Ronald Lemmen at Convergence – Ask the Expert and Michael Höhne at Meet me at Convergence EMEA 2008 in Copenhagen. You can also ask the panel questions at this thread in the Microsoft Dynamics CRM Forums.

The buzz has started.

JaAG

CRM 4: Waiting Workflow VS Windows Service

October 29, 2008 Leave a comment

Guest blogger Ayaz Ahmad is a CRM MVP who blogs at http://ayazahmad.wordpress.com/.

Developing some service or workflow to handle some periodic tasks is a common task when creating a CRM solution. Last week, I was looking for the best way to handle periodic tasks such as sending reminders when creating tasks based on date/time, birthday notification and the like.

For example, I have an entity project schedule with thousands of records of this entity. For each of these I want to send email reminders. For each record I can have a waiting workflow which results in thousands of waiting workflows. Or, I can develop a Windows service that runs overnight and process records according to my criteria which includes sending reminders.

What about performance considerations for hundreds of waiting workflows versus one windows service instance running overnight? Here are some merits and demerits for both techniques:

1. Workflows are configurable with no code but we would need to create a configuration mechanism for Windows service and scheduled tasks.

2. We can control the best time to execute a Windows service while workflows are fired on the event matching time condition.

3. Any change in the logic can be rolled out with ease using Windows service during off peak time while waiting workflow remain in waiting as per old logic. So using workflows can create a sizable maintenance problem.

4. If the number of records are high then creating waiting workflows may cause a strain on the system and degrade performance when compared to Windows service.

5. If there is frequent update operation on the waiting workflow entity then every time a change occurs workflow becomes active and moves to a waiting state. This can cause a strain to your system.

Although waiting workflows will not consume any CPU or memory resources, the overhead of handling change and strain on system during peak hours make them less favorable when compared to using a Windows service. Personally I feel the control and maintainability of a Windows service is better than workflows, especially when you take all of this into consideration.

Cheers,

Ayaz Ahmad

PDC 2008 – First Look

October 29, 2008 Leave a comment

The Professional Developers Conference 2008 is moving along quickly; it started big yesterday with the long awaited Azure Services Platform announcements (long awaited for me any ways). The slide below shows the set of services that make up the platform including “Microsoft CRM Online Services”. There is some speculation about what this future offering is but why wait and speculate?

As Dave Thompson demonstrated during yesterday’s keynote, Microsoft Dynamics CRM is already available as a service and has a full set of tools that allow VARs and IT professionals to build high productivity, process aware business applications in an on demand environment. Of course if you’re reading this you probably already knew that.

CRMnAzure

Humberto and David Shutt, respectively, gave presentations on customizing the client and on the move from a single tenant architecture to multi tenant. Tomorrow, Andy Bybee will do a session on building custom application logic (plug-ins and the like) and he’ll provide a peek at CRM 5. If you’re at the PDC don’t miss it.

Barry Givens

Microsoft Dynamics CRM Plug-in Template for Visual Studio

October 28, 2008 Leave a comment

The Microsoft Dynamics CRM User Experience team is always looking to improve our customers’ experience when using the SDK. Past examples of our work in this area include publishing the Plug-in Developer tool and the Plug-in Registration tool for registering plug-ins and custom workflow activities. This blog is about another useful tool for your development toolbox, a C# and VB plug-in template for Microsoft Visual Studio 2008.

Since I have to write plug-ins now and then, I wanted to come up with a standard Visual Studio code template for writing a plug-in. In the code template provided in this blog I have included the following features:

  • A plug-in constructor that supports passing secure and non-secure information to the plug-in
  • A standard Execute method that retrieves and verifies information from the passed context parameter
  • Web service proxy instantiation
  • Basic handling of SOAP exceptions
  • Custom methods that instantiate the Web service proxies for those plug-ins that execute in a child pipeline

The complete plug-in template can be downloaded here. To install the template into Visual Studio, copy the downloaded zip file into your Visual Studio 2008\Templates\ProjectTemplates\Visual C# (or Visual Basic) folder.

To use the template, simply create a new project in Visual Studio, select a C# or Visual Basic project type in the New Project dialog box, and then select the MSCRM Plug-in template.

clip_image002

After creating the new project, you may need to remove and then add the project references to the Microsoft.Crm.Sdk and Microsoft.Crm.SdkTypeProxy assemblies in Solution Explorer if Visual Studio cannot find the assemblies on your system when you build the project. In the project’s property page on the Signing tab, remember to check Sign the assembly and create a new key file.

Below is the C# version of the plug-in code if you just want the code and don’t need the Visual Studio template.

   1: using System;
   2: using System.Collections.Generic;
   3: using Microsoft.Win32;
   4: 
   5: // Microsoft Dynamics CRM namespaces
   6: using Microsoft.Crm.Sdk;
   7: using Microsoft.Crm.SdkTypeProxy;
   8: using Microsoft.Crm.SdkTypeProxy.Metadata;
   9: 
  10: namespace Crm.Plugins
  11: {
  12:     public class MyPlugin : IPlugin
  13:     {
  14:         // Provide configuration information that can be passed to a plug-in at run-time.
  15:         private string _secureInformation;
  16:         private string _unsecureInformation;
  17: 
  18:         // Note: Due to caching, Microsoft Dynamics CRM does not invoke the plug-in 
  19:         // contructor every time the plug-in is executed.
  20:
  21:         // Related SDK topic: Writing the Plug-in Constructor
  22:         public MyPlugin(string unsecureInfo, string secureInfo)
  23:         {
  24:             _secureInformation   = secureInfo;
  25:             _unsecureInformation = unsecureInfo;
  26:         }
  27: 
  28:         // Related SDK topic: Writing a Plug-in
  29:         public void Execute(IPluginExecutionContext context)
  30:         {
  31:             DynamicEntity entity = null;
  32: 
  33:             // Check if the InputParameters property bag contains a target
  34:             // of the current operation and that target is of type DynamicEntity.
  35:             if (context.InputParameters.Properties.Contains(ParameterName.Target) &&
  36:                context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
  37:             {
  38:                 // Obtain the target business entity from the input parmameters.
  39:                 entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
  40: 
  41:                 // TODO Test for an entity type and message supported by your plug-in.
  42:                 // if (entity.Name != EntityName.account.ToString()) { return; }
  43:                 // if (context.MessageName != MessageName.Create.ToString()) { return; }
  44:             }
  45:             else
  46:             {
  47:                 return;
  48:             }
  49: 
  50:             try
  51:             {
  52:                 // Create a Microsoft Dynamics CRM Web service proxy.
  53:                 // TODO Uncomment or comment out the appropriate statement.
  54: 
  55:                 // For a plug-in running in the child pipeline, use this statement.
  56:                 // CrmService crmService = CreateCrmService(context, true);
  57: 
  58:                 // For a plug-in running in the parent pipeline, use this statement.
  59:                 ICrmService crmService = context.CreateCrmService(true);
  60:
  61:                 // TODO Plug-in business logic goes here.
  62: 
  63:             }
  64:             catch (System.Web.Services.Protocols.SoapException ex)
  65:             {
  66:                 throw new InvalidPluginExecutionException(
  67:                     String.Format("An error occurred in the {0} plug-in.",
  68:                        this.GetType().ToString()),
  69:                     ex);
  70:             }
  71:         }
  72: 
  73:         #region Private methods
  74:         /// <summary>
  75:         /// Creates a CrmService proxy for plug-ins that execute in the child pipeline.
  76:         /// </summary>
  77:         /// <param name="context">The execution context that was passed to the plug-ins Execute method.</param>
  78:         /// <param name="flag">Set to True to use impersonation.</param>
  79:         /// <returns>A CrmServce instance.</returns>
  80:         private CrmService CreateCrmService(IPluginExecutionContext context, Boolean flag)
  81:         {
  82:             CrmAuthenticationToken authToken = new CrmAuthenticationToken();
  83:             authToken.AuthenticationType = 0;
  84:             authToken.OrganizationName = context.OrganizationName;
  85: 
  86:             // Include support for impersonation.
  87:             if (flag)
  88:                 authToken.CallerId = context.UserId;
  89:             else
  90:                 authToken.CallerId = context.InitiatingUserId;
  91: 
  92:             CrmService service = new CrmService();
  93:             service.CrmAuthenticationTokenValue = authToken;
  94:             service.UseDefaultCredentials = true;
  95: 
  96:              // Include support for infinite loop detection.
  97:             CorrelationToken corToken = new CorrelationToken();
  98:             corToken.CorrelationId = context.CorrelationId;
  99:             corToken.CorrelationUpdatedTime = context.CorrelationUpdatedTime;
 100:             corToken.Depth = context.Depth;
 101: 
 102:             RegistryKey regkey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");
 103: 
 104:             service.Url = String.Concat(regkey.GetValue("ServerUrl").ToString(), "/2007/crmservice.asmx");
 105:             service.CorrelationTokenValue = corToken;
 106: 
 107:             return service;
 108:         }
 109: 
 110:         /// <summary>
 111:         /// Creates a MetadataService proxy for plug-ins that execute in the child pipeline.
 112:         /// </summary>
 113:         /// <param name="context">The execution context that was passed to the plug-ins Execute method.</param>
 114:         /// <param name="flag">Set to True to use impersonation.</param>
 115:         /// <returns>A MetadataServce instance.</returns>
 116:         private MetadataService CreateMetadataService(IPluginExecutionContext context, Boolean flag)
 117:         {
 118:             CrmAuthenticationToken authToken = new CrmAuthenticationToken();
 119:             authToken.AuthenticationType = 0;
 120:             authToken.OrganizationName = context.OrganizationName;
 121: 
 122:             // Include support for impersonation.
 123:             if (flag)
 124:                 authToken.CallerId = context.UserId;
 125:             else
 126:                 authToken.CallerId = context.InitiatingUserId;
 127: 
 128:             MetadataService service = new MetadataService();
 129:             service.CrmAuthenticationTokenValue = authToken;
 130:             service.UseDefaultCredentials = true;
 131: 
 132:             RegistryKey regkey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");
 133: 
 134:             service.Url = String.Concat(regkey.GetValue("ServerUrl").ToString(), "/2007/metadataservice.asmx");
 135: 
 136:             return service;
 137:         }
 138:         #endregion Private Methods
 139:     }
 140: }

Cheers,

Peter Hecke

Storing Configuration Data for Microsoft Dynamics CRM Plug-ins

October 27, 2008 Leave a comment

CRM MVP Mitch Milam returns as a guest blogger. You can read more from Mitch at his blog.

One of the benefits to the plug-in architecture of CRM 4.0 is the ability to store plug-ins in the CRM database so they may be used by multiple CRM servers. This introduces a slight complication regarding the storage of configuration information. Because the plug-in assembly doesn’t reside on the disk the normal method of using a .config file located with the assembly no longer works.

Luckily, the plug-in architecture solves this issue by allowing the developer to supply configuration information for each step executed by the plug-in.

Plug-in Configuration Architecture

As noted in the CRM SDK article, Writing the Plug-in Constructor, when creating your plug-in, you may define a constructor that passes two parameters to your plug-in: unsecure configuration and secure configuration:

   1: public class SamplePlugin : IPlugin
   2: {
   3:   public SamplePlugin(string unsecureConfig, string secureConfig)
   4:   {
   5:   }
   6: }

Both parameters are strings and may contain any configuration data, in any format, that you wish. For the purposes of this discussion, we will only be concerned with the unsecure configuration parameter.

Creating a Configuration Structure

Since most of us are familiar with the XML configuration provided by the standard Properties.Settings structure, I thought it would be a great idea to retain as much of that experience as possible so we can move code from a stand-alone test application to a plug-in with little difficulty.

Using an XML fragment that closely resembles the Settings section found in the .config file of a .Net assembly, we can create a similarly functional system for storing configuration data. Consider the following XML:

   1: <Settings>
   2:     <setting name="RetryCount">
   3:         <value>5</value>
   4:     </setting>
   5:     <setting name="TaskPrefix">
   6:         <value>This task was created on {0}.</value>
   7:     </setting>
   8:     <setting name="FirstRun">
   9:         <value>false</value>
  10:     </setting>
  11: </Settings>

As you can see, we have three settings which contain values that we would normally find in our .config file and which are used to configure our assembly. Using the Plug-in Registration Tool, we can add this information to the Unsecure Configuration field when registering a new step, as show by the figure below:

MitchCode

Plug-in Configuration Class

I created a simple class to extract values from an XML document for simple data types such as Guids, strings, Booleans, and integers, given the structure we discussed above:

   1: class PluginConfiguration
   2: {
   3:     private static string GetValueNode(XmlDocument doc, string key)
   4:     {
   5:         XmlNode node = doc.SelectSingleNode(String.Format("Settings/setting[@name='{0}']", key));
   6: 
   7:         if (node != null)
   8:         {
   9:             return node.SelectSingleNode("value").InnerText;
  10:         }
  11:         return string.Empty;
  12:     }
  13: 
  14:     public static Guid GetConfigDataGuid(XmlDocument doc, string label)
  15:     {
  16:         string tempString = GetValueNode(doc, label);
  17: 
  18:         if (tempString != string.Empty)
  19:         {
  20:             return new Guid(tempString);
  21:         }
  22:         return Guid.Empty;
  23:     }
  24: 
  25:     public static bool GetConfigDataBool(XmlDocument doc, string label)
  26:     {
  27:         bool retVar;
  28: 
  29:         if (bool.TryParse(GetValueNode(doc, label), out retVar))
  30:         {
  31:             return retVar;
  32:         }
  33:         else
  34:         {
  35:             return false;
  36:         }
  37:     }
  38: 
  39:     public static int GetConfigDataInt(XmlDocument doc, string label)
  40:     {
  41:         int retVar;
  42: 
  43:         if (int.TryParse(GetValueNode(doc, label), out retVar))
  44:         {
  45:             return retVar;
  46:         }
  47:         else
  48:         {
  49:             return -1;
  50:         }
  51:     }
  52: 
  53:     public static string GetConfigDataString(XmlDocument doc, string label)
  54:     {
  55:         return GetValueNode(doc, label);
  56:     }
  57: }

Putting PluginConfiguration to Work

Once we have our PluginConfiguration class added to our project, we need to modify the plug-in constructor to extract the values from our configuration string:

   1: public SamplePlugin(string unsecureConfig, string secureConfig)
   2: {
   3:     XmlDocument doc = new XmlDocument();
   4:     doc.LoadXml(unsecureConfig);
   5: 
   6:     string TaskPrefix = PluginConfiguration.GetConfigDataString(doc, "TaskPrefix");
   7:     bool FirstRun = PluginConfiguration.GetConfigDataBool(doc, "FirstRun");
   8:     int RetryCount = PluginConfiguration.GetConfigDataInt(doc, "RetryCount");
   9: }

There is no automatic determination of data types so you will need to know which method to use to extract a specific value from the configuration data.

Conclusion

Today we’ve covered how to store configuration information used by a CRM plug-in within the CRM database itself. One of the items you may wish to remember is that each step executed by a plug-in has its own configuration information. If you are using the same configuration data for multiple steps, you will need to set the configuration values to be the same between steps or just insert configuration data that is necessary for a particular step to complete successfully.

Mitch Milam

CRM MVPs on the Microsoft Dynamics CRM Resource Center

October 22, 2008 Leave a comment

Joel Lindstrom of CustomerEffective is the first CRM MVP to provide an article on the Resource Center. It is titled, “Categorizing tracked e-mail in Microsoft Dynamics CRM“.

rc

“You are a busy professional who sends and receives a large quantity of e-mail messages. Fortunately, you have Microsoft Dynamics CRM, so you track these messages from Microsoft Office Outlook and associate them with the account, contact, or opportunity that they pertain to.

But say that you want to further categorize these e-mail messages, so that when you look through a mile-long list of e-mails on a contact record, you can see those tagged as, for example, “Product Question.”

Read more…

Congrats Joel!

Renee Wesberry

Trip Report: CRM Users Group First annual Summit

October 21, 2008 Leave a comment

CRMugLogo

crmugbanner

September 22-24th, Clay and I attended the inaugural CRM Users Group Summit in Las Vegas! If you’ve never been to Vegas, it is quite the spectacle… You know what they say about Las Vegas:

“What happens in Vegas, stays in Vegas!”

Good thing that the CRMUG event actually took place in Henderson, Nevada, which is a suburb of Las Vegas, so we can safely tell you about all that happened. J

Summit Overview

This was the 1st annual CRM Users Group Summit, with approximately ~110 attendees – a mixture of CRM Users and a select set of CRM ISVs and Partners, including Cincom Systems, Experlogix, Sales Centric, Ten Digits, Tectura, Tribridge, and Green Beacon Solutions among many others. By rough estimate, about half of the attendees were running CRM 3.0 and about half on CRM 4.0. The primary focus of the summit was to enable customers to learn more about the product, network with other users, network with the CRM team, and learn about various ISV solutions.

CRMKeynoteKeynote – Bryan Nielson, Director of CRM Product Management, delivered the keynote, imparting his perspective on the state of the business and what to expect between now and the release of CRM 5.0. In particular the Solution Accelerators program was highlighted, which generated a lot of customer interest and positive feedback. 

Sessions – There were two and a half days of sessions organized around executive, infrastructure, IT/development, and sales/marketing/customer service tracks.

CRMsessions

Microsoft Dynamics Communities Booth in the Expo Hall

Expo Hall – In addition to sessions, there was an Expo Hall featuring product booths from various CRM and GP partners and ISVs, as well as Dynamics Communities and “Ask the Experts” support booths. 

In addition to leading and participating in sessions, Clay and I manned the CRM “ask the experts” booth with members of the CRM Support team.

CRMexperts

Ask the Experts Booth. Nick Johnson (CRM Support), Mark Rhodes (CRMUG Program Coordinator), Jason Phillips (CRM Support)

Town Hall – Finally, we participated in a “town hall” panel discussion where we fielded questions from the audience along with John O’Donnell (ISV Evangelist), David Held (CRM Product Marketing), and Nick Johnson (CRM Support Lead).

In all, the summit was well received. One customer provided this feedback to the event organizers:

“Thank you for putting together such a terrific event.  Not only did I learn a lot, but I left the conference feeling energized after being around like minded CRM people for 3 days!…  Thanks again for putting together a terrific panel of speakers and vendors.  I’m already looking forward to next year!”

CRMUG Background

The CRMUG was started in 2007 by Dynamic Communities Inc. – an administrative organization behind professional associations and software user groups, whose mission is to “foster the development and growth of User Groups that maximize value to individuals and companies using the products they are founded around.” This organization is independent of Microsoft, but is developed in conjunction with Microsoft. Dynamic Communities was formed initially for Dynamics AX as a grass-roots, volunteer-driven user group (Microsoft provided seed money). Eventually, GP and Nav user groups formed, making it a multi-group organization. Also of note, the CRMUG Pacific Northwest regional chapter met for the first time at this conference.

From my perspective, it is very good to see an independent user group forming around our product. It is a real marker of a maturing product and in addressing the need for a strong network of customers who act as a resource for each other. 

Derik Stenerson & Clay Robinson