Saturday, July 31, 2010

Categories

Minimize

Archive

Minimize

Tag Cloud

Minimize

Altriva Team Blog

Minimize

Using SQL Server Identity columns to implement Custom Entity numbering

Posted by: Phil Edry on 1/28/2010
  • Categories:
  • CRM

NOTE:  This entry assumes the reader has experience implementing and deploying MS CRM 4.0 plug-ins. More detailed information on how to develop and deploy plug-ins is available here: Altriva Blog - Execution Pipeline Overview and here: MSDN-Registering Plug-ins.

Using SQL Server to generate auto-incrementing numbers is a simple way to avoid number generation risks like number collision, where two entities could be assigned the same number. The procedure flow for generating auto-incrementing entity numbers is as follows:
  1. When a user creates a new entity, a plug-in registered on pre-create or post-create of that entity fires.
  2. The plug-in confirms that the correct entity fired the plug-in.
  3. The plug-in then calls a stored procedure to insert a row into a table with an auto-incrementing identity column. If the plug-in was registered on post-create of the entity, the GUID of the entity can be inserted into the table for future reference.
  4. The stored procedure then selects the generated identity for that row and returns it to the plug-in.
  5. The plug-in then sets an attribute on the entity to the identity and returns.

A separate plug-in must be created and registered for each entity that uses auto-numbering. However, it is up to the implementer whether to use one table to generate all auto-numbering or to create separate tables and stored procedures for each entity. Using one table would mean all entities would share the same sequence of numbers, while using multiple tables would allow each entity to start at “1” and have its own sequence.

I suggest creating the table and stored procedure in the MSCRM database with unique names. While creating database objects in the MSCRM database is unsupported, it allows for much simpler system backups and deployment maintenance when working with multiple environments. Care should be taken in naming tables and stored procedures to avoid naming the objects something that Microsoft might use in future versions of CRM.

Below is the plug-in and stored procedure code for an implementation of auto-numbering for the opportunity entity. The plug-in is registered on pre-create due to a client’s requirement that the displayed opportunity name “new_name” be updated and displayed on save with the auto-number appended. The plug-in stores the auto-number in the “name” attribute. Note that the connection string is hardcoded in this sample code, so changing database servers would require a recompilation of the plug-in. Be sure to modify the connection string as appropriate.


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Web.Services;

