Tag Archives: CRM

Eliminating manual device registration

If you are connecting to CRM Online with a Live ID, you need to register a Device ID and Device Password when using the organization service from an outside caller.  I walk through this in my Connecting to CRM Online from an outside caller post.  Thanks to some internal discussions with folks on the SDK team, I discovered a simpler way that doesn’t require manually registering and putting the DeviceId /DevicePassword in the config file.  First you’ll have to download and use the Helper Code: DeviceIdManager Class from the SDK.  In addition to the references from the previous post, you’ll need to add references to System.Security.dll and System.ServiceModel.dll to get it to build.  Once you have done that, you can eliminate the DeviceId and DevicePassword settings from the connection string:

<connectionStrings>

    <add name="CrmOnline" connectionString="Url=[YOUR_CRM_ORG_URL]; Username=[YOUR_LIVE_ID]; Password=[YOUR_PASSWORD];"/>

    </connectionStrings>

You can now connect to CRM Online like so:

var connection = new CrmConnection("CrmOnline");

connection.DeviceCredentials = DeviceIdManager.LoadOrRegisterDevice();

var context = new XrmServiceContext(connection);

DeviceIdManager.LoadOrRegisterDevice() automates the manual steps from the Connecting to CRM Online from an outside caller post.  I’m pretty sure it requires full trust, but haven’t taken the time to confirm.  If you have, leave me a comment to confirm/correct.

@devkeydet

Geocoding and displaying a map for an address in CRM 2011

There are a few blog posts out there that cover this topic, but the ones I’ve found don’t take you through it step by step.  I will, however, make assumptions like you know how to create an entity, add web resources to a form, etc.  Here goes…

The overall solution is going to consist of:

If you are a “yea yea, blog blah blah, just give me the code” type then jump to the bottom of the postSmile.

Let’s get started.  First, you will need the Developer Toolkit for Microsoft Dynamics CRM 2011 and Microsoft Dynamics CRM Online and NuGet installed.  Let’s create an entity called GeocodeMapSample. As a habit, I tend to uncheck all the entity defaults after the Options for Entity section of the form.  You can always go back and turn additional features on as you need them after you create the entity.

image

Next create the following fields (all of type single line of text):

  • AddressLine1
  • AddressLine2
  • City
  • State
  • Zip
  • Latitude
  • Longitude

Yours should look like mine except you will have a different prefix than dkdt_.

image

Finally, put everything on the form:

image

Once you’ve done all this, go ahead and Publish your changes.  Next, create a new Dynamics CRM 2011 Package project called GeocodeMapSamplePackage:

image

Fill out the Connect to Dynamics CRM Server dialog:

image

If you are using CRM Online, then your discovery service is dev.crm.dynamics.com if you log in with a Live ID or disco.crm.dynamics.com if your org was provisioned through Office 365.  Make sure you select HTTPS as the protocol if necessary.  Add a Dynamics CRM 2011 Plug-in Library called GeocodMapSamplePlugins to the Visual Studio solution:

image

Find the GeocodeMapSample entity in the Entities node of CRM Explorer, right-click it and select Create Plug-in:

image

Make your dialog look like mine:

image

I am picking the Pre-Operation for the Pipeline Stage because I want to be able to throw an exception back to the caller and roll the transaction back if geocoding fails because my business requirement is that addresses must be valid.  Create another plug-in for the same entity.  This time, we want to do a few things differently.  First, set the value of Message to Update, but DO NOT click OK yet:

image

We only want this plug-in to fire when address related fields change.  Click the Filtering Attributes ellipsis (…) to bring up the following dialog:

image

Make sure you have deselected all of the fields, then ONLY selected items in the image.  In an update scenario, we want to make sure the plugin has the previous values from when the form was loaded.  Click the Parameters ellipsis (…) next to Pre Image Alias to bring up a similar dialog to before and select the same set of attributes.  We’re going to ask the Bing Maps REST service for JavaScript Object Notation (JSON) since it’s the fastest across the wire.  Add the DynamicJson package from NuGet to the plug-in project:

image

image

Now it’s time to generate some early-bound types.  The CRM SDK covers early-bound vs. late-bound here.  Most developers prefer the early bound approach unless they truly need a late-bound for writing very generic, reusable code.  This is because you become more productive and less error prone due to intellisense and compile time checking.  While the developer toolkit does have the ability to generate strongly typed classes:

clip_image002

…it does not give you any control over what classes get generated.  It just brute force creates one for each entity you have read permissions to.  Therefore, you end up generating a bunch of unnecessary code that bloats your codebase and makes your plugin WAY BIGGER than it needs to be.  Instead, I prefer to use this approach to generate the classes:

http://dkdt.me/LO71gI

Which one you choose, is up to you.  Generate Wrapper is OK for now since you are just learning, but take my advice and learn the approach above for production codeSmile.  Add a new class to the plug-in project called GeocodeMapSampleCommon.cs.  Replace the entire contents of the class declaration with the following code:

using System;

using System.Net;

using System.Text;

using Codeplex.Data;

using GeocodeMapSamplePackage.GeocodeMapSamplePlugins.Entities;

using Microsoft.Xrm.Sdk;

 

namespace GeocodeMapSamplePackage.GeocodeMapSamplePlugins

{

    class GeocodeMapSampleCommon

    {

        internal static void GeocodeAddress(Plugin.LocalPluginContext localContext, Entity preImageEntity)

        {

            if (localContext == null)

            {

                throw new ArgumentNullException("localContext");

            }

 

            var pluginExecutionContext = localContext.PluginExecutionContext;

            var targetEntity = pluginExecutionContext.InputParameters["Target"] as Entity;

 

            if (targetEntity == null)

            {

                throw new NullReferenceException("targetEntity");

            }

 

            try

            {

                var targetGeocodeMapSampleEntity = targetEntity.ToEntity<dkdt_GeocodeMapSample>();

                var sb = new StringBuilder();

                

                // see http://dkdt.me/KH4roL for Bing Maps REST API reference

                const string restQueryStart = "https://dev.virtualearth.net/REST/v1/Locations/";

                const string noAddress = restQueryStart + ",,,,";

                var address1 = string.Empty;

                var address2 = string.Empty;

                var city = string.Empty;

                var state = string.Empty;

                var zip = string.Empty;

 

                sb.Append(restQueryStart);

 

                if (preImageEntity != null)

                {

                    var previousGeocodMapSampleEntity = preImageEntity.ToEntity<dkdt_GeocodeMapSample>();

 

                    if (targetGeocodeMapSampleEntity.dkdt_AddressLine1 == null && previousGeocodMapSampleEntity.dkdt_AddressLine1 != null)

                        address1 = previousGeocodMapSampleEntity.dkdt_AddressLine1;

                    if (targetGeocodeMapSampleEntity.dkdt_AddressLine2 == null && previousGeocodMapSampleEntity.dkdt_AddressLine2 != null)

                        address2 = previousGeocodMapSampleEntity.dkdt_AddressLine2;

                    if (targetGeocodeMapSampleEntity.dkdt_City == null && previousGeocodMapSampleEntity.dkdt_City != null)

                        city = previousGeocodMapSampleEntity.dkdt_City;

                    if (targetGeocodeMapSampleEntity.dkdt_State == null && previousGeocodMapSampleEntity.dkdt_State != null)

                        state = previousGeocodMapSampleEntity.dkdt_State;

                    if (targetGeocodeMapSampleEntity.dkdt_Zip == null && previousGeocodMapSampleEntity.dkdt_Zip != null)

                        zip = previousGeocodMapSampleEntity.dkdt_Zip;

                }

 

                if (targetGeocodeMapSampleEntity.dkdt_AddressLine1 != null)

                    address1 = targetGeocodeMapSampleEntity.dkdt_AddressLine1.Trim();

                sb.Append(address1);

                sb.Append(",");

                if (targetGeocodeMapSampleEntity.dkdt_AddressLine2 != null)

                    address2 = targetGeocodeMapSampleEntity.dkdt_AddressLine2.Trim();

                sb.Append(address2);

                sb.Append(",");

                if (targetGeocodeMapSampleEntity.dkdt_City != null)

                    city = targetGeocodeMapSampleEntity.dkdt_City.Trim();

                sb.Append(city);

                sb.Append(",");

                if (targetGeocodeMapSampleEntity.dkdt_State != null)

                    state = targetGeocodeMapSampleEntity.dkdt_State.Trim();

                sb.Append(state);

                sb.Append(",");

                if (targetGeocodeMapSampleEntity.dkdt_Zip != null)

                    zip = targetGeocodeMapSampleEntity.dkdt_Zip.Trim();

                sb.Append(zip);

 

                var restQuery = sb.ToString();

 

                if (restQuery == noAddress)

                {

                    return;

                }

 

                // TODO: Move key to plugin configuration

                const string key = "?key=[INSERT_YOUR_BING_MAPS_KEY]";

 

                var webClient = new WebClient();

 

                var jsonString = webClient.DownloadString(restQuery + key);

                var response = DynamicJson.Parse(jsonString);

 

                if (response.statusCode != 200)

                {

                    throw new InvalidPluginExecutionException("Bad address.  Please fix it and try again.");

                }

 

                var coordinates = response.resourceSets[0].resources[0].point.coordinates;

                targetGeocodeMapSampleEntity.dkdt_Latitude = coordinates[0].ToString();

                targetGeocodeMapSampleEntity.dkdt_Longitude = coordinates[1].ToString();

            }

            catch (Exception ex)

            {

                throw new InvalidPluginExecutionException("Something went wrong in the plug-in.  This must be buggy sample code eh?", ex);

            }

        }

    }

}

