Detecting IE Compatibility View (C#)

May 18th, 2012

Recently I was working on a project that required me to force the user into IE8 document mode, but ONLY if they were using IE9 compatibility view (CV). If you are unfamiliar with the method of forcing a particular document mode, you can read more about it in this MSDN article. It looks a little bit like this:

	<head>
		<title>My Page</title>
		<meta http-equiv="X-UA-Compatible" content="IE=8" />
	</head>

This meta tag is placed in the head section of your page and must be placed before all other elements (with the exception of the title element and other meta tags). This is a perfectly valid way to force IE8, however I needed to make sure this was only done for IE9 CV, and that brings me to the topic of this post.

Regardless of your reasons to detect CV in IE, here is how you can accomplish it…

Each version of IE can be told apart by its user agent string. I won’t get into the details of the UA string as its history is lengthy, but you can read all about it here. The main elements that we need to look at are the Version token and the Trident token. Below are the tokens that you will find in each browser:

VERSION TRIDENT
IE9 Standard MSIE 9.0 Trident/5.0
IE9 CV MSIE 7.0 Trident/5.0
IE8 Standard MSIE 8.0 Trident/4.0
IE8 CV MSIE 7.0 Trident/4.0
IE7 MSIE 7.0 No Trident token
IE6 MSIE 6.0 No Trident token

As you can see, each browser has a unique combination of these two tokens. We can use this knowledge to now create a function that will tell us what browser mode is in use. My function is shown below:

	private string GetIEBrowserMode()
    {
        string mode = "";

        string userAgent = Request.UserAgent; //entire UA string
        string browser = Request.Browser.Type; //Browser name and Major Version #

        if (userAgent.Contains("Trident/5.0")) //IE9 has this token
        {
            if (browser == "IE7")
            {
                mode = "IE9 Compatibility View";
            }
            else
            {
                mode = "IE9 Standard";
            }
        }
        else if (userAgent.Contains("Trident/4.0")) //IE8 has this token
        {
            if (browser == "IE7")
            {
                mode = "IE8 Compatibility View";
            }
            else
            {
                mode = "IE8 Standard";
            }
        }
        else if (!userAgent.Contains("Trident")) //Earlier versions of IE do not contain the Trident token
        {
            mode = browser;
        }

        return mode;
    }

Pretty simple once you know what to look for. The last step for my requirements was to force the browser into IE8 document mode if the user was using IE9 CV.

	protected void Page_Load(object sender, EventArgs e)
    {
        if (GetIEBrowserMode() == "IE9 Compatibility View")
        {
            HtmlMeta force = new HtmlMeta();
            force.HttpEquiv = "X-UA-Compatible";
            force.Content = "IE=8";
            Header.Controls.AddAt(0, force);
        }
    }

This snippet is very straightforward. We are creating a new meta tag with the necessary attributes, and we are adding it to the header of our page at an index of 0 because it needs to be among the first header elements. Now run your page! If you’d like to test the results, a good way to do so is to open IE Developer Tools (F12) and at the top there will be a drop down titled “Browser Mode”. Add a label to your page that shows the result of GetIEBrowserMode() and you can easily switch between the different browser modes with an accurate detection.

Lastly, IE10 is on the horizon and will contain different tokens in its UA string as well. The IE10 Trident token is now Trident/6.0, and compatibility mode for IE10 will still map to IE7 standards. It might be a good idea to add this to your function as it will be in use soon. Below are a few very helpful links on UA strings (IE in particular).

Creating UDF using SQLCLR on MS SQL

May 14th, 2012

Let’s begin this post by defining SQLCLR (SQL Common Language Runtime), SQLCLR refers to the use of CLR execution within the SQL Server database Engine. CLR is also often referred to as the “ .Net Framework”, “.NET”, or by the name of the programming language that might be used, such as “C#” or “VB.Net”. The SQLCLR allows managed code to be hosted by, and run in, the Microsoft SQL Server environment. With SQLCLR you can create Stored Procedures, User Defined functions, table-value UDF, triggers, user-defined types and user-defined aggregates.

In order to allow the SQLCLR code to be executed within this SQL Server, you have to run the following code:

--This fist line enables CLR code changing the setting to 1 (true)
EXEC sp_configure ‘clr enabled’, 1

--This line of code tells SQL Server to start using any changed setting.
RECONFIGURE;

Now the server is ready to run the CLR code, but we need to write it first, so open your Visual Studio (I’m using VS 2010) , create a new project and select the SQLCLR Database Project. Visual Studio has 2 different options: C# or VB. All the code in this post has been written in C#, however it is easy enough to understand.

I will not cover all the possible options we can build using SQLCLR so, I will focus this blog post on creating a user defined function only.

One important part of the project is connecting to the database. You don’t have to do it but if you do it your deployments would be a lot easier.

The image below shows where you have to do it. You just have to click the browse button and select the database where you would deploy the assembly.

In order to create a User Defined Function (UDF), right click on your project and click on the option ADD then New Item. Then select the UDF option from the list shown in the image below.

Visual Studio will create a “hello” version for you. Notice that Microsoft template is using “[Microsoft.SqlServer.Server.SqlFunction]” on top of the function. The SQLFunction attribute is
used to indicate that the method should be registered in SQL Server as a function. Attributes are also used to set the DataAccess, FillRowMethodName, IsDeterministic, IsPrecise, Name, SystemDataAccess, and TableDefinition properties of the function. For more information visit the MSDN library

The code below has two functions the first function is called Sum2 and it just adds two values and return the total. The second function is called Average2, which basically gets a list of comma-delimited values as a string, parses the list, calls Sum2 function which sums the values and then divide the by the total number of values received.


public partial class UserDefinedFunctions
{

    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlInt32 Sum2( SqlInt32 p1, SqlInt32 p2 )
    {
        return p1 + p2;
    }

    [Microsoft.SqlServer.Server.SqlFunction]
    public static float Average2(SqlString values)
    {
        if (!values.IsNull)
        {//yield break;

            SqlInt32 sum = 0;
            int count = 0;

            foreach (string v in values.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                sum = Sum2(sum, SqlInt32.Parse(v));
                count++;
            }
            return (float)sum / count;
        }
        return 0;
    }

};

As you can see it is a simple code. SQLCLR UDFs are used only to perform computations, that is, not to access any tables. Your functions are then typically used in an SQL DML statement.

Now how to use the code? You definitely have to compile the code, in Visual Studio it is as easy as building the solution. But what about if you are not using VS and you want to do it from the command prompt, well in that case, you can use the C# or the VB.Net compiler. Take a look to the following example:

//C#
csc.exe /target:library  /out:CLRAssemblies.dll  UDFPractice.cs
'VB
vbc.exe /target:library  /out:CLRAssemblies.dll  UDFPractice.cs

Both compilers can be typically found in “C:WindowsMicrosoft.netFrameworkv3.5”. The /Target:library switch tells the compiler that you are compiling an assembly without a starting point. The /out switch simply tells the compiler what it should name the created assembly.

I think is time to create the assembly, If your visual studio is set up to deploy directly on your database server you should not have to do almost anything because it would register the assembly for you, however if that’s not the case the following code would help you.

--Run this on your Database server using SQL Management Studio

Create Assembly CLRAssemblies
From ‘[path] CLRAssemblies.dll ’

You can also check if the assembly was created as shown on the following image:

Now let’s test it out running the code for Sum2 and Average2 UDFs:

Sum Results:

Average Results:

SQLCRL UDFs are very easy to understand and use.I hope you have fun creating your own. For more information visit the MSDN Library.

CRM 2011: Error when Deleting an Audited Entity with a Plug-in

May 11th, 2012

Normally, auditing in CRM 2011 doesn’t cause any issues. A user can easily delete an audited entity manually, via OData, or a .NET plug-in. However, I’ve recently run into an issue when attempting to delete during the SetStateDynamicEntity step for the Quote.

The requirement was to delete a set of associated Assets on a closed Quote during the “Activate Quote” process for the revised copy of the closed Quote. My first pass at the deletion code was as simple and straightforward as the following:

public static void DeleteAsset(new_asset assetToDelete)
{
   try
   {
      using (var ctx = new OrgContext(_service))
      {
         ctx.Delete(&quot;new_asset&quot;, assetToDelete.Id);
         ctx.SaveChanges();
      }

   }
   catch (Exception ex)
   {
   }
}

I’ve deleted entities in plug-ins before, but this was throwing an error when saving, for some reason I could not determine. The Microsoft.Crm.AuditMonikerMessagesPlugin was throwing a null reference exception, despite the context and entity object being populated:

The Web Service plug-in failed in OrganizationId: 8fa8e23a-2083-e111-9161-005056bc4f66; SdkMessageProcessingStepId: b92673ed-dc92-442b-a6c6-82f2fce14585; EntityName: new_asset; Stage: 25; MessageName: Delete; AssemblyName: Microsoft.Crm.AuditMonikerMessagesPlugin, Microsoft.Crm.Audit, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35; ClassName: Microsoft.Crm.AuditMonikerMessagesPlugin; Exception: Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.Crm.AuditLib.GetAction(IPluginExecutionContext context, Entity entity, Boolean useParentMessageName)
   at Microsoft.Crm.AuditBase.LogAuditData(IPluginExecutionContext context, Guid objectId, Boolean allowEntityOnlyAudit, Entity entity, EntityMetadata entityMetadata)
   at Microsoft.Crm.AuditMonikerMessagesPlugin.Execute(IServiceProvider serviceProvider)
   at Microsoft.Crm.Extensibility.V5PluginProxyStep.ExecuteInternal(PipelineExecutionContext context)
   at Microsoft.Crm.Extensibility.VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)