namespace Altriva.CRM.Plugins
{
    public class OpportunityCreateHandler: IPlugin
    {        public void Execute(IPluginExecutionContext context)
        {
              DynamicEntity entity = null; 

              // Check whether the input parameters property bag contains a target
              // of the create operation and that target is of type DynamicEntity.
              if (context.InputParameters.Properties.Contains("Target") &&
                 context.InputParameters.Properties["Target"] is DynamicEntity)
              {
                  // Obtain the target business entity from the input parmameters.
                  entity = (DynamicEntity)context.InputParameters.Properties["Target"]; 

                  // Verify that the entity represents an opportunity.
                  if (entity.Name != EntityName.opportunity.ToString()) { return; }
              }
              else
              {
                  return;
              } 

              try
              {
                  int sequentialId = 0;

                  string connectionString = "Data Source=<Data source>;Initial Catalog=<Organization Name>_MSCRM;Integrated Security=sspi";
                  SqlConnection conn = new SqlConnection(connectionString);
                  SqlCommand cmd = new SqlCommand("Altrivacustp_GenerateUniqueId", conn);
                  cmd.CommandType = CommandType.StoredProcedure;
                  cmd.Parameters.AddWithValue("@objectID", null);
                  conn.Open();
                  sequentialId = (int)cmd.ExecuteScalar();
                  conn.Close(); 

                  if (entity.Properties.Contains("name") == false
                  {                                                
                      StringProperty opportunityID = new StringProperty("name", sequentialId.ToString());
                      entity.Properties.Add(opportunityID); 

                     if (entity.Properties.Contains("new_name") == true)
                      {
                          StringProperty opportunityName = new StringProperty("new_name", sequentialId.ToString() + " - " + entity.Properties["new_name"]);
                          entity.Properties.Remove("new_name");                           entity.Properties.Add(opportunityName);
                      }
                  }
                  else
                  {
                      // Throw an error, because Opportunity ID must be system generated
                      throw new InvalidPluginExecutionException("The Opportunity ID can only be set by the system");
                  }                             
                  return;

               }
              catch (System.Web.Services.Protocols.SoapException ex)
              {
                  throw new InvalidPluginExecutionException("An error occurred in the OpportunityCreateHandler plug-in.", ex);
              }
        }
    }
}

Table Creation and Stored Procedure Code:


GO
/****** Object: Table [dbo].[Altrivacustp_OpportunityNumber]    Script Date: 05/07/2008 14:28:54 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Altrivacustp_OpportunityNumber]')
AND
type in (N'U'))
DROP TABLE [dbo].[Altrivacustp_OpportunityNumber]

GO
CREATE TABLE Altrivacustp_OpportunityNumber
(objectId Uniqueidentifier NOT NULL,
ObjectNumber Int IDENTITY(1,1),
Generated bit)

GO
/****** Object: StoredProcedure [dbo].[Altrivacustp_GenerateUniqueId]    Script Date: 05/07/2008 14:27:43 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Altrivacustp_GenerateUniqueId]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[Altrivacustp_GenerateUniqueId]

/****** Object: StoredProcedure [dbo].[Altrivacustp_GenerateUniqueId]    Script Date: 05/07/2008 13:29:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[Altrivacustp_GenerateUniqueId]( @objectID UniqueIdentifier = NULL) as
/************ Test the Stored procedure
Declare @MyTestGuid UniqueIdentifier
SELECT @MyTestGuid = NEWID()
EXECUTE Altrivacustp_GenerateUniqueId @MyTestGuid
SELECT @MyTestGuid = NULL
EXECUTE Altrivacustp_GenerateUniqueId @MyTestGuid
SELECT * FROM Altrivacustp_OpportunityNumber
**********************/
BEGIN
set nocount on

Declare @Generated bit
SELECT @Generated = 0

-- Check if @ObjectID is null
if @objectID IS NULL
       begin
              select @objectID = NEWID(), @Generated =
       end
INSERT Altrivacustp_OpportunityNumber
       (objectId, Generated)
VALUES (@objectID, @Generated)

SELECT ObjectNumber
FROM Altrivacustp_OpportunityNumber
WHERE objectId = @objectID
GO

 

Note that users who are offline and using the CRM for Outlook with Offline Access client will not have a number generated until they go back online and the plug-in is fired during the playback of the playback graph.

I hope these code snippets give you a good starting place for integrating with other systems when entity GUID primary keys are not ideal, or when a simple integer numbering scheme helps your business processes.

Phil Edry

Create a trackback from your own site.

0 Comments

Leave A Comment



Please enter the CAPTCHA phrase above.



  
  

Recent Comments

Minimize

"Hi Lee -- I would just create one-off workflows for each user in your system in that case. Or, create a number of sync users for yourself, and then bulk edit those sync users to another record." Read more
by Phil Edry on Allow Multiple Users to Sync the Same Contact to Outlook Effortlessly with Microsoft CRM

"Norbert and Jeff -- CRM Online couldn't support this exact setup due to current limitations with plug-ins. However, a different integration could be developed that would work with Online and BPOS. If you'd like additional information, please feel free to email info@altriva.com." Read more
by Phil Edry on CRM to SharePoint Site Creation and Clients List Integration

"great article thanks Would the CRM security model be adhered to by the calendar? I have 30 business units in CRM and want a shared calendar at the BU level but for users to only see the records from thier BU and not the whole system. Would be nice not to have to build multiple calendars?" Read more
by Jeff Murtari on Adding a Custom Calendar to the CRM UI

"I also have the same question as Norbert. I'm very interested in this if you're able to do this with CRM Online. " Read more
by Jeff Snyder on CRM to SharePoint Site Creation and Clients List Integration

"Would i be able to do this with the online versions of CRM and BPOS? " Read more
by Norbert on CRM to SharePoint Site Creation and Clients List Integration

"Thanks guys this has solved a major hurdle for us. I may be missing something obvious here but is it possible to modify the workflow, so that "Sync User" can be selected as we have a situtation where the PA would like to set the 'Sync User' for multiple contacts as their manager." Read more
by Lee Southam on Allow Multiple Users to Sync the Same Contact to Outlook Effortlessly with Microsoft CRM

"Good example... event more flexible then CRM Customization Manager" Read more
by Peter Olsson on Export CRM customizations using PowerShell

"Thanks! I encountered this same error, so this was helpful. It would be nice to know what attributes are causing the problem. I heard that some rollups actually changed the size of some attributes. I'm not sure how that would be related to CRM Online or if it could be impacted by the date the organization started using CRM." Read more
by Gretchen Mann on Importing Customizations from one CRM Online Instance to Another CRM Online Instance

  
Copyright 2010 by Altriva LLC