Tag Archives: CRM

Virtual Convergence 2013

Two days ago, the Convergence team announced Virtual Convergence 2013:

https://twitter.com/MSFTConvergence/status/310740576477782016

If you aren’t familiar with Convergence, it is similar to the likes of past technical conferences like PDC/MIX or current ones like TechEd and //build/, but exclusively focused on the Dynamics business at Microsoft.  I am delighted to see that people who couldn’t attend the conference in person can get access to the content for FREE.  Mark your calendars and follow the link to register.

@devkeydet

Calling CRM from ASP.NET using impersonation to ActOnBehalfOf the logged in user

UPDATE: Please review the comments for this post.  There is a better, easier way to do this using OAuth that works with both the SOAP and REST/OData service.

Sometimes you need to run ASP.NET code outside of Dynamics CRM to achieve your goals.  This usually manifests itself either as a page embedded in CRMs main content area which is accessible via a link in the sitemap similar to the following:

 

image

Another place this is often used is embedding external content through an IFrame in a CRM form.  The general approach is covered in the SDK:

Implement Single Sign-on from an ASPX Webpage or IFRAME

Walkthrough: Single Sign-on from a Custom Web Page

Of course, your code will usually need to call back into Dynamics CRM through the organization (web) service to do things like CRUD on CRM data, etc.  In this scenario, you want CRM to execute code under the context of the logged in user.  The CRM SDK covers how to do this here:

Impersonate Another User

Sample: Impersonate Using the ActOnBehalfOf Privilege

See my CRM Online & Windows Azure Series post for a walkthrough of the Single Sign On (SSO) configuration.  The goal of this post is to bring all of these concepts together in as simple of a “hello world” style code sample as possible.  The sample code is actually the code for the embedded page in the screenshot above (called ActOnBehalfOf.aspx).  The solution is made up of an ASP.NET web form, some code behind the web form, and a helper class I built.  In order to get this code to compile, you are going to have to add the necessary .NET assembly references and fix some of the namespaces.  I’ll leave that exercise to you.

ActOnBehalfOf.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ActOnBehalfOf.aspx.cs" Inherits="AdfsEnabledReportViewerWebRole.ActOnBehalfOf" %>

 

<html>

    <head runat="server">

        <title></title>

    </head>

    <body>

        <form id="form1" runat="server">

            <div>

                <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">

                    <Columns>

                        <asp:BoundField DataField="Id" HeaderText="Id" />

                        <asp:BoundField DataField="Name" HeaderText="Name" />

                    </Columns>

                </asp:GridView>

            </div>

        </form>

    </body>

</html>

ActOnBehalfOf.aspx.cs:

 1: using System;

 2: using System.Linq;

 3: using Microsoft.Xrm.Client;

 4: using Microsoft.Xrm.Sdk.Client;

 5:  

 6: namespace AdfsEnabledReportViewerWebRole

 7: {

 8:     public partial class ActOnBehalfOf : System.Web.UI.Page

 9:     {

 10:         protected void Page_Load(object sender, EventArgs e)

 11:         {

 12:             var contextConnection = ActOnBehalfOfHelper.CreateContextAndConnection();

 13:             CrmConnection conn = contextConnection.Connection;

 14:             OrganizationServiceContext ctx = contextConnection.Context;

 15:  

 16:             // CallierId is what forces CRM to execute the API calls within the security context of the CRM User

 17:             conn.CallerId = ActOnBehalfOfHelper.GetCallerId();

 18:  

 19:             var accountQuery = from a in ctx.CreateQuery<Account>()

 20:                         select new Account

 21:                         {

 22:                             Id = a.Id,

 23:                             Name = a.Name

 24:                         };

 25:  

 26:             var accounts = accountQuery.ToList();

 27:  

 28:             GridView1.DataSource = accounts;

 29:             GridView1.DataBind();

 30:         }

 31:     }

 32: }

If you’ve reviewed the resources in this post, then ActOnBehalfOf.aspx and ActOnBehalfOf.aspx.cs should be pretty self explanatory.  It’s a page with a GridView.  The code behind queries CRM for data using the organization service.  Note that Account from line 19 comes from a class file where I used crmsvcutil.exe to generate the class.  I always use Erik Pool’s approach to only generate classes I need in my code.  I digress.  The code sets the CallerId property of the CrmConnection object instance before executing the code.  By doing this, CRM will execute all calls made to through the OrganizationServiceContext instance as the CRM user based on the CallerId value passed in.  CallerId is the GUID of the CRM user who needs to be impersonated.  The ActOnBehalfOfHelper does the real work to get the proper GUID based on the claims available to the ASP.NET page.  Specifically, it uses the UPN claim value to find the CRM user.  Once the CRM user is found, the code returns the Id of the CRM user as a GUID. 