.
.

Doing a bit of Googling, the closest article I could find was “Can’t Delete an Email From Microsoft Dynamics CRM 2011″, where the situation wasn’t quite like mine, but I gave his suggestion a shot anyway and disabled auditing on the Asset entity.

To my surprise, that actually worked perfectly. Unfortunately, while the records were being deleted as expected, we needed auditing to be enabled for the entity, so this wasn’t a suitable fix. In the end, I simply hooked up a JavaScript function to the “Activate Quote” ribbon button that used OData to delete the Asset records.

Disabling Export to Excel in MS CRM 2011

April 25th, 2012

** Note – the solution for this project is available for download.

I had a requirement to disable the export to excel functionality in CRM 2011. Fortunately, CRM has an privilege that can be set on security roles (open a security role, go to the Business Management tab and look under “Miscellaneous Privileges”) that enables or disables the export to Excel functionality. Unfortunately when this privilege is removed, users can’t save reports in any format such as a PDF file, an Excel file, a Word file, etc. (I understand that if you don’t want a user exporting to excel from a list view then why would you let them run a report and save it in Excel format, but it also removes the ability save as PDF. I didn’t make up this requirement.) I didn’t find much help in my searching the web so I started thinking about how I’d go about it. The idea that finally came to me was to do something like this:

  1. Remove the standard MS CRM Export to Excel using the RibbonDiffXml.
  2. Replace the standard MS CRM button with a custom button that looks like the standard Export to Excel button in the RibbonDiffXml.
  3. Create a new security role that will determines if the user should have the Export to Excel functionality.
  4. Create a custom EnableRule in the RibbonDiffXml that will get assigned to the custom button created above that calls a JavaScript function to check if a user has the needed security role to enable the Export to Excel button. If the user has that security role, the Export to Excel button will be enabled, otherwise it will be disabled.

Here are the steps to remove the standard Export to Excel button and replace it with a custom button that can be enabled/disabled based on security roles.

Step 1 – Create the Solution

Create a new solution – you can name it whatever you want. Add the “Application Ribbon” to this solution.

Step 2 – Create the Security Role

Create a new Security Role in MS CRM named “Export to Excel”. Capitalization matters because of the JavaScript involved later. You don’t need to grant any permissions to the role – just the existence of the role will be enough for the JavaScript to function correctly.

Step 3 – Modify the RibbonDiffXml