This code does all the geocoding work.  Your generated classes will have different prefix values than dkdt_, so fix the errors by replacing dkdt_ with your prefix.  You will also have to replace the [INSERT_YOUR_BING_MAPS_KEY] string with yours from http://dkdt.me/KH4pNx.  Because of the way I am sharing this class across multiple plugins we, need to make a slight tweak to the Plugin.cs class.  Change protected class Plugin : IPlugin to protected internal class Plugin : IPlugin.  Now is a good time to build.  If you get build errors, well fix em Smile.  It’s a blog post after all.  Assuming you got your build errors fixed, there are a couple more things to do.  Open PreGeocodeMapSampleCreate.cs and replace the contents of  the ExecutePreGeocodeMapSampleCreate method with:

GeocodeMapSampleCommon.GeocodeAddress(localContext, null);

Open PreGeocodeMapSampleCreate.cs and replace the contents of  the ExecutePreGeocodeMapSampleCreate method with:

if (localContext == null)

{

    throw new ArgumentNullException("localContext");

}

 

var context = localContext.PluginExecutionContext;

 

var preImageEntity = (context.PreEntityImages != null && context.PreEntityImages.Contains(preImageAlias)) ? context.PreEntityImages[preImageAlias] : null;

 

GeocodeMapSampleCommon.GeocodeAddress(localContext, preImageEntity);

Probably a good time to build and troubleshoot again.  If your build is successful, then sign the plug-in assembly:

image

image

Ok, now you should be able to right-click the package project and deploy:

image

This will register your plug-in and messages.  Now go create a new GeocodeMapSample entity through the CRM UI.  Save, but don’t save and close.  You will see Latitude and Longitude have values after the save.  So now we have to present this stuff on a map.  Let’s do the easy one first: static map image.  Go back to Visual Studio and add a new web resource to the package project:

image

image

Replace the contents of StaticMap.htm with the following:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>

    <head>

        <title></title>

        <script type="text/javascript">
   1:  

   2:             function renderStaticMap() {

   3:                 // ReSharper disable InconsistentNaming

   4:                 var Xrm = window.parent.Xrm;

   5:                 // ReSharper restore InconsistentNaming

   6:  

   7:                 var latitude = Xrm.Page.getAttribute("dkdt_latitude").getValue();

   8:                 var longitude = Xrm.Page.getAttribute("dkdt_longitude").getValue();

   9:  

  10:                 if (latitude == null) {

  11:                     return;

  12:                 }

  13:                 if (longitude == null) {

  14:                     return;

  15:                 }

  16:  

  17:                 // see http://dkdt.me/KH4roL for Bing Maps REST API reference

  18:                 var imageUrl = "http://dkdt.me/KH4rF4" +

  19:                 latitude + "," + longitude +

  20:                     "/18?key=[INSERT_YOUR_BING_MAPS_KEY]";

  21:                 document.getElementById("mapImage").setAttribute("src", imageUrl);

  22:             }

  23:         

</script>

    </head>

    <body onload="renderStaticMap();" style="BORDER-RIGHT-WIDTH: 0px; BACKGROUND-COLOR: rgb(246,248,250); MARGIN: 0px; PADDING-LEFT: 0px; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; PADDING-TOP: 0px">

        <img id="mapImage" alt="An map image of the current record" src=""/>

    </body>