ActOnBehalfOfHelper.cs:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using Microsoft.IdentityModel.Claims;

using Microsoft.Xrm.Client;

using Microsoft.Xrm.Client.Services;

using Microsoft.Xrm.Sdk.Client;

 

namespace AdfsEnabledReportViewerWebRole

{

    public static class ActOnBehalfOfHelper

    {

 

// ReSharper disable InconsistentNaming

        private const string CRM_CALLERID = "CRM_CALLERID";

// ReSharper restore InconsistentNaming

 

        public static ContextConnection CreateContextAndConnection()

        {

            var contextConnection = new ContextConnection();

            // Connect to CRM with a single named user (i.e. system account / trusted subsystem model) who has the ActOnBehalfOf privelege

            contextConnection.Connection =

                CrmConnection.Parse("Url=[YOUR_ORG_URL];Username=[YOUR_USERNAME];Password=[YOUR_PASSWORD]");

            contextConnection.Context =

                new OrganizationServiceContext(new OrganizationService(contextConnection.Connection));

 

            return contextConnection;

        }

 

        public static Guid GetCallerId()

        {

            Guid callerId;

            var contextConnection = CreateContextAndConnection();

            var ctx = contextConnection.Context;

 

            // NOTE: I am caching the CallerId to minimize calls to the CRM Organization Service.

            // For production code, you should not store the CallerId in plain text in a cookie.

            // Malicious code, once authenticated, can change the cookie value and execute as another caller.

            // You could apply encryption when creating the cookie and decryption when reading 

            // the cookie value:

            // http://msdn.microsoft.com/en-us/library/windowsazure/hh697511.aspx

            // You could even encrypt/decrypt the cookie name to obfuscate the purpose of the cookie.

            // Alternatively, find a different approach to cache the CallerId value (ASP.NET Session for example)

            // or simply don't cache the CallerId.

 

            HttpCookie callerIdCookie = HttpContext.Current.Request.Cookies[CRM_CALLERID];

 

            // If the cookie exists, reuse the Guid string value to execute the call as the current user

            // If not, then query CRM to get the Guid of the authenticated user based on the upn claim

            if (callerIdCookie == null)

            {

                ClaimCollection claims = ((IClaimsIdentity) HttpContext.Current.User.Identity).Claims;

 

                IEnumerable<Claim> claimQuery = from c in claims

                                                where

                                                    c.ClaimType ==

                                                    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"

                                                select c;

 

                Claim upnClaim = claimQuery.FirstOrDefault();

 

                var userQuery = from user in ctx.CreateQuery<SystemUser>()

                                where user.DomainName == upnClaim.Value

                                select user.SystemUserId.Value;

 

                callerId = userQuery.FirstOrDefault();

                if (callerId == Guid.Empty)

                {

                    // Send HTTP status code of 403

                    // See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

                    HttpContext.Current.Response.StatusCode = 403;

                    HttpContext.Current.Response.End();

                }

 

                string callerIdString = callerId.ToString();

                HttpContext.Current.Response.Cookies.Add(new HttpCookie(CRM_CALLERID, callerIdString));

            }

            else

            {

                callerId = new Guid(callerIdCookie.Value);

            }

 

            return callerId;

        }

    }

 

    public class ContextConnection

    {

        public CrmConnection Connection { get; set; }

 

        public OrganizationServiceContext Context { get; set; }

    }

}

Note the comments in the code.  I am doing some caching of the user GUID in a cookie.  Right now, the cookie and cookie value is in plain text.  As I state, this is done for simplicity of the sample.  Make sure you read the comments and make the proper adjustments to protect access to the CallerID GUID from malicious code/callers.

@devkeydet

A primer in Extended CRM Development

UPDATE (06MAY2013): If you like the idea of implementing more functionality using point and click configuration (like in Part II) without having to write code (like in Part III), then you might want to have a look at North 52 Formula Manager.  I have a blog post about it here.  Like I say in the post, I get nothing from the makers of the addon by making you aware of it.  However, I am a big fan of it.  I think it’s a significant time saver enabling you to quickly implement functionality that would otherwise require code.  You can decide for yourself after you have evaluated it.