Modify the RibbonDiffXml so that does the following (these are general steps, specifics follow below):

  1. Hides the existing Export to Excel button
  2. Create a new Export to Excel button with a CommandDefinition and EnableRule that will call JavaScript that will return true or false which will enable or disable the new button.

I don’t want this to become a post on RibbonDiffXml, but I’ve found a dearth of info on how to customize the RibbonDiffXml, so I am going to explain what the RibbonDiffXml does in this particular instance. Below are more detailed steps on how to customize the RibbonDiffXml.

  1. Export the solution created above.
  2. Extract the “customizations.xml” from the exported solution zip file.
  3. Edit the customizations.xml file and replace the entire RibbonDiffXml node and child nodes with the RibbonDiffXml (below).
  4. Update the exported solution zip file with the updated customizations.xml file.
  5. Import the updated solution file back into your CRM org.

Here is the RibbonDiffXml:

<RibbonDiffXml>
    <CustomActions>
      <HideCustomAction Location="Mscrm.HomepageGrid.{!EntityLogicalName}.ExportToExcel" HideActionId="TopLine.HomepageGrid.EntityName.ExportToExcel"></HideCustomAction>
      <CustomAction Id="TopLine.HomepageGrid.MainTab.ExportData.Controls.CustomAction" Location="Mscrm.HomepageGrid.{!EntityLogicalName}.MainTab.ExportData.Controls._children">
        <CommandUIDefinition>
          <Button Id="TopLine.HomepageGrid.{!EntityLogicalName}.ExportToExcel" ToolTipTitle="$Resources:Ribbon.HomepageGrid.Data.Export.ExportToExcel" ToolTipDescription="$Resources:Ribbon.Tooltip.ExportToExcel" Command="TopLine.ExportToExcel.Command" Sequence="35" LabelText="$Resources:Ribbon.HomepageGrid.Data.Export.ExportToExcel" Alt="$Resources:Ribbon.HomepageGrid.Data.Export.ExportToExcel" Image16by16="/_imgs/ribbon/exporttoexcel16.png" Image32by32="/_imgs/ribbon/exporttoexcel32.png" TemplateAlias="o2" />
        </CommandUIDefinition>
      </CustomAction>
      <HideCustomAction Location="Mscrm.SubGrid.{!EntityLogicalName}.ExportToExcel" HideActionId="TopLine.SubGrid.EntityName.ExportToExcel"></HideCustomAction>
      <CustomAction Id="TopLine.SubGrid.MainTab.ExportData.Controls.CustomAction" Location="Mscrm.SubGrid.{!EntityLogicalName}.MainTab.ExportData.Controls._children">
        <CommandUIDefinition>
          <Button Id="TopLine.HomepageGrid.{!EntityLogicalName}.ExportToExcel" ToolTipTitle="$Resources:Ribbon.HomepageGrid.Data.Export.ExportToExcel" ToolTipDescription="$Resources:Ribbon.Tooltip.ExportToExcel" Command="TopLine.ExportToExcel.Command" Sequence="21" LabelText="$Resources:Ribbon.HomepageGrid.Data.Export.ExportToExcel" Alt="$Resources:Ribbon.HomepageGrid.Data.Export.ExportToExcel" Image16by16="/_imgs/ribbon/exporttoexcel16.png" Image32by32="/_imgs/ribbon/exporttoexcel32.png" TemplateAlias="o1" />
        </CommandUIDefinition>
      </CustomAction>
    </CustomActions>
    <Templates>
      <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
    </Templates>
    <CommandDefinitions>
      <CommandDefinition Id="TopLine.ExportToExcel.Command">
        <EnableRules>
          <EnableRule Id="TopLine.ExportToExcel.Rule" />
        </EnableRules>
        <DisplayRules />
        <Actions>
          <JavaScriptFunction FunctionName="Mscrm.GridRibbonActions.exportToExcel" Library="/_static/_common/scripts/RibbonActions.js">
            <CrmParameter Value="SelectedControl" />
            <CrmParameter Value="SelectedEntityTypeCode" />
          </JavaScriptFunction>
        </Actions>
      </CommandDefinition>
    </CommandDefinitions>
    <RuleDefinitions>
      <TabDisplayRules />
      <DisplayRules />
      <EnableRules>
        <EnableRule Id="TopLine.ExportToExcel.Rule">
          <CustomRule FunctionName="CheckExportToExcelRole" Library="$Webresource:new_/js/DisableExportToExcel.js" Default="true" />
        </EnableRule>
      </EnableRules>
    </RuleDefinitions>
    <LocLabels />
  </RibbonDiffXml>

Here’s an image that has some numbered items that correspond to the numbers below with explanations of what the various components of the RibbonDiffXml does (click for a larger, more readable view): RibbonDiffXml

1. Hide Custom Action – This is what hides the standard Export to Excel button when on the “HomepageGrid”. The {!EntityLogicalName} represents any entity. Below is a screen shot of the account HomepageGrid.

HomepageGrid

2. CustomAction – This is where the custom action (i.e., customization to the ribbon) for the HomepageGrid is defined.

3. CustomAction ID – This is a unique name for the customization we are making.

4. CustomAction Location – This defines where our button should be placed. The location tells it to appear on the Main tab for the entity, within the Export Data group, the controls within that group and it’s a part of the children. So, on the account list view, this would be the “Accounts” tab, the “data” group, and it’s one of the child controls.

5. CommandUI Definition – This is the section that defines the button we are placing in our CustomAction location.

6. Button definition ID – This is the name of the custom button.

7. Command – This is the name of the command that is associated with this button. The name here is important because it means there must be a CommandDefinition with the same name. The arrow in the above image of the RibbonDiffXml shows the two parts that are linked.

8. Sequence – This defines where within all the buttons our button should be placed.

Numbers 9 – 16 are essentially dupes of 1 – 8, but for the SubGrid as opposed to the HomepageGrid. A good way to think of the HomepageGrid is the list of items you see (such as accounts, contacts or opportunities) when in the main CRM window. The SubGrid is a list of items for a single record. So if you open the account form, you’ll see a list of contacts (a subgrid) and a list of opportunities (another subgrid).