</html>

Again, replace dkdt_ with your prefix.  You will also have to replace the [INSERT_YOUR_BING_MAPS_KEY] string with yours from http://dkdt.me/KH4pNx.  Drop the web resource in the GeocodeMapSample entity form.  Publish it all and you should find a static map image on the form next time you load/refresh it.  Go through the same process to build a web resource named InteractiveMap.htm.  Replace the contents of the file with the following:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>

    <head>

        <title></title>

        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

        <script type="text/javascript" src="https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&s=1"></script>
   1:  

   2:         <script type="text/javascript">

   3:         var _map;

   4:  

   5:         function loadMap() {

   6:             _map = new Microsoft.Maps.Map(document.getElementById("map"), { credentials: "[INSERT_YOUR_BING_MAPS_KEY]" });

   7:  

   8:             // ReSharper disable InconsistentNaming

   9:             var Xrm = window.parent.Xrm;

  10:             // ReSharper restore InconsistentNaming

  11:  

  12:             var latitude = Xrm.Page.getAttribute("dkdt_latitude").getValue();

  13:             var longitude = Xrm.Page.getAttribute("dkdt_longitude").getValue();

  14:  

  15:             if (latitude == null) {

  16:                 return;

  17:             }

  18:             if (longitude == null) {

  19:                 return;

  20:             }

  21:  

  22:             var location = new Microsoft.Maps.Location(latitude, longitude);

  23:             var pushpin = new Microsoft.Maps.Pushpin(location);

  24:             _map.setView({ center: location, zoom: 10 });

  25:             _map.entities.push(pushpin);

  26:         }

  27:         

</script>

    </head>

    <body onload="loadMap();" style="BORDER-RIGHT-WIDTH: 0px; BACKGROUND-COLOR: rgb(246,248,250); MARGIN: 0px; PADDING-LEFT: 0px; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; PADDING-TOP: 0px">

        <div id="map" style="position: relative; width: 100%; height: 100%"></div>

    </body>

</html>

Again, replace dkdt_ with your prefix.  You will also have to replace the [INSERT_YOUR_BING_MAPS_KEY] string with yours from http://dkdt.me/KH4pNx.  Drop the web resource in the GeocodeMapSample entity form.  Drop the web resource in the GeocodeMapSample entity form.  Publish it all and you should find a static map image on the form next time you load/refresh it.  Finally, Latitude & Longitude really are just there for code interaction.  They don’t need to be visible on the form.  Go ahead and hide them.

That’s it!  If you made it through the whole post, you now know the fundamentals of using plug-ins, web resources, and the Bing Maps SDKs to add basic “geospatial” functionality to CRM 2011.  Of course, you probably won’t want to scatter your Bing Maps key through out your code, but I wanted to keep the walkthrough as simple to follow as possible.  You can grab the unmanaged solution and Visual Studio source code here:

http://sdrv.ms/KH4j8D

@devkeydet

Connecting to CRM Online from an outside caller

UPDATE: Thanks to some internal discussions with folks on the SDK team, I discovered a simpler way that doesn’t require manually putting the deviceid/devicepassword in the config file.  I decided to leave this post as-is for two reasons.  First, because the easier way requires full trust and won’t work for you if your hoster or IT department requires partial trust.  Second, so people understand what the easier way automates.  I’ve updated this post with a “you can skip this step” explanation. 

Scenario:

“I want to make calls to CRM from an ASP.NET page, .NET middle tier code, etc.  How do I do it with CRM Online?”

Simple answer right?  Maybe for the persistent developer who is happy to reverse engineer the steps from the SDK documentation.  However, I have to say the SDK doesn’t spell it out for you step by step. 

The following resources seem to help on the surface:

Walkthrough: Build a Web Application That Connects to Microsoft Dynamics CRM 2011 Using Developer Extensions

ASP.NET Web Forms and Data Binding

Create Early Bound Entity Classes with the Code Generation Tool (CrmSvcUtil.exe)

Simplified Connection to Microsoft Dynamics CRM

Sample: Authenticate Users with Microsoft Dynamics CRM Web Services