Dynamics CRM 2011 offers a powerful platform for building line of business applications.  These applications are often referred to as Extended CRM applications or XRM Framework applications because they leverage Dynamics CRM 2011 capabilities beyond the typical customer relationship management (CRM) scenarios.  Since I work with US Government, Healthcare, and Education customers and partners, I spend the majority of my time focused on XRM.  I’ve used My “crash course” in Dynamics CRM 2011 Development post as starter resource for folks who want to learn more.  However, sometimes I get the following feedback: “That’s great, but can you give me the short version?  I want the primer, not the deep dive.”  This post is my attempt to do just that.  To learn the fundamentals of XRM, do the following:

  • Watch my XRM in a Nutshell series:
    • Part I: Defining the Data-centric Business Application built using Dynamics CRM (22:16)
    • Part II: Building the Data-centric Business Application using Dynamics CRM (53:24)
    • Part III: Extending the Data-centric Business Application with the Dynamics CRM SDK (51:20)

After watching these videos, the next step would be to read the Building Business Applications with Microsoft Dynamics CRM 2011 whitepaper.  Next, you can dig deeper using my My “crash course” in Dynamics CRM 2011 Development post. 

In Part II, I show modifying the Site Map and Ribbon using my favorite community tools:

http://dkdt.me/TxS9XB

In Part III, I talk about the integrated external wed application with SSO scenario.  However, I don’t show a demo of it.  I cover examples of this scenario as part of my CRM Online & Windows Azure Series.   Specifically in the Configuring Single Sign On (SSO) and Calling CRM from ASP.NET using impersonation to ActOnBehalfOf the logged in user posts.

Finally, if you like these kinds of videos, then you might like How to Customize Microsoft Dynamics CRM 2011 Online which is in a similar vein as my videos.  There’s a little bit of overlap, but I think you’ll still find the content useful. 

@devkeydet

WEBCAST FOLLOW UP: Unit Testing Dynamics CRM Plugin and JavaScript code

Thanks to everyone who attended my presentation on Unit Testing Dynamics CRM Plugin and JavaScript code hosted by http://www.xrmvirtual.com/.  I’ll update this post when the recording is available.  However, the webcast was basically a shortened walkthrough of these two posts:

http://blogs.msdn.com/b/devkeydet/archive/2012/10/31/how-i-develop-and-unit-test-crm-2011-plugins.aspx

http://blogs.msdn.com/b/devkeydet/archive/2012/11/27/unit-testing-crm-2011-javascript-web-resources.aspx

…with some additional “what is and why unit testing” commentary.

Here’s the presentation:

@devkeydet

The new bulk API in Polaris/UR12

If you haven’t heard, CRM Online has completed the Polaris rollout and UR12 is available.  With it comes a new bulk API which makes programmatic data import into Dynamics CRM 2011 faster.  Check out Sonoma Partners blog post on a simple benchmark of the improvements.  As you can see, it’s really worth moving to this new API.  What?  You don’t do data migration in code:)?  You use a third party tool like the CozyRoc and KingswaySoft SSIS adapters or Scribe?  The good news is that both CozyRoc and KingswaySoft have already updated their adapters to support this new API.  I’ve also updated my Improving the performance of multiple Inserts, Updates, and Deletes via the CRM 2011 Organization Service post to suggest how one can combine bulk with parallelism to achieve even better performance.  In fact, if you review my Data Migration with CRM 2011 post, you’ll recognize that the Balanced Data Distributor (BDD) is an addon to SQL SSIS which enables parallelism within your SSIS package.

@devkeydet

CRM, Accessibility, and 508

I’ve spent many a time having accessibility and 508 compliance discussions with customers and partners over the years working at Microsoft and focusing on various web based technologies.  In fact, it’s hard to believe I recorded this video over seven years ago:

https://channel9.msdn.com/Blogs/keydet/Creating-an-Accessible-Web-Page

As it relates to Dynamics CRM 2011, whether your overall implementation is accessible is a combination of:

  1. Whether the HTML the CRM product produces is accessible
  2. Whether your web resources are accessible
  3. Whether any custom ASP.NET, etc. pages integrated into the CRM UI (say through iFrames) are accessible

For #1, the CRM product team produces a document called a Voluntary Product Accessibility Template (VPAT).  Ben does a good job explaining the relationship to 508 compliance and VPATs here so I won’t go into it.  As Ben mentions, all of Microsoft’s VPATs are available at:

http://www.microsoft.com/government/en-us/products/section508/Pages/default.aspx

The most recent VPAT for Dynamics CRM 2011 (as of writing this) is: Microsoft Dynamics CRM December 2012 Service Update VPAT.  For #2, the CRM 2011 SDK has a section called Create Accessible Web Resources to help you.  This section is chock full of resources and links to external resources on accessibility.  What surprised me is that it doesn’t talk about Visual Studio’s built in accessibility checker which is covered in Visual Studio’s Accessibility in Visual Studio and ASP.NET documentation.  For #3, you need to similarly understand how to make sure those pages are accessible.  Visual Studio’s ASP.NET Controls and Accessibility should prove helpful.  In fact, the 7+ year old video I recorded that I link to earlier shows both the accessibility checker and general ASP.NET accessibility techniques in action.  Hopefully this post is helpful to those of you who are going to be in the business of ensuring your CRM implementation is accessible.  Here are a few other resources you will want to review which are not linked from the other resources in this post as far as I can tell:

http://webaim.org/

http://www.section508.gov/

Now that the latest CRM Online update is complete and UR12 is out, Dynamics CRM 2011 is now cross browser capable.  Therefore, you can now use the popular WAVE Toolbar Firefox addon to check accessibility of your customizations.  Yea, I know, a Microsoft guy recommending Firefox.  I don’t know of a better free tool that allows you to check accessibility of web pages without uploading html or point the tools servers at your web site.  The latter kind of validator won’t work for CRM deployments because of authentication.  Plus, you should be testing all your customizations in Firefox and Chrome anyway.

@devkeydet

CRM Online & Windows Azure Series

I’ve been working with a few CRM Online customers on some advanced scenarios which require using Windows Azure as part of their overall solution.  As a result, I will be publishing series of blog posts and samples which walk you through the following:

As I get the rest of the posts published, I will update links in this post.  This series is a complement to my Design for Online and CRM Online + Windows Azure = Many Possibilities posts.

@devkeydet

CRM Online & Windows Azure: Configuring Single Sign On (SSO)

NOTE: While these instructions may work, they are fairly old.  If you are using Visual Studio 2013, there’s a much quicker way to accomplish the same end goal.  See: http://azure.microsoft.com/en-us/documentation/videos/azure-identity-application-to-authenticate/

In this video, I walk you through how to configure Single Sign On (SSO) across CRM Online and Windows Azure using Visual Studio 2010.  My walkthrough is based on the Web Single Sign-On with .NET and Windows Azure Active Directory walkthrough, but adds the nuances of getting this working with Windows Azure & Visual Studio 2010.  If you are using Visual Studio 2012, follow the instructions in the link.  If you are using Windows Azure Web Sites instead of a Web Role, then there is an even easier way to configure SSO: http://www.asp.net/vnext/overview/latest/windows-azure-authentication.

In the video, I use Fiddler to redirect https traffic to the Windows Azure Compute Emulator.  You can download it here:

http://www.fiddler2.com

@devkeydet

Improving perceived performance in a CRM form

In general web development, there are useful patterns to improving the perceived performance in a web page.  A great example is this blog post:

http://blog.michaelckennedy.net/2012/11/13/improve-perceived-performance-of-asp-net-mvc-websites-with-async-partialviews/

Once you understand the core concepts of the post (even tough it’s about ASP.NET MVC), you should be able apply similar patterns with CRM Web Resources.  You can achieve the same thing by breaking up your UI pieces into separate web resources and laying them out on the form appropriately.  Or you can have separate div tags in your web resource for each thing that needs to be loaded discretely.  Often times, in CRM, people want to have the similar spinning loading indicator while the initialization of the web resource is happening.  I’ve put together a little sample of how I do it.  I’ve packaged it up as a managed solution you can download and review:

http://sdrv.ms/14dZNvi

Here’s a quick video of it in action:

All I did was add the dkdt_/simulateslowload.htm web resource to the form.

Basically, what happens is when the web resource loads it has the real content hidden.  I simulate an asynchronous web service call using setTimeout with a delay of 3 seconds.  The idea is that the real content gets fully generated after the web service call is complete.  Therefore, a delay.  While we’re waiting, I show a loading animation via an overlaid IFrame.  Once the 3 seconds are over, I hide the animation and show the content.

@devkeydet