9. Hide Custom Action – This is what hides the standard Export to Excel button when on the “SubGrid”. A SubGrid is a list of items when viewing the many items in a 1:N relationship. An example is the list of contacts when viewing an account detail page. The {!EntityLogicalName} represents any entity. Below is a screen shot of the contacts SubGrid on the account form.

SubGrid

10. CustomAction – This is where the custom action (i.e., customization to the ribbon) for the SubGrid is defined.

11. CustomAction ID – This is a unique name for the customization we are making.

12. CustomAction Location – This defines where our button should be placed. The location tells it to appear on the List Tools tab for the entity that makes up the N side in the 1:N relationship. (In the subgrid screen shot above, it’s the tab that’s currently selected). It specifies to show the within the Export Data group (labelled on the screen as Data), the controls within that group and it’s a part of the children.

13. CommandUI Definition – This is the section that defines the button we are placing in our CustomAction location.

14. Button definition ID – This is the name of the custom button.

15. Command – This is the name of the command that is associated with this button. The name here is important because it means there must be a CommandDefinition with the same name. The arrow in the above image of the RibbonDiffXml shows the two parts that are linked.

16. Sequence – This defines where within all the buttons our button should be placed.

17. This is the actual definition of the command (i.e., action) that should be associated with the button. Note the relationship between the ID and the Button definitions in the above image of the RibbonDiffXml.

18. This is the name of the rule that will be used to enable and disable the Export to Excel button. The arrow in the image shows that the name must be the same as the EnableRule in the RuleDefinitions section.

19. This is the name of the JavaScript function in the specified library that should be executed when the button is pressed. In this case we are specifying the exact same function that the standard button calls. Note that per this article (see the Ribbon Customization heading) this does not appear to be supported as this customization reuses the JavaScript functions defined in the ribbon commands, so two things to note: 1) unsupported doesn’t mean it won’t work, it’s just not supported, and 2) use this customization at your own risk. Also, you might want to keep this in mind during upgrades or when appying update rollups, as that’s the most likely time this might break.

20. This is the custom javascript rule. The FunctionName parameter is the javascript function name and the library is the name of the web resource to be called (created in the next step).

Step 4 – Create the JavaScript web resource

Create a new web resource named “new_/js/DisableExportToExcel.js”. The code should be as follows:

function UserHasRole(roleName) {
    var serverUrl = Xrm.Page.context.getServerUrl();
    var oDataEndpointUrl = serverUrl + "/XRMServices/2011/OrganizationData.svc/";
    oDataEndpointUrl += "RoleSet?$top=1&$filter=Name eq '" + roleName + "'";
    var service = GetRequestObject();
    if (service != null) {
        service.open("GET", oDataEndpointUrl, false);
        service.setRequestHeader("X-Requested-Width", "XMLHttpRequest");
        service.setRequestHeader("Accept", "application/json, text/javascript, */*");
        service.send(null);
        var requestResults = eval('(' + service.responseText + ')').d;
        if (requestResults != null && requestResults.results.length == 1) {
            var role = requestResults.results[0];
            var id = role.RoleId;
            var currentUserRoles = Xrm.Page.context.getUserRoles();
            for (var i = 0; i < currentUserRoles.length; i++) {
                var userRole = currentUserRoles[i];
                if (GuidsAreEqual(userRole, id)) {
                    return true;
                }
            }
        }
    }
    return false;
}

function GetRequestObject() {
    if (window.XMLHttpRequest) {
        return new window.XMLHttpRequest;
    }
    else {
        try {
            return new ActiveXObject("MSXML2.XMLHTTP.3.0");
        }
        catch (ex) {
            return null;
        }
    }
}
function GuidsAreEqual(guid1, guid2) {
    var isEqual = false;
    if (guid1 == null || guid2 == null) {
        isEqual = false;
    }
    else {
        isEqual = guid1.replace(/[{}]/g, "").toLowerCase() == guid2.replace(/[{}]/g, "").toLowerCase();
    }

    return isEqual;
}

function CheckExportToExcelRole() {
    if (UserHasRole("Export to Excel")) {
        return true;
    }
    else {
        return false;
    }

}

Step 5 – Set up users and test

To set things up to test you need to do several things.

  • Remove the standard MS CRM “Export to Excel” privilege from ALL roles that you want to replace with the custom security functionality. Do this by opening each role, going to the Business Management tab and unselecting the “Export to Excel” privilege. (If you don’t want to modify the “base” roles, you can always copy them and modify the copy.)
  • Set up a user without the system administrator and system customizer roles – just a regular user with a role such as Salesperson – just make sure the salesperson role has the standard MS CRM Export to Excel privilege removed.
  • Make sure you publish all your changes.
  • Log in as the salesperson user.
  • Navigate to accounts or contacts – if everything is set up correctly then the “Export to Excel” button should be grayed out/disabled.
  • Now, to test that your custom permission is working, log in as a system administrator user and grant the security role “Export to Excel” (created in step 2 above) to your “Salesperson” user and navigate to the accounts or contacts – the export to excel button should be enabled!
  • Now that it’s working, all you need to do is grant the “Export to Excel” role to all other users.

The entire solution available for download here.

Script Table Data with TSQL

April 20th, 2012

As a developer, when working with a database the need to script data often arises, especially if only a subset of the data is changed or added to a table in your Development environment. The new or changed data will need to be added to your Test and/or Production environment at some point or sent to a client. In my case, I was dealing with a number of rule tables in a SQL Server 2005 database that had new records add which needed to be applied to our Test environment.

You can use SQL Server Management Studio to script table data, but its level of granularity and ease of use leaves a lot to be desired. While writing my own scripting logic a while back, I ran across a handy stored procedure called sp_generate_inserts. It did everything I needed and more and it’s very easy to use. I won’t go into much detail here, as the link/site provides good documentation and examples. To script all the records for a given table, you would simply called the stored procedure like this:

-- Script all records from the drugcard rule table
EXEC sp_generate_inserts
        @table_name = 'rule_med_drugcard'

To restrict the data, simply add a filter/from parameter like this:

-- Script all active records from the drugcard rule table
EXEC sp_generate_inserts
        @table_name = 'rule_med_drugcard'
        ,@from = 'FROM dbo.rule_med_drugcard WHERE active = 1'

For a recent project, I needed to script selected records from several rule tables using a common filter and I didn’t want to have to manually repeat the sp_generate_inserts call and parameters over and over. So I used a query similar to the one below to simplify the task.

-- Create a temp table to house table names and date filter
DECLARE @tables     TABLE(Id int IDENTITY(1,1), OwnerName VARCHAR(128), TableName VARCHAR(128), CreateDate DATETIME)

-- Create variables for the sp_generate_inserts stored procedure parameters
DECLARE @owner      VARCHAR(128)
        ,@tableName VARCHAR(128)
        ,@sqlFrom   VARCHAR(MAX)
        ,@createDate   DATETIME
        ,@index     INT
        ,@rows      INT

-- Set defaults
SELECT  @index = 1

-- Populate the temp table with the rule tables to script
-- and the create date filter field
INSERT INTO @Tables(OwnerName, TableName, CreateDate)
    SELECT 'dbo', 'RULE_HDHP_R_DRUGCARD', '03/28/2012' UNION
    SELECT 'dbo', 'RULE_HDHP_R_PREVENTIVEDRUG', '03/28/2012' UNION
    SELECT 'dbo', 'RULE_MED_DRUGCARD', '03/28/2012' UNION
    SELECT 'dbo', 'RULE_MED_FORMULARY', '03/28/2012' UNION
    SELECT 'dbo', 'RULE_MED_R_DRUGCARD', '03/28/2012' UNION
    SELECT 'dbo', 'RULE_MED_R_FORMULARY', '03/28/2012'
SET @rows = @@ROWCOUNT