But there’s a little CRM Online nuance that sort of gets lost in the shuffle unless you connect the dots across some of these articles.  CRM Online, when using a Live ID, requires that you use a DeviceId and DevicePassword when connecting.  While the Simplified Connection to Microsoft Dynamics CRM article does explain this, it leaves it up to the reader to hunt down the whole Device ID setup process.  This post is my attempt to connect the dots in a step by step walkthrough of building a simple ASP.NET Web Forms page that connects to CRM Online using a Live ID.

Let’s start by creating a new ASP.NET Empty Web Application and call it CrmOnlineConnectedWebApp:

image

Add the following references from the SDKbin folder.

  • Microsoft.Xrm.Client.dll
  • Microsoft.Xrm.Sdk.dll

Add the following references from .NET.

  • System.Data.Services.dll
  • System.Data.Services.Client.dll
  • System.Runtime.Serialization.dll

DEVICE REGISTRATION

You can skip this next step and jump to CREATE EARLY BOUND TYPES if you follow the enhancement in the Eliminating manual device registration post.

There’s a section titled “To Generate your individual device ID and password” in the Create Early Bound Entity Classes with the Code Generation Tool (CrmSvcUtil.exe), but it’s easy to miss.  Follow the instructions from that section:

  • Open and build the DeviceRegistration project: SDKToolsDeviceRegistrationDeviceRegistration.csproj.
  • Run the executable file from the command line. To register your device, set the /operation parameter to Register.
  • C:deviceregistration.exe /operation:Register
  • Copy the displayed device ID and password values and use them as the deviceid and devicepassword parameter values when you run the CrmSvcUtil tool.

The easiest way to copy the Device ID and the Device Password from the console is to right-click and select all:

image

…right-click again (this will copy the contents) and paste into notepad.  Then you can copy the actual values. 

CREATE EARLY BOUND TYPES

Let’s create some early bound types as described in step one of Walkthrough: Build a Web Application That Connects to Microsoft Dynamics CRM 2011 Using Developer Extensions.  However, we need to remove the domain flag:

CrmSvcUtil.exe /codeCustomization:"Microsoft.Xrm.Client.CodeGeneration.CodeCustomization, Microsoft.Xrm.Client.CodeGeneration" /out:Xrm.cs /url:[YOUR_CRM_ROOT]/XRMServices/2011/Organization.svc /username:[YOUR_USERNAME] /password:[YOUR_PASSWORD] /namespace:Xrm /serviceContextName:XrmServiceContext

Now add the generated file to your Visual Studio project.  Now is a good time to build.  If the build fails, you are likely missing an assembly reference.  Now let’s add a Web Form called Default.aspx:

image

Add a GridView to the markup of Default.aspx:

image

The next steps are slightly different if you decided to use the approach in my Eliminating manual device registration post.  Switch to Default.aspx.cs and paste the following code into the Page_Load handler:

var connection = new CrmConnection("CrmOnline");

var context = new XrmServiceContext(connection);

 

var query = from a in context.AccountSet

            select new

            {

                a.Name,

                a.AccountNumber,

                a.Address1_City

            };

 

GridView1.DataSource = query.ToList();

GridView1.DataBind();

The final step is to put the CrmOnline connection string into your web.config:

<connectionStrings>

    <add name="CrmOnline" connectionString="Url=[YOUR_CRM_ORG_URL]; Username=[YOUR_LIVE_ID]; Password=[YOUR_PASSWORD]; DeviceId=[DEVICE_ID_FROM_CONSOLE]; DevicePassword=[DEVICE_PWD_FROM_CONSOLE]"/>

    </connectionStrings>

You should be able to run the app now and see data in the GridView.

@devkeydet

Controlling CRM 2011 popup windows

Now and then, I hear from users that don’t like the fact that CRM loads screens in distinct windows in certain scenarios.  I can understand how these windows can get a little daunting if you have too many open and are switching context between multiple windows on your desktop.  However, this is actually a really nice to have capability when you are working in a multi monitor setup.  A tip that I have been sharing quite a bit lately, but have yet to blog about is to use the features of your browser to control the behavior if you prefer not having to deal with the separate windows.  Specifically, change your tabs configuration:

image

Now let’s walk through the behavior.  I’ll start by clicking an Account record:

image

Notice how the account opened in a new tab next to my first tab:

image

Again, I will click something that would traditionally give me a popup window.  Now there is a third tab:

image

There’s also a natural “breadcrumb effect” because the tabs are in order left to right of how I interacted with them.  Also, when I close the rightmost tab, I’m back where I came from, and so on.  So if you are in the “I don’t like all these popup windows” camp, this will hopefully help you out.