-- Loop through the temp table and generate a delete statement and
-- data script for each table using the sp_generate_inserts stored
-- procedure
WHILE( @index &lt;= @rows ) BEGIN
    SELECT  @owner = OwnerName
            ,@tableName = TableName
            ,@createDate = CreateDate
            ,@sqlFrom = 'FROM ' + OwnerName + '.' + TableName + ' WHERE CreateDate = ''' + CONVERT(VARCHAR, CreateDate, 101) + ''''
    FROM    @tables WHERE id = @index

    -- Output a delete statement to removed the existing data
    -- allowing the script to be processed again without issues
    PRINT 'DELETE ' + @sqlFrom
    PRINT ''

    -- Script the data for the specified table using the from filter
    EXEC sp_generate_inserts
        @table_name = @tableName
        ,@owner = @owner
        ,@from = @sqlFrom
        ,@ommit_identity = 1    -- disable identity insert
    PRINT '----------------------------------------------------------------------------------------------------------'
    PRINT ''

    SET @index = @index +1
END

The above query generated the output below, that can then be applied to appropriate database in the TEST environment. The query can be easily modified to include additional filters or parameters as needed. If you need additional functionality that’s not part of the sp_generate_inserts stored procedure, it can be modify as well to suit your needs.

-- Outputted script
DELETE FROM dbo.RULE_HDHP_R_DRUGCARD WHERE CreateDate = '03/28/2012'

SET NOCOUNT ON
PRINT 'Inserting values into [RULE_HDHP_R_DRUGCARD]'

INSERT [dbo].[RULE_HDHP_R_DRUGCARD] ([CreateDate],[ModifyDate],[DrugCardId_Allow],[DrugCardId_Prior],[SortOrder],[DateFrom],[DateTo],[Options],[IsGrandfather],[IsActive],[Notes])VALUES('Mar 28 2012 12:00:00:000AM','Mar 28 2012 12:00:00:000AM',5704,5704,1,'
INSERT [dbo].[RULE_HDHP_R_DRUGCARD] ([CreateDate],[ModifyDate],[DrugCardId_Allow],[DrugCardId_Prior],[SortOrder],[DateFrom],[DateTo],[Options],[IsGrandfather],[IsActive],[Notes])VALUES('Mar 28 2012 12:00:00:000AM','Mar 28 2012 12:00:00:000AM',4386,5704,2,'
INSERT [dbo].[RULE_HDHP_R_DRUGCARD] ([CreateDate],[ModifyDate],[DrugCardId_Allow],[DrugCardId_Prior],[SortOrder],[DateFrom],[DateTo],[Options],[IsGrandfather],[IsActive],[Notes])VALUES('Mar 28 2012 12:00:00:000AM','Mar 28 2012 12:00:00:000AM',5704,4386,3,'
INSERT [dbo].[RULE_HDHP_R_DRUGCARD] ([CreateDate],[ModifyDate],[DrugCardId_Allow],[DrugCardId_Prior],[SortOrder],[DateFrom],[DateTo],[Options],[IsGrandfather],[IsActive],[Notes])VALUES('Mar 28 2012 12:00:00:000AM','Mar 28 2012 12:00:00:000AM',2909,5704,3,'

PRINT 'Done'
SET NOCOUNT OFF
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

DELETE FROM dbo.RULE_HDHP_R_PREVENTIVEDRUG WHERE CreateDate = '03/28/2012'

SET NOCOUNT ON
PRINT 'Inserting values into [RULE_HDHP_R_PREVENTIVEDRUG]'

INSERT [dbo].[RULE_HDHP_R_PREVENTIVEDRUG] ([CreateDate],[ModifyDate],[PreventiveDrugId_Allow],[DrugCardId],[PreventiveDrugId_Prior],[SortOrder],[DateFrom],[DateTo],[Options],[IsGrandfather],[IsActive],[Notes])VALUES('Mar 28 2012 12:00:00:000AM','Mar 28 201
INSERT [dbo].[RULE_HDHP_R_PREVENTIVEDRUG] ([CreateDate],[ModifyDate],[PreventiveDrugId_Allow],[DrugCardId],[PreventiveDrugId_Prior],[SortOrder],[DateFrom],[DateTo],[Options],[IsGrandfather],[IsActive],[Notes])VALUES('Mar 28 2012 12:00:00:000AM','Mar 28 201

PRINT 'Done'
SET NOCOUNT OFF

The sp_generate_inserts stored procedure makes it easy to create insert statements from existing data. It supports an array of options and there are versions for SQL 2000 thru SQL 2008. If you need to script data, be sure to give it a try. Using TSQL to dynamically create TSQL statements makes it simple to perform common and repetitive tasks.

Handling and Logging Errors in ASP.NET

April 13th, 2012

At some point in development, we all have seen the infamous server error page.

Server Error

While sometimes annoying, it is still generally helpful if you are debugging. That being said – this page should only be viewable by a developer; errors need to be handled much differently for users. First things first, you should always do your best to manage possible errors by wrapping your code in try-catch blocks. This leaves unexpected errors only – and we will handle them the best we can. Let’s start by redirecting the user to a better looking page. In your web.config file you can add a section titled customErrors – all of the available options are documented in this MSDN article, but we are just going to cover the basics in this post. Here is what to add to our web.config:

<configuration>
	<system.web>
		<customErrors defaultRedirect="~/GenericError.aspx" mode="RemoteOnly"></customErrors>
	</system.web>
</configuration>

I have two attributes set here: defaultRedirect, and mode. defaultRedirect is the page we want to redirect the user to if they have encountered an unexpected error. Mode defines when we want this error handling to take place (more about this in the article above). “RemoteOnly” means that if I am running my site locally I will still see the server error page, but users viewing my site will be redirected to the new error page. This is generally what I have mine set at, however you may want to temporarily change it to “On” for testing purposes. Now if you start debugging and throw an uncaught error – your user will be redirected to GenericError.aspx, and it’s as easy as that! That being said, if you’re like me you might be thinking “well this is great and all, but it would be nice to know what errors I’m not catching…” – and that brings us to our next section – logging the errors.

The Global.asax file is an optional file that contains application-level and session-level event handlers, one of which is Application_Error (which we will get to in a second). If you create a website with Visual Studio it may have already created the Global.asax file for you, but if not you can add it: Solution Explorer > Add New Item > Global Application Class > Add. Here is what the Application_Error block should look like:

void Application_Error(object sender, EventArgs e)
{
	//we are going to store the last exception for later use
	HttpContext.Current.Cache["LastException"] = HttpContext.Current.Server.GetLastError();
}

So now we have stored the error, and the user is redirected to the error page. On Page_Load for GenericError.aspx we have the following:

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		try
		{
			string stacktrace = "";
			string message = "";
			if (((Exception)Cache["LastException"]) != null)
			{
				stacktrace = ((Exception)Cache["LastException"]).StackTrace;
				message = ((Exception)Cache["LastException"]).Message;
			}
			// insert the error info into the database and return errorID
			int errorID = obj.LogError(SessionHandler.SessionVariables(), message, stacktrace);
			ViewState["errorID"] = errorID;
		}
		catch
		{
			// THE ERROR PAGE CAN'T HAVE AN ERROR!!!!!!!!!! ahhhhhhhhhhhhh
			// Seriously though, make sure you use try-catch blocks on this page.
			// if an error occurs on your error page your application will catch the error
			// and then try to redirect again... and again, and again, thus creating an
			// infinite loop.
		}
	}
}

You may have noticed in the comments above that I am not only inserting the error information into the database, but also returning the corresponding errorID. You don’t have to do this, but I would like to give the user the option of submitting additional information about what they might have been doing when the error occurred – so I will need the errorID in order to update the database entry later. Here is what my error page looks like:

GenericError.aspx

The user can enter more information in the textbox if they choose to, and when they click Report we will update the error to include their message.

protected void btnReport_Click(object sender, EventArgs e)
{
	try
	{
		int errorID = (int)ViewState["errorID"];
		obj.UpdateError(errorID, tbUserMessage.Text);
	}
	catch
	{
		// no errors
	}

	//then redirect
	Response.Redirect("Default.aspx");
}

You have successfully logged an error in your database! It’s up to you to decide how you receive this information – you can query the database, or you might choose to have the errors emailed to you as well. Regardless of what you choose, the point is that you are now informed of the errors occurring on your site, and you can promptly address the issues.

Dynamic Pivot Table

April 6th, 2012

On this post, I will talk about how to create a dynamic Pivot Table. As you may know the “Pivot” statement is a data summarization tool that can be used in order to analyze specific information. The Pivot Table is very common on BI Tools such as Microsoft Reporting Services.

The first step would be creating the headers that will be used for our Pivot Table. Because the Pivot Table I am creating is dynamic the columns have to be created first.

In order to do that, I will create a table that will have all the months. Here is the first part of the code:


DECLARE @MonthNames TABLE
( 	 MonthNames  varchar(max)
	,MonthId int
)

Then the table has to be filled with the months, this way:

DECLARE @StartDate DateTime
DECLARE @EndDate 	 DateTime

SET @StartDate = '2010-01-01'
SET @EndDate = '2011-12-31'

-- Inserts into the @MonthNames Table
INSERT INTO @MonthNames
SELECT DISTINCT DATENAME(MONTH,createdate), MONTH(CREATEDATE)
FROM OPPORTUNITY
WHERE CREATEDATE &gt;= @StartDate
AND   CREATEDATE &lt;= @EndDate
AND STATUS ='Approved'

You may be thinking, why not just embed the “selected statement” that fills the @MonthNames table into the “Pivot Statement”?
Well that would return an error because the month function has to be on the “Select statement” and when a select statement is embedded, it can’t return more than 1 column.

Then we can create @PivotHeader variable and fill it with the moths:

--Declare the variable that will hold all the headers. 

DECLARE @PivotHeader AS VARCHAR(max)

SELECT @PivotHeader = COALESCE( @PivotHeader + ',['+ MonthNames + ']' , '[' + MonthNames+ ']' )
FROM @MonthNames
ORDER BY MonthId

This would be the return.

I think we are ready to ensemble the “Pivot” statement.

Since it is a dynamic statement I created a new variable called @PivotSQL, then I set the value to it.


DECLARE @PivotSQL AS VARCHAR(max)

SET @PivotSQL = N'
	SELECT * FROM (
	SELECT o.OPPORTUNITYID as opp,
	Year(CREATEDATE) as [Year],
	DATENAME(month,CREATEDATE) AS [Month]
			FROM OPPORTUNITY  o
			WHERE CREATEDATE&gt;=''' + @StartDate + '
			AND CREATEDATE&lt;=  ''' + @EndDate   + '
			 ) AS PivotData
	PIVOT(COUNT(opp)
			FOR   [Month] IN ('+ @PivotHeader +'
	  ) ) AS PivotTable'

--Finally we will call the execute.

Execute (@PivotSQL)

Here is an example of what the Pivot could return. As you can see there is a row for every Year and Moth totals are listed as columns.

You will able to find more information at Micorost.com

Sorting a Listbox with jQuery

March 23rd, 2012

One of the web pages for a recent project required the use of several listboxes and I found myself needing a way to quickly sort a given listbox due to the large number of options in some cases. Changing the underlying data source would certainly work for say, ascending order (and I did do that), but what if you wanted to toggle between ascending and descending? I could used Ajax, but since the sorting was only to aid selecting an item, I decided to just use jQuery client-side.

The following is an example of a listbox with several options and a button to perform the sort.

&lt;select size=&quot;4&quot; name=&quot;lbCarrier&quot; id=&quot;lbCarrier&quot; class=&quot;listbox&quot;&gt;
    &lt;option value=&quot;0&quot;&gt;None&lt;/option&gt;
    &lt;option value=&quot;8&quot;&gt;Principal&lt;/option&gt;
    &lt;option value=&quot;14&quot;&gt;Other&lt;/option&gt;
    &lt;option value=&quot;3&quot;&gt;Cigna&lt;/option&gt;
    &lt;option value=&quot;2&quot;&gt;Blue Cross&lt;/option&gt;
    &lt;option value=&quot;11&quot;&gt;Trustmark&lt;/option&gt;
    &lt;option value=&quot;10&quot;&gt;Starmark&lt;/option&gt;
    &lt;option value=&quot;1&quot;&gt;Atena&lt;/option&gt;
    &lt;option value=&quot;5&quot;&gt;Humana&lt;/option&gt;
&lt;/select&gt;

&lt;input type=&quot;submit&quot; id=&quot;btnSortItems&quot; value=&quot;Sort Items&quot; /&gt;

Using jQuery I simply wired up a handler for the SortItems button click event and added a Sort function to do the work.

$(function() {
    $(&quot;#btnSortItems&quot;).click(function(e) {
        e.preventDefault();
        Sort(&quot;lbCarrier&quot;);
    });
});

function Sort(elementId) {
    // Convert the listbox options to a javascript array and sort (ascending)
    var sortedList = $.makeArray($(&quot;#&quot; + elementId + &quot; option&quot;))
        .sort(function(a, b) {
            return $(a).text() &lt; $(b).text() ? -1 : 1;
        });
    // Clear the options and add the sorted ones
    $(&quot;#&quot; + elementId).empty().html(sortedList);
}

To sort descending, you simply reverse the operator in the comparison as shown below. You can also sort the listbox by the option value using $(a).val() > $(b).val() but that’s not as meaningful since those values aren’t visible and if the values are actually numbers, as in my case, they won’t sort properly because they are treated as strings.

// Descending sort
return $(a).text() &gt; $(b).text() ? -1 : 1;

Here’s the carrier listbox with the default, ascending and descending sorts.

Once the sorting is complete, you may want to select an option based on it’s Text, Value or Index. Here are a few of the many different ways to select an option.

// Select by Text
$(&quot;#lbCarrier&quot;).find(&quot;option:contains('Other')&quot;).attr(&quot;selected&quot;, &quot;selected&quot;)

// Select by Value
$(&quot;#lbCarrier option[value='8']&quot;).attr(&quot;selected&quot;, &quot;selected&quot;)

// Select by Index
$(&quot;#lbCarrier option&quot;).eq(3).attr(&quot;selected&quot;, &quot;selected&quot;)

// Select the first option
$(&quot;#lbCarrier option:first&quot;).attr(&quot;selected&quot;, &quot;selected&quot;)

For my actual implementation, I wired up the listbox dblclick event to so that I could easily toggle between ascending and descending sorts instead of using a button, but the process is basically the same and it works for the standard select list/combobox as well.

jQuery makes it easy to add functionality or quickly try things out, it reduces the amount of code you would typically have to write and makes working with JavaScript something to look forward to instead of dreading.

Handling Session Timeout without Using Cookies

March 9th, 2012

Recently I’ve been working on the early stages of development for a website. While testing, the session state timeout was beginning to be quite a pain. Sure I can extend it, but if I walk away from my computer long enough for a timeout, I come back expecting to be able to use the site and am unpleasantly surprised when I realize my session has been lost. I needed to come up with a more graceful way to handle this situation.

My first decision was – why not just keep the session open indefinitely?  I’ll refresh the session every few minutes; this way if the user is logged in, they could walk away for as long as they want, come back, and be able to use the site as expected. If the browser is closed, the session will be unable to refresh and the timeout will take care of ending the session. To implement, I followed this example by Malcom Sheridon.

This method works, however, I just couldn’t get past the security hole that will exist if the session is constantly open. What if the user is on a public computer and leaves the browser open? Anyone could sit down at that computer and have complete access to the user’s account. This method wasn’t going to cut it; I had to end the session somehow. This is when I decided that a session timeout warning (popup) was going to be needed.

The alert would be needed on all pages, so I incorporated it into a user control on my master page. Here is what the front end looks like, where “divTimeoutPopup” is the semi-transparent background, and “innerPopup” is the alert itself:

&lt;div id=&quot;divTimeoutPopup&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;innerPopup&quot;&gt;
    &lt;div id=&quot;divTimeLeft&quot;&gt;&lt;/div&gt;
    &lt;br /&gt;
    &lt;asp:Button ID=&quot;btnOK&quot; runat=&quot;server&quot; Text=&quot;OK&quot; OnClick=&quot;btnOK_Click&quot; /&gt;
    &lt;asp:Button ID=&quot;btnLogout&quot; runat=&quot;server&quot; Text=&quot;Logout&quot; OnClick=&quot;btnLogout_Click&quot; /&gt;
&lt;/div&gt;

On Page_Load on my master page, I had the following:

//  we only want the popup to show if the user is logged in
If (UserLoggedIn)
{
	//  this line is taken from the blog mentioned above
	//  when a postback occurs the session will be refreshed
	//  but not if the user clicks the back/forward buttons
	//  in their browser, so we will call this just to make
	//  sure the session gets refreshed
	ScriptManager.RegisterStartupScript(Page, Page.GetType(), &quot;keepAlive&quot;, &quot;KeepSessionAlive('&quot; + ResolveUrl(&quot;~/KeepSessionAlive.ashx&quot;) + &quot;');&quot;, true);

	//  this line will show the timeout message after a
	//  given time (I have it set to 5 seconds for testing
	//  purposes
	ScriptManager.RegisterStartupScript(Page, Page.GetType(), &quot;initiateTimeout&quot;, &quot;setTimeout(function(){showTimeoutMessage('&quot; + ResolveUrl(&quot;~/Logout.aspx?msg=sessiontimeout&quot;) + &quot;')}, 5000);&quot;, true);
}

I should note that I use subdirectories in my site so I use ResolveUrl to help with the issue of absolute/relative paths within a master page. If you don’t use subdirectories you don’t need to pass in the URLs that you need and can put them directly into your javascript. The first function KeepSessionAlive is in my master page (or linked .js file) and looks like this:

function KeepSessionAlive(pageURL) {
    $.post(pageURL, null, function () { });
}

If you didn’t get a chance to look at the blog post mentioned above, the code for KeepSessionAlive.ashx is shown below. Basically all this does is set a session value, which refreshes the current session and keeps it active.

&lt;%@ WebHandler Language=&quot;C#&quot; Class=&quot;KeepSessionAlive&quot; %&gt;

using System;
using System.Web;
using System.Web.SessionState;

public class KeepSessionAlive : IHttpHandler, IRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        context.Session[&quot;KeepSessionAlive&quot;] = DateTime.Now;
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Next showTimeoutMessage is the code that actually displays the popup, this I have in my user control

function showTimeoutMessage(logoutURL) {
		//  show the popup
        $('#divTimeoutPopup').css('display', 'block');
        $('#innerPopup').css('display', 'block');
        $('#divTimeoutPopup').css('opacity', '0.5');

		//  second value at which our countdown begins
        var secondsLeft = 3;
        UpdateTime();
        setInterval(UpdateTime, 1000); //every second update the seconds left

        function UpdateTime() {
            if (secondsLeft &gt; 0) {
                $('#divTimeLeft').text('You have ' + secondsLeft + ' seconds before your session expires! Click OK to continue your session.');
                secondsLeft--;
            } else if (secondsLeft == 0) {
                window.location.href = logoutURL; // if time has run out, redirect the user to the logout page
            }

        }
    }

OK, so now we have everything in place to actually display the popup, now all we need to do is write the code for the buttons in the popup. Here is the code behind for my user control:

protected void btnLogout_Click(object sender, EventArgs e)
    {
        Response.Redirect(&quot;~/Logout.aspx&quot;);
    }

    protected void btnOK_Click(object sender, EventArgs e)
    {
        ScriptManager.RegisterStartupScript(Page, Page.GetType(), &quot;refreshSession&quot;, &quot;RefreshSession('&quot; + ResolveUrl(&quot;~/KeepSessionAlive.ashx&quot;) + &quot;', '&quot; + ResolveUrl(&quot;~/Logout.aspx?msg=sessiontimeout&quot;) + &quot;');&quot;, true);
    }

As you can see the logout button simply redirects the user to the logout page. The OK button will refresh the user’s session. Here is the javascript for RefreshSession() which I have located in my user control.

	function RefreshSession(pageURL, logoutURL) {
		// refresh the session
        $.post(pageURL, null, function () { });

		//  hide the popup divs
        $('#divTimeoutPopup').css('display', 'none');
        $('#innerPopup').css('display', 'none');

		//  call this again so that the popup can display again if needed
        setTimeout(function () { showTimeoutMessage(logoutURL) }, 5000)
    }

The finished product:

Session Timeout Popup

It may seem like a lot, but it is actually pretty simple. Keep in mind I have my session set to timeout after 1 minute and my popup to display after 5 seconds – this is merely for testing purposes as it would be a nuisance to have a session popup so often! I suggest keeping the popup display time as close to your session timeout as possible to minimize unnecessary posts. You will have to decide on a value for your session timeout (in web.config) – this is important to know because this will be the minimum amount of time it will take to end the session if the browser has been closed.

CRM 2011: Custom Reports & the rsProcessingAborted Error

February 24th, 2012

I created a custom report for CRM 2011 in BIDS months ago for a client, but was recently notified that it had stopped working after some significant changes were made to their environment. Out-of-the-box reports would display just fine, but the one I wrote displayed a vague “The report cannot be displayed. (rsProcessingAborted)” message and there was nothing useful in the Event Viewer to shed more light on the problem.

After confirming that the data source and credentials my report was using were still valid, I started poking around and discovered that other custom reports, even those created through the CRM 2011 Report Wizard, were failing with the same error. I tried a few solutions that I found online, but the one that fixed it is covered in When CRM 2011 Custom Reports Don’t Work.

Turns out all I needed to do was look up which account was being used for the CRM Application account by checking the CRMAppPool in IIS, then add an SPN for it with “setspn –a HTTP/NETBIOSNAME DOMAINSERVICEACCOUNT”. Such a quick and easy solution for something I was beginning to worry would end up being a real headache to fix!