@devkeydet

Using the CRM 2011 OData service from a Metro style app

UPDATE: Fixed a few bugs in the downloadable sample including one where it wouldn’t work in Office 365 based CRM Online subscriptions.  Edited some text in the post for clarity.

UPDATE2: Added some text about how to get this working for Windows Authentication.

UPDATE3: I’ve just blogged about an update to the sample here.  The updates in the code might make following this post a little confusing.  The reason is that due to the final terminology for Windows Store Apps, I needed to refactor the solution/project files and class names.  While I plan on updating this post soon, I wanted to get the changes published.  Hopefully, the naming changes are obvious enough for folks until I update this post. 

Scenario:

“I want to build a Windows 8 Metro style application.  Since my app is going to just perform CRUD operations, I want to use the OData service using json so that I have the least amount of ‘data across the wire’ possible.”

DISCLAIMER: This a pretty hacky approach, but it works and is the only way I know of to do it.  If you like it, use it.  If not, wait until there’s an official sample in the SDK.  You’ve been warned.  I’m just a guy getting creative to solve a problemSmile.

First I will walk you through how to do it.  Then I will explain how it works.  To get started, download and import the following solution into you CRM 2011 organization:

http://sdrv.ms/S3MND1

Next, download the source code of my sample library:

http://sdrv.ms/RYzVzj

I say sample library very loosely.  This is FAR from being a complete, well tested library.  I have good intentions to make it one, but wanted to get a sample of the basic plumbing working for those of you who have asked me how to do it.  This sample should be enough to get you started, but you’ll have to finish out the rest of the functionality.  If anyone wants to take this sample and turn it into an open source project, YOU HAVE MY FULL PERMISSIONSmile.

USING THE SAMPLE

Create a new Metro style app.  I am going to use the Blank App template to keep things simple:

image

Add my sample library to the solution:

image

Don’t forget to right-click References nod of the Crm2011MetroStyleODataApp in Solution Explorer and a reference to the project you just added:

image

image

Find the Devkeydet.CrmMetroODataHelper.csproj file and add it.  Replace the default Grid in MainPage.xaml with the following:

<Grid x:Name="LayoutRoot" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

    <StackPanel x:Name="MainUIStackPanel" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed">

        <TextBlock x:Name="myTextBlock" Text="TextBlock" TextAlignment="Center" />

        <Button x:Name="queryButton" Content="Button" />

    </StackPanel>

</Grid>

In MainPage.xaml.cs, paste the following code into the OnNavigatedTo method:

var oDataHelperWebResource = "https://devkeydet.crm.dynamics.com/WebResources/dkdt_/MetroODataHelper.htm";

 

if (!CrmMetroODataHelper.IsLoggedIn)

{

    CrmMetroODataHelper.SignIn(oDataHelperWebResource, LayoutRoot);

    CrmMetroODataHelper.SignInComplete = () => MainUIStackPanel.Visibility = Visibility.Visible;

}

Make sure to replace “https://devkeydet.crm.dynamics.com” with the right url for you.  You will have to add a using Devkeydet statement to the file.  Ok, now it’s time to “Add Service Reference” to the OData service.  Unless you are using Windows Authentication, you can’t point the “Add Service Reference” dialog to the data service url.  Instead, you need to download it and point to it locally, from Visual Studio:

image

image

Save it somewhere and point, then “Add Service Reference” by pointing Visual Studio to the file:

image

Add the following method to MainPage.xaml.cs:

private  async void ExecuteODataQueryAsync()

{

    var ctx =

        new devkeydetContext(new Uri("https://devkeydet.crm.dynamics.com/XRMServices/2011/OrganizationData.svc/"));

 

    // Use the generated DataServiceContext to compose the query using LINQ, 

    // but get the string of the query via ToString().

    // We still get to use the query composition capabilities of LINQ!

    var query = from a in ctx.AccountSet

                where a.Name.StartsWith("A")

                select new Account

                {

                    Name = a.Name

                };

 

    var accounts = await CrmMetroODataHelper.RetrieveMultipleRecordsAsync<Account>(query.ToString());

 

    myTextBlock.Text = accounts.First().Name;

}

Again, remember to replace “https://devkeydet.crm.dynamics.com” with the right url for you.  You will have to add a using statement based on the Namespace you gave the generated code and devkeydetContext will need to be replaced with your generated context name.  Now, wire up an event handler for the button (ex: double click the button on the design surface) and call the method:

ExecuteODataQueryAsync();

If your app is using Windows Authentication, then you need to double-click on the Package.appxmanifest and make sure you have the following Capabilities checked:

image

Go ahead and run the app.  You will be prompted for your credentials if necessary:

image

Sign in with your credentials, click the button, and the TextBlock should have the value of the Name property of the first result:

image

There you go!  You just issued an OData query from a C# Metro style app!

HOW THE SAMPLE WORKS

The sample wraps the WebView control.  The WebView control tries to load an html web resource from the CRM server (that’s what’s in the devkeydetMetroODataHelper_1_0.zip solution).  If you haven’t logged in, then the WebView is set to be visible and it will just present you with the web based login UI.  Once logged in, the WebView visibility is collapsed and the C# wrapper basically proxies OData calls through WebView via the web resource using a modified version of the SDK.REST.js file from the CRM 2011 SDK.  The major modification is that the default SDK.REST.js sample will turn the resulting json string into an JavaScript object.  Instead, I just pass the json string back to the C# wrapper and use JSON.NET to turn it into C# objects.

As I mentioned earlier, this is FAR from being a complete and reusable library.  I have good intentions to make it one, but wanted to get a sample of the basic plumbing working for those of you who have asked me how to do it.  This sample should be enough to get you started, but you’ll have to finish out the rest of the functionality.  I only implemented executing a query that returns multiple results.  Furthermore, I didn’t handle continuation tokens.  If anyone wants to take this sample and turn it into an open source project, YOU HAVE MY FULL PERMISSIONSmile.

@devkeydet

Populate Two Options values when creating a record in a dialog

Scenario:

“I have an entity with a field of type ‘Two Options’ that I would like to populate within a ‘Create Record’ activity in a CRM 2011 dialog.   The ‘Two Options’ type in CRM 2011 is a boolean.  The ‘Prompt and Response’ activity in a CRM 2011 dialog only gives me options for Text/Integer/Float.”

image

“Because of this, I can’t set the value of the answer to the field in the entity  within ‘Set Properties’ because the types don’t match.”

image

image

Custom workflow activities to the rescue!  I wrote a very simple workflow activity that converts the integer to a boolean.

image

image

image

Once you have the ‘Two Options’ typed value, you can map it to your field.

image

The code for the workflow activity is the simplest workflow activity I have ever created:

image

If you don’t want to build/deploy the code yourself, but just want to use the workflow activity to solve this problem, then you can download an unmanaged solution here:

https://skydrive.live.com/redir?resid=1F72DA7294089597!16571

I also have an end to end example in an unmanaged solution here:

https://skydrive.live.com/redir?resid=1F72DA7294089597!16570

In the end to end example, I went ahead and created a ribbon button that will launch the dialog without having to select an entity in the grid.

image

I used the User entity trick from http://dkdt.me/KmqxOZ.  In putting together the sample, I was also inspired by http://dkdt.me/KmqwdV.  However, I took a shortcut.  I hard coded the CRM User guid for the ObjectId in the dkdt_/scripts/LaunchModalDialog.js file.  You will need to go into it and update it with an actual user in your CRM org for the sample to work.

If you want the source code for everything, you can grab it here:

https://skydrive.live.com/redir?resid=1F72DA7294089597!16569

NOTE: The code requires the Developer Toolkit for Microsoft Dynamics CRM 2011.

 

@devkeydet

REMINDER: SharePoint BCS connector for CRM

I’ve been going through a bunch of CRM integration scenarios to refresh my “hands on” abilities.  You have to do that in a job like mine, otherwise you’re just a talking head that doesn’t know what you’re talking aboutSmile.  Anyway, the topic of integrating CRM with SharePoint beyond what’s available out of the box comes up often.  Two sessions worth watching to understand some of the possibilities are:

http://dkdt.me/L5DcGW

http://dkdt.me/kUq33n

They are very similar sessions, but from two different presenters.  One of the things covered in these sessions is how to integrate CRM and SharePoint through BCS.  The trick to doing it productively is the sample code Chris and Girish made available here:

http://dkdt.me/L5DcH4

With this, you can now open up the world of possibilities that SharePoint BCS enables including cross entity searching.  See Configuring Business Connectivity Services (BCS) Search in SharePoint 2010 as an example.

@devkeydet

TIP: Associate a schema with an XML file in Visual Studio 2010

It’s been a while since I had to do it.  However, as I’ve started to need to tweak FetchXML after I’ve built the baseline using my little trick, I realized I was doing things the hard way.  I was looking at the FetchXML reference on MSDN and typing things “notepad" style” but with syntax highlighting.  Then I had that “DUH” moment.  Just point the file to the FetchXML schema in the SDK and you’ll get intellisense.  Here’s how:

Take a look at the Properties of your XML file in Visual Studio:

image

In the Properties pane, click on the ellipsis () for the Schemas property:

image

Click the Add… button:

image

Find the fetch.xsd file that’s in the schemas folder of wherever you unzipped the Microsoft Dynamics CRM 2011 Software Development Kit (SDK) and click the Open button:

image

Now you have XML intellisense for your FetchXML:

image

@devkeydet

Converting a SQL report to FecthXML

Scenario:

“I want to move to CRM Online, but I wrote all my SQL Server Reporting Services (SSRS) using the SQL Data Source.  How do I convert them to use FetchXML?”

Below I have three reports:

image

All three reports will look identical to the end user in the end.

facilityFetch.rdl – Written using the “Microsoft Dynamics CRM Fetch” Data Source available after you install the Microsoft Dynamics CRM 2011 Report Authoring Extension.

facilitySql.rdl – Written using the “Microsoft SQL Server” Data Source.

facilityConvertedSqlToFetch.rdl – Identical to facilitySql.rdl to start, but will be identical to facilityFetch.rdl once converted.

The key to a quick conversion is getting the results of the fetch query to look IDENTICAL to the sql query.  Basically, what you need to do is go through all your Datasets and update them with a matching fetch query.  In my example, facilityConvertedSqlToFetch.rdl DataSet1 uses a local Data Source called sqlds:

image

image

image

First step is to create a new fetch data source:

image

image

I tend to develop locally for developer productivity so I am pointing a local CRM 2011 deployment in my VM.  The format for the connection string is [root];[orgname].  Now that we have the fetch Data Source created, we need to create a matching fetch query.  My favorite way of going about this is here.  So let’s update the Dataset:

image

Make sure you update the Data Source to use the new fetch data source and replace the query with the right FetchXML query:

image

Again, the KEY here is that the shape of the Dataset is the same AND the column names are the same.  Save & Preview your new report.  It should “just work.”  For more advanced FetchXML scenarios, see Build Queries with FetchXML.

@devkeydet

Cross domain calls from JavaScript

It’s exciting times for web developers.  Many of the hoops we used to have to jump through are being eliminated as more browsers support more of the HTML5 spec and more people are using modern browsers.  One of the things that has me excited is Cross-origin resource sharing (CORS)

In the context of Dynamics CRM, it means that more can be done from HTML and JavaScript web resources without having to do things like proxy calls through a server.  It also reduces the scenarios that require you to implement Cross domain calls to the parent CRM 2011 form.  The need to replace window.parent with window.postMessage when you truly do need to communicate cross domain across iFrames.  However, I’ve used the window.postMessage approach to have a hidden iFrame in a CRM form that is a blank ASP.NET page that calls a third party web service from server-side code, then passes the response to a the client, which then passes the results to the CRM form using window.postMessage and the form code prefills controls in the form.  All this hoop jumping is necessary because of the way older browsers work.  The complexity could be reduced with CORS. 

The rub is that there are still browsers out there that don’t support CORS.  The wikipedia link covers which.  The good news is that modern browser adoption is growing fast.  We’re approaching a time where there is a small enough group of the older browsers where you might feel comfortable displaying a message to your users saying “Are those cobwebs I see on your browser?  Please upgrade to use this app.”  Not everyone can do that, but if you can, then read on.

IE10 supports CORS through the XMLHttpRequest object.  IE8 & 9 support it, but the through the XDomainRequest object.  I don’t know about you, but I don’t XMLHttpRequest directly anyway.  I typically use the jQuery $.get(), $.getJSON(), or $.ajax() methods.  There is a helper library for jQuery called jQuery.iecors that allows you to use jQuery unchanged.  The last thing you need to know is that the server you are calling to has to allow CORS.  Thanks to enable-cors.org, you have a handy reference to know how enable it on your server. 

@devkeydet