Chapter 10

Managing Customer Information in SharePoint Online

What's In This Chapter?

  • Building a SharePoint Online dashboard that brings various customer-related information from Microsoft Dynamics CRM Online
  • Understanding CRM Online and how to interact with its web services
  • Using Silverlight and Windows Azure to bridge SharePoint Online and CRM Online

Customers are the key to any successful business. In our current global economy, gaining new customers, while at the same time keeping existing customers happy, is more important than ever. As a result, customer relationship management (CRM) systems have evolved rapidly in the last decade. What were once nice-to-have tools are now must-have tools for businesses worldwide. If you are new to CRM, it is essentially an application to manage the sales pipeline, marketing activities, and customer service requests of a business.

Traditionally, businesspeople have maintained their customer information in a simple list within Microsoft Excel, but their needs quickly outgrow such simple lists. Software as a service-based (SaaS) CRM systems are already one of the major applications in the cloud computing space, and many analysts forecast that the number of customers using such online CRM systems will grow significantly in the next few years.

Although most CRM systems are capable of handling all business functions (such as ordering, billing, and inventory), many organizations still use multiple line-of-business (LOB) applications to manage such functions. Integrating data from these disparate LOB applications and creating a unified view of the information is critical if business users are to analyze and collaborate on the information it provides. This is where SharePoint comes into the picture, as it is an indispensible platform for building such integrated solutions.

In this chapter, you will build a simple SharePoint Online application that provides a unified view of data from CRM Online. The SharePoint application can then be extended to include data from other LOB applications.

Overview of Microsoft Dynamics CRM

Microsoft Dynamics CRM is an application built by Microsoft as part of the Microsoft Dynamics family of business applications. Dynamics CRM offers a full suite of functionality in the sales, marketing, and customer service areas. Similar to SharePoint, Dynamics CRM is available in three deployment models: traditional on-premises software, partner-hosted systems, and the Microsoft-hosted Microsoft Dynamics CRM Online (aka CRM Online). This chapter focuses only on CRM Online, especially the R6 update of CRM Online released in January 2011, and currently available in 40 markets and 41 languages worldwide.

note Although this chapter covers only CRM Online, you should be able to use the CRM code from this chapter in other deployment models, as well. All three types of CRM deployment have the same underlying code base, and the developer extensibility options are similar across all three.

From a developer's perspective, CRM Online is very similar to SharePoint in that it is a web application built on .NET, has industry-standard web service endpoints, and uses SQL Server as the underlying storage system, with a variety of middle-tier components, including plug-ins, workflows, and so on. The CRM UI uses components from SharePoint and Office, such as the navigation pane and ribbon, as shown in Figure 10.1.

Figure 10.2 shows an architectural overview of the CRM platform and the various components contained within it. The gear symbols represent customization points for developers. For more information on the CRM architecture, check out the CRM developer center, at http://msdn.microsoft.com/dynamics/crm.

note Developers have built a variety of LOB applications on top of the CRM platform. To learn more about CRM and how developers can extend it, check out the whitepaper titled “Building Business Applications with Microsoft Dynamics CRM 2011,” available at www.microsoft.com/downloads/en/details.aspx?FamilyID=42b24fb7-b0c7-408a-ae65-90884616285f.

CRM Online is built on .NET Framework 4 and offers a couple of WCF-based web services to interact with the data. This enables developers to use a variety of techniques to interact with CRM's web services, as shown in Figure 10.3. In this chapter, we'll be interacting with CRM Online web services from a Windows Azure proxy using late-bound connectivity to the SOAP endpoint with Microsoft.Xrm.Sdk assemblies.

As mentioned earlier, the API model is the same for both on-premises software and CRM Online. The only difference is how you authenticate to the web services.

The Solution Architecture

In this chapter, the objective is to build an account-management dashboard within SharePoint Online that integrates customer-related information from a variety of sources, such as CRM Online, document libraries, and a custom list within SharePoint. Figure 10.4 shows the result of following the steps described in this chapter.

One of the major components of the dashboard that we'll be building in this chapter is the Silverlight-based custom web part, which displays account information in a grid and the sales pipeline in a nice chart. Figure 10.5 shows the high-level architecture of the Silverlight web part.

The customer data is stored within CRM Online as two different entities: an Account entity and an Opportunity entity. You can think of “entities” in CRM Online as analogous to tables within a SQL Server database.

The web part running on the SharePoint Online page uses a custom Silverlight 4 control. The Silverlight control calls a thin WCF proxy web service running on Windows Azure. The WCF proxy web service, in turn, calls the CRM Online web services to read and write customer data. You can also enable caching on the proxy to improve web part performance. Windows Azure helps with scalability, distribution, and composition of multiple data sources, if necessary. The WCF proxy uses .NET Framework version 4 so that you can use the Microsoft.Xrm.Sdk assemblies, which are in .NET 4 as well. The WCF proxy also enables cross-domain web service calls to be made from the Silverlight control running on SharePoint Online. Caching can be optionally enabled on the proxy to further improve data load performance for the SharePoint Online users.

Using Silverlight with SharePoint

There is a good reason why many developers are building custom UI components for SharePoint using Silverlight. As a platform, SharePoint 2010 natively supports Silverlight out of the box. Because of Silverlight's tight integration with SharePoint, and hence support within Visual Studio, it is easy for developers to build and deploy such applications. Given the sandbox restrictions in SharePoint Online, which don't allow you to host custom server-side web parts, Silverlight web parts that execute on the client make perfect sense.

One of the main reasons we are using Silverlight within this solution is because of Silverlight's capability to seamlessly call external web services (Windows Azure WCF proxy, in this case) in an asynchronous fashion. The client object model within the SharePoint API also has a Silverlight version, making it easy for developers to use Silverlight controls as artifacts within SharePoint. Furthermore, Silverlight, along with Expression Blend, makes it easy for developers and designers to collaborate when creating user interfaces.

Security and Authentication

Any cloud-based system must take into careful consideration the security it employs for both the data it stores and the data in transit. This section provides an overview of the important security measures developers must consider before deploying this solution within their environment. A detailed, step-by-step process for implementing security is beyond the scope of this book, of course.

Data in transit can be secured by using HTTPS (HTTP with SSL/TLS). SharePoint Online and CRM Online have security measures to protect data that is stored; and HTTPS is enabled by default to secure the connection on their end. If an intermediary Azure proxy web service doesn't have SSL enabled, the end-to-end web calls are not fully secure, and the browser might warn users of mixed-mode errors. However, if you enable HTTPS for the proxy as well, you shouldn't see such browser errors with this solution.

Authentication for SharePoint Online can be done using Office 365's built-in authentication methods. In this solution, we are going to assume that not all SharePoint Online users are CRM Online users, so we will be using one set of CRM Online credentials as “Service” credentials for all SharePoint Online users. If every SharePoint Online user is also a CRM user, then, ideally, authentication can be flowed across systems through claims-based federated authentication.

Warning

The underlying code in CRM Online uses claims-based authentication using Microsoft's Windows Identity Framework (WIF), although at the time of this writing, Windows Live ID is the only supported authentication mechanism for CRM Online.

Building the Dashboard

In this section, you will build various components of the dashboard. The build process is split into the following three steps:

1. Connect document libraries to CRM Online.

2. Build the Windows Azure proxy.

3. Create the Silverlight grid and chart.

You will then bring together the components to create the dashboard. In order to complete the activities in this chapter, you will need the following developer tools and online accounts:

Connecting Document Libraries to CRM Online

Once you have provisioned your CRM Online and SharePoint Online subscriptions, the next step is to connect them together for document libraries. CRM Online now enables you to use the document-management capabilities of SharePoint directly from within Microsoft Dynamics CRM. You can store and manage documents in the context of a CRM record on the SharePoint platform. This is made possible by a SharePoint solution called the List component, which the CRM team ships as part of CRM Online.

note For more information on this topic and a step-by-step configuration guide to connecting the document libraries to CRM Online, refer to the CRM 2011 SDK topic titled “Integrate SharePoint with Microsoft Dynamics CRM,” at http://msdn.microsoft.com/en-us/library/gg334768.aspx. A step-by-step configuration guide can be found at http://blogs.msdn.com/b/crm/archive/2010/10/08/crm-with-sharepoint-integration-introduction.aspx.

Once you have configured the connection between SharePoint document libraries and CRM Online, you have completed the first component required for the dashboard. When you browse to the Documents section for a record within CRM (on the left in Figure 10.6), you will see it as native CRM functionality, but the documents are being stored and managed in SharePoint (on the right in Figure 10.6).

Building the Windows Azure Proxy

Now that you've configured the connection to document libraries, you can start building the Silverlight part of the dashboard. The Silverlight control is a typical three-tiered application (refer to Figure 10.5), and you'll be building it from the bottom up. Given that the CRM Online and SharePoint Online platforms have already taken care of the data model, you can now build the middle-tier — the WCF web services proxy running in Windows Azure.

1. Open Visual Studio 2010 and click File ⇒ New ⇒ Project. In the New Project dialog, choose Other Project Types ⇒ Visual Studio Solutions ⇒ Blank Solution to create a blank solution. Enter the name of the solution as WroxCRMOnlineWebpart and click OK (see Figure 10.7).

2. Right-click on the solution in Solution Explorer and choose Add New Project to add a new Windows Azure Project within the solution. Name it AzureCRMProxy (see Figure 10.8). Click OK.

3. Add a WCF Service Web Role in the New Windows Azure Project dialog.

4. Rename the WCF Service Web Role to WCFCRMProxyRole, as shown in Figure 10.9.

5. Visual Studio 2010 will create a default WCF service called Service1. Within the file Service1.svc.cs, use the Rename command on the Visual Studio Refactor menu, as shown in Figure 10.10, to change the class name to CRMProxyService.

6. Similarly, rename the appropriate interface class to ICRMProxyService.

It's a good practice to change the filenames to appropriate class names. After you have made the changes from steps 5 and 6, your Visual Studio Solution Explorer should look like Figure 10.11.

7. Right-click on the WCFCRMProxyRole project, select Add Reference, and then click the Browse tab to add the microsoft.xrm.sdk.dll and microsoft.crm.sdk.proxy.dll files.

These assemblies, located within the CRM 2011 SDK installation folder under the sdkin directory, simplify the amount of code needed to interact with the CRM Online WCF web services.

8. Similarly, add a reference to Microsoft.IdentityModel.dll from the folder C:Program FilesReference AssembliesMicrosoftWindows Identity Foundationv3.5. If you don't see this file, ensure that you have installed the Windows Identity Foundation v3.5 runtime.

9. Within Visual Studio Solution Explorer, right-click on Microsoft.IdentityModel.dll and select Properties. Within the Properties window, set the value to Copy Local to True.

10. Also add a reference to System.Security (version 4.0.0.0) under the .NET tab in the Add Reference dialog.

11. Now you need to include a couple of helper source code files from the CRM SDK. Browse to the CRM 2011 SDK installation subfolder sdksamplecodecshelpercode and copy the files deviceidmanager.cs and myorganizationcrmsdktypes.cs. Right-click on the WCFCRMProxyRole project, select Open Folder in Windows Explorer, and paste the two files within the open folder.

12. Within Visual Studio Solution Explorer, select Show All Files. The two new copied files will be displayed in Solution Explorer. Right-click on both the files and choose Include in Project.

13. To define the DataContract for the data you'll need and the OperationContract for operations you'll be performing with the data, open the ICRMProxyService.cs file and add the following code:

download
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFCRMProxyRole
{
  [ServiceContract]
  public interface ICRMProxyService
  {
    [OperationContract]
    List<AzureAccount> GetAccounts();
  }

  // Note that the CRM datatypes defined in myorganizationcrmsdktypes.cs are not
  // compatible with Silverlight client
  [DataContract]
  public class AzureOpportunity
  {
    private string _Name;
    [DataMember]
    public string Name
    {
      get { return _Name; }
      set { _Name = value; }
    }

    private decimal _EstimatedRevenue;
    [DataMember]
    public decimal EstimatedRevenue
    {
      get { return _EstimatedRevenue; }
      set { _EstimatedRevenue = value; }
    }
  }

  [DataContract]
  public class AzureAccount
  {
    [DataMember]
    public List<AzureOpportunity> Opportunities;

    private string _AccountID;
    [DataMember]
    public string AccountID
    {
      get { return _AccountID; }
      set { _AccountID = value; }
    }

    private string _AccountName;
    [DataMember]
    public string AccountName
    {
      get { return _AccountName; }
      set { _AccountName = value; }
    }

    private string _MainPhone;
    [DataMember]
    public string MainPhone
    {
      get { return _MainPhone; }
      set { _MainPhone = value; }
    }

    private string _Email;
    [DataMember]
    public string Email
    {
      get { return _Email; }
      set { _Email = value; }
    }

    private string _City;
    [DataMember]
    public string City
    {
      get { return _City; }
      set { _City = value; }
    }

    private string _State;
    [DataMember]
    public string State
    {
      get { return _State; }
      set { _State = value; }
    }
  }
}

code snippet 076576 Ch10_Code.zip//ICRMProxyService.cs

14. With the contracts defined, you need the WCF endpoint URL for your instance of CRM Online. You can get it by logging into CRM Online and choosing Settings ⇒ Customizations ⇒ Developer Resources. Your URL will be displayed under Organization Service, as shown in Figure 10.12.

15. Open the web.config file to enter the WCF endpoint URL (referred to as Organization URI), and enter the credentials that you'll be using to call CRM Online web services. The endpoint URL and the credentials referred to here will be used in the code to retrieve data from CRM Online.

download
<appSettings>
  <add key="OrganizationUri"
  value="https://yourcrm.api.crm.dynamics.com/XRMServices/2011/Organization.svc"/>
  <add key="WLIDUsername" value="[email protected]"/>
  <add key="WLIDPassword" value="yourpassword"/>
</appSettings>

code snippet 076576 Ch10_Code.zip/WCFCRMProxyRole/Web.config

Warning

In this chapter, we have entered credentials in plain text within the configuration file for readability and illustrative purposes, but you should use encrypted configuration sections or claims-based/federated authentication in real-world implementations.

16. Open the CRMProxyService.svc.cs file and replace its contents with the following code to declare various variables that will be needed:

download
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using System.ServiceModel.Description;
using System.Configuration;

namespace WCFCRMProxyRole
{
  public class CRMProxyService : ICRMProxyService
  {
    public Uri OrganizationUri;
    public Uri HomeRealmUri = null;
    public ClientCredentials Credentials = null;
    public ClientCredentials DeviceCredentials = null;

    private OrganizationServiceProxy _serviceProxy;
    private IOrganizationService _service;

    public List<AzureAccount> GetAccounts()
    {
    }
  }
}

code snippet 076576 Ch10_Code.zip/CRMProxyService.svc.cs

17. Add the following code within the GetAccounts() method. In this code segment, we create a reference to the CRM Online web service and retrieve all the accounts from CRM Online and their corresponding sales pipeline. We are using LINQ statements to query CRM Online.

download
...
// Retrieve the CRMOnline Organization from the web.config settings
OrganizationUri = new Uri(ConfigurationManager.AppSettings["OrganizationUri"]);
ClientCredentials Credentials = new ClientCredentials(); ;
Credentials.UserName.UserName = ConfigurationManager.AppSettings["WLIDUsername"];
Credentials.UserName.Password = ConfigurationManager.AppSettings["WLIDPassword"];
DeviceCredentials = Microsoft.Crm.Services.Utility.DeviceIdManager.
                                                        LoadOrRegisterDevice();

// Connect to the Organization service.
// The using statement assures that the service proxy will be properly disposed.
using (_serviceProxy = new OrganizationServiceProxy(OrganizationUri,
                                                    HomeRealmUri,
                                                    Credentials,
                                                    DeviceCredentials))
{
  // This statement is required to enable early-bound type support.
  _serviceProxy.EnableProxyTypes();

  _service = (IOrganizationService)_serviceProxy;
  OrganizationServiceContext orgContext = new
      OrganizationServiceContext(_service);

  List<AzureAccount> azureAccounts = new List<AzureAccount>();
  // Get the list of Accounts from CRMOnline
  azureAccounts = (from a in orgContext.CreateQuery<Account>()
                    select new AzureAccount
                    {
                      AccountID = a.Id.ToString(),
                      AccountName = a.Name,
                      City = a.Address1_City,
                      Email = a.EMailAddress1,
                      MainPhone = a.Telephone1,
                      State = a.Address1_StateOrProvince,
                      Opportunities = new List<AzureOpportunity>()
                    }).ToList();

  List<AzureOpportunity> azureOpps = new List<AzureOpportunity>();
  // Get the list of corresponding opportunities from CRMOnline
  var allCRMOpps = (from accountId in
                      (from a in azureAccounts
                        select Guid.Parse(a.AccountID))
                    join o in orgContext.CreateQuery<Opportunity>()
                    on accountId equals o.CustomerId.Id
                    where o.StateCode.Value == OpportunityState.Open
                    select new Opportunity
                    {
                      CustomerId = o.CustomerId,
                      Name = o.Name,
                      EstimatedValue = o.EstimatedValue
                    });

  // Associate the opportunities to account objects
  foreach (Opportunity opp in allCRMOpps)
  {
    AzureAccount currentAccount = azureAccounts.FirstOrDefault(acc =>
        acc.AccountID.Equals(opp.CustomerId.Id.ToString()));
    currentAccount.Opportunities.Add(new AzureOpportunity()
    {
      Name = opp.Name,
      EstimatedRevenue = opp.EstimatedValue.Value
    });
  }
  return azureAccounts;
}
...

code snippet 076576 Ch10_Code.zip/CRMProxyService.svc.cs

18. In order for the Silverlight control to be able to call this WCF web service, add a file called clientaccesspolicy.xml within the root of the WCF web role with the following contents:

download
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="SOAPAction">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

code snippet 076576 Ch10_Code.zip/clientaccesspolicy.xml

19. The WCF service is now ready to go! You can right-click on the AzureCRMProxy project and select Publish to deploy the service to Windows Azure. For more information on publishing to Windows Azure, refer to http://msdn.microsoft.com/en-us/library/ff683672.aspx. You can also right-click on the WCFCRMProxyRole project and select Debug to run the service locally within ASP.NET development server.

note If you publish to Azure with an instance count of 1, you will see a warning in the Azure portal. In order to maintain the uptime guaranteed by Windows Azure SLA, you need to deploy a minimum of two instances. For more information on Windows Azure SLA, see http://www.microsoft.com/windowsazure/sla.

It's a good idea to test this service to ensure it works as expected. You can write a custom command-line application to do the testing, but an easier approach is to use the WcfTestClient.exe included with Visual Studio. WcfTestClient.exe can be found within the folder C:Program Files (x86)Microsoft Visual Studio 10.0Common7IDE. For more information on WcfTestClient.exe, see http://msdn.microsoft.com/en-us/library/bb552364.aspx.

Creating the Silverlight Grid and Chart

With the middle-tier complete, you can now build the user interface components in Silverlight 4:

1. Right-click on the Visual Studio solution and select Add New Project.

2. In the Add New Project dialog, select Silverlight Application from the list of Installed Templates, name the project CRMSLParts, as shown in Figure 10.13, and then click OK.

3. In the New Silverlight Application dialog, select New Web Project to host the test page for the Silverlight control, and then click OK (see Figure 10.14).

Although we won't be using this website as part of the solution, it is very useful for testing and debugging the Silverlight control.

4. Within the Solution Explorer in Visual Studio, right-click on the CRMSLParts project and select Add Service Reference to refer to the WCF proxy service you built earlier. Enter CRMProxy as the namespace for the reference. As shown in Figure 10.15, you're adding the proxy from the local emulator, but you could enter the corresponding URL from Windows Azure if you already have it deployed.

note You could change the URL referred to in step 4 at a later time by right-clicking on CRMProxy within Solution Explorer ⇒ Configure Service Reference and entering the updated URL.

5. To the same project, also add references to the following four assemblies: System.Windows.Controls.Data.dll and System.Windows.Controls.DataVisualization.Toolkit.dll from the Silverlight 4 Toolkit, and Microsoft.SharePoint.Client.Silverlight.dll and Microsoft.SharePoint.Client.Silverlight.Runtime.dll from the SharePoint SDK.

You can find Silverlight 4 Toolkit assemblies within the folder C:Program Files (x86)Microsoft SDKsSilverlightv4.0ToolkitApr10Bin. The SharePoint SDK assemblies are located in C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions14TEMPLATELAYOUTSClientBin.

6. The next step is to write the code to define the classes required to represent the customer data within the Silverlight project. Create a new folder called Model within the project and add a new C# class called Account.cs to it. Enter the following code within that file:

download
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

using System.Collections;
using System.Collections.Generic;

namespace CRMSLParts
{
  public class Account : INotifyPropertyChanged
  {
    public List<Opportunity> AccOpps;
    private bool _IsDirty = false;
    public bool IsDirty
    {
      get { return _IsDirty; }
      set { _IsDirty = value; }
    }

    private string _AccountID;
    public string AccountID
    {
      get { return _AccountID; }
      set { _AccountID = value; NotifyPropertyChanged("AccountID"); }
    }

    private string _AccountName;
    public string AccountName
    {
      get { return _AccountName; }
      set { _AccountName = value; NotifyPropertyChanged("AccountName"); }
    }

    private string _MainPhone;
    public string MainPhone
    {
      get { return _MainPhone; }
      set { _MainPhone = value; NotifyPropertyChanged("MainPhone"); }
    }

    private string _Email;
    public string Email
    {
      get { return _Email; }
      set { _Email = value; NotifyPropertyChanged("Email"); }
    }

    private string _City;
    public string City
    {
      get { return _City; }
      set { _City = value; NotifyPropertyChanged("City"); }
    }

    private string _State;
    public string State
    {
      get { return _State; }
      set { _State = value; NotifyPropertyChanged("State"); }
    }

    protected void NotifyPropertyChanged(string PropertyName)
    {
      if (null != PropertyChanged)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        this.IsDirty = true;
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;
  }
}

code snippet 076576 Ch10_Code.zip/Account.cs

As shown here, you are designing it so that the data can be updated within the Silverlight code. Although we don't include steps to update CRM when the data changes in Silverlight, it should be easy to accomplish.

7. Similar to step 6, add the Opportunity.cs class to the model folder and enter the following code:

download
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace CRMSLParts
{
  public class Opportunity
  {
    private string _OpportunityID;
    public string OpportunityID
    {
      get { return _OpportunityID; }
      set { _OpportunityID = value; }
    }

    private string _Name;
    public string Name
    {
      get { return _Name; }
      set { _Name = value; }
    }

    private decimal _EstimatedRevenue;
    public decimal EstimatedRevenue
    {
      get { return _EstimatedRevenue; }
      set { _EstimatedRevenue = value; }
    }

  }
}

code snippet 076576 Ch10_Code.zip/Opportunity.cs

8. To add the associated actions upon the data, add AccountManager.cs with the following code:

download
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections.Generic;

using CRMSLParts.CRMProxy;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Collections;

namespace CRMSLParts
{
  public class AccountManager
  {
    public ObservableCollection<Account> Accounts { get; set; }
    public ObservableCollection<Opportunity> Opportunities { get; set; }

    public AccountManager()
    {
      Accounts = new ObservableCollection<Account>();
      Opportunities = new ObservableCollection<Opportunity>();
      FillData();
    }

    private void FillData()
    {
      CRMProxyServiceClient service = new CRMProxyServiceClient();
      service.GetAccountsCompleted += new
          EventHandler<GetAccountsCompletedEventArgs>
          (service_GetAccountsCompleted);
      service.GetAccountsAsync();

    }

    void service_GetAccountsCompleted(object sender,
                                        GetAccountsCompletedEventArgs e)
    {
      IEnumerable<AzureAccount> crmAccounts =
          (IEnumerable<AzureAccount>)e.Result;

      foreach (AzureAccount account in crmAccounts)
      {

        Account currentAccount = new Account()
            {
              AccountID = account.AccountID,
              AccountName = account.AccountName,
              City = account.City,
              Email = account.Email,
              MainPhone = account.MainPhone,
              State = account.State,
              AccOpps = new List<Opportunity>(),
            };
        foreach (AzureOpportunity opp in account.Opportunities)
        {
          currentAccount.AccOpps.Add(new Opportunity()
          {
            Name = opp.Name,
            EstimatedRevenue = opp.EstimatedRevenue
          });
        }
        Accounts.Add(currentAccount);
      }
    }

    public void GetOpportunityPipeline(string accountID)
    {
      Opportunities.Clear();
      Account acc = FindAccountByID(accountID);
      foreach (Opportunity opp in acc.AccOpps)
      {
        Opportunities.Add(opp);
      }
    }

    public Account FindAccountByID(string accountID)
    {
      foreach (Account acc in Accounts)
      {
        if (acc.AccountID.Equals(accountID,
            StringComparison.OrdinalIgnoreCase))
          return acc;
      }
      return null;
    }

    public void UpdateData()
    {
      // Use  Account.IsDirty to determine accounts that have changed
      // Call the Proxy WebService to update the data back into CRMOnline
    }
  }
}

code snippet 076576 Ch10_Code.zip/AccountManager.cs

After you've added the Account.cs, Opportunity.cs, and AccountManager.cs files, your Silverlight project structure should look like what is shown in Figure 10.16.

9. To create the user interface, double-click to open the MainPage.xaml file and view it in XAML view.

10. Enter the following bolded parameters within the <UserControl> XAML tag:

download
<UserControl x:Class="CRMSLParts.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:my="clr-namespace:CRMSLParts"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls .Data"
xmlns:chartingToolkit= "clr-namespace:System.Windows.Controls.DataVisualization .Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit">

code snippet 076576 Ch10_Code.zip/MainPage.xaml

11. Within the <UserControl> tag, enter the following snippet to define the view source:

<UserControl.Resources>
  <my:AccountManager x:Key="accountManagerViewSource" />
</UserControl.Resources>

note The WPF Designer in Visual Studio might show an unhandled exception at this point, but you can safely ignore it.

12. Replace the <Grid> tag in XAML with the following code, where you'll be creating the data grid, buttons, and chart. These controls will be laid out within the grid using a set of stack panels:

download
<Grid x:Name="LayoutRoot" Background="White">
  <StackPanel Orientation="Vertical" Margin="15">
    <data:DataGrid AutoGenerateColumns="False"
                  DataContext="{Binding Path=Accounts,
                  Source={StaticResource accountManagerViewSource}}"
                  ItemsSource="{Binding}" SelectionChanged="dg_SelectionChanged"
                  HorizontalAlignment="Left" Margin ="5" Height="300"  Name="dg"
                  IsReadOnly="False" HeadersVisibility="Column"
                  Grid.Row="0" Grid.Column="0" >
      <data:DataGrid.Columns>
        <data:DataGridTextColumn Binding="{Binding AccountName}"
                                        Header="Account Name" />
        <data:DataGridTextColumn Binding="{Binding MainPhone}"
                                        Header="Phone" />
        <data:DataGridTextColumn Binding="{Binding Email}"
                                        Header="Email" />
        <data:DataGridTextColumn Binding="{Binding City}" Header="City" />
        <data:DataGridTextColumn Binding="{Binding State}"
                                        Header="State" />
      </data:DataGrid.Columns>
    </data:DataGrid>
    <StackPanel Orientation="Horizontal" Margin="3" Grid.Row="1">
      <Button Margin="3" Content="Save Changes" x:Name="btnSave"
                  Click="btnSave_Click"/>
      <Button Margin="3" Content="Escalate Account" x:Name="btnPublishToSP"
                  Click="btnPublishToSP_Click"/>
    </StackPanel>
    <chartingToolkit:Chart HorizontalAlignment="Left" Margin="12,12,0,0"
                              Name="chart1" Title="Opportunity Pipeline"
                              VerticalAlignment="Top" Height="276" Width="520">
      <chartingToolkit:Chart.Series>
        <chartingToolkit:ColumnSeries Title="Revenue"
                                            ItemsSource="{Binding}"
                          IndependentValueBinding="{Binding Name}"
                          DependentValueBinding="{Binding EstimatedRevenue}"/>
      </chartingToolkit:Chart.Series>
    </chartingToolkit:Chart>
  </StackPanel>
</Grid>

code snippet 076576 Ch10_Code.zip/MainPage.xaml

13. Right-click the SelectionChanged within the <DataGrid> tag to automatically generate the C# event handler within the file MainPage.xaml.cs, and then enter the following code:

download
private void dg_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  AccountManager dataObject = this.Resources["accountManagerViewSource"]
                                      as AccountManager;
  dataObject.GetOpportunityPipeline((dg.SelectedItem as Account).AccountID);
}
private void btnPublishToSP_Click(object sender, RoutedEventArgs e)
{
  // To be implemented.
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
  // To be implemented.
}

code snippet 076576 Ch10_Code.zip/MainPage.xaml.cs

This code enables the chart to show the corresponding opportunity data when an account is selected in the grid.

Warning

For readability, we have written some of the business logic code in MainPage.xaml.cs. In real-world implementations, it is recommended to follow the Model-View-ViewModel (M-V-VM) pattern.

14. Within the MainPage class, add the following bolded code to wire up the initial Loaded event:

download
public MainPage()
{
  InitializeComponent();
  Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
  AccountManager dataObject = this.Resources["accountManagerViewSource"]
                                      as AccountManager;
  ColumnSeries column = chart1.Series[0] as ColumnSeries;
  column.ItemsSource = dataObject.Opportunities;
}

code snippet 076576 Ch10_Code.zip/MainPage.xaml.cs

15. The Silverlight control is almost ready. You can test it by right-clicking on the CRMSLParts.Web project and choosing Debug ⇒ Start New Instance to verify.

The next step is to wire the Silverlight control with the SharePoint web part.

16. Right-click on the solution and add a new project. Choose Empty SharePoint Project and name it CRMSPOWebPartProj, as shown in Figure 10.17, and then click OK.

17. In the dialog that appears, enter the URL of the SharePoint site you will be using and deploy it as a sandboxed solution. Click OK.

note The URL can point to your on-premises development environment, but because you're creating it as a sandboxed solution, it works with both SharePoint 2010 Online and SharePoint On-Premises.

18. Right-click on the SharePoint project and select Add ⇒ New Item. In the Add New Item dialog, create a new module named XAPModule (see Figure 10.18), and click Add.

19. Delete the Sample.txt file within the XAPModule folder in Solution Explorer and add the following contents to the Elements.xml file:

download
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="XAPModule" Url="XAP_Bin">
    <File Path="CRMSLParts.xap" Url="CRMSLParts.xap" />
  </Module>
</Elements>

code snippet 076576 Ch10_Code.zip/Elements.xml

20. Right-click on XAPModule in Solution Explorer, select Add ⇒ Existing Item, and then browse to the folder containing the XAP output of the Silverlight project you created earlier. The folder can be found within the solution under WroxCRMOnlineWebpartCRMSLPartsBinDebug.

21. Find the file CRMSLParts.xap within that folder and instead of clicking the Add button, click the downward arrow next to the button and select Add As Link, as shown in Figure 10.19.

note This step enables the SharePoint web part to automatically refer to the latest version of the Silverlight control if you make any changes to it later.

22. Create a new web part called CRMDashboard. To do so, right-click on the project and select Add New Item, and then navigate to the SharePoint 2010 node and select Web Part. In the CRMDashboard.cs file, add the following code.

Note that you'd have to replace the reference to intranet.contoso.com within this code to your SharePoint instance that you chose earlier in step 17.

download
using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace CRMSPOWebPartProj.CRMDashboard
{
  [ToolboxItemAttribute(false)]
  public class CRMDashboard : WebPart
  {
    protected override void CreateChildControls()
    {
      string slStreamCode = "<div id="slApp"/>" +
      "<script language="JavaScript" type="text/javascript">" +
      "var slSPDIV = document.getElementById(‘slApp’);" +
      "slSPDIV.appendChild(slSPDIV);" +
      "slSPDIV.innerHTML =" +
      "‘<object data="data:application/x-silverlight," " +
      " type="application/x-silverlight" width="740" height="800">" +
      "<param name="source" " +
      "value="http://intranet.contoso.com/XAP_Bin/CRMSLParts.xap"/> " +
      "<param name="initParams" value="MS.SP.url=" +
      SPContext.Current.Site.Url + "" /></object>’;" +
      "</script>";

      this.Controls.Add(new LiteralControl(slStreamCode));

      base.CreateChildControls();
    }
  }
}

code snippet 076576 Ch10_Code.zip/CRMDashboard.cs

At this point, you merely have a Silverlight control surfaced in a SharePoint web part. In the next step, you'll wire up this web part to connect to a SharePoint custom list.

23. In your SharePoint site, create a new custom list called CRM Escalations with the columns Title, Phone, and Email (see Figure 10.20).

24. Go back to the CRMSLParts Silverlight project and connect it with this custom SharePoint list. Open the MainPage.xaml.cs and add the following code highlighted in bold:

download
using Microsoft.SharePoint.Client;

namespace CRMSLParts
{
  public partial class MainPage : UserControl
  {
    List CRMEscalations;
    Account selectedAccount;

    public MainPage()
…

code snippet 076576 Ch10_Code.zip/MainPage.xaml.cs

25. Add the following code within the MainPage class to wire up the btnPublishToSP_Click handler.

Again, remember to replace the reference to intranet.contoso.com within this code to the SharePoint instance that you chose earlier in Step 17.

download
private void btnPublishToSP_Click(object sender, RoutedEventArgs e)
{
  ClientContext context;

  if (App.Current.IsRunningOutOfBrowser)
  {
    context = new ClientContext("http://intranet.contoso.com");
  }
  else
  {
    context = ClientContext.Current;
  }

  //Get the list
  CRMEscalations = context.Web.Lists.GetByTitle("CRM Escalations");
  context.Load(CRMEscalations);

  //Create the escalation list item
  ListItemCreationInformation itemCreateInfo =
      new ListItemCreationInformation();
  ListItem listItem = CRMEscalations.AddItem(itemCreateInfo);
  selectedAccount = dg.SelectedItem as Account;
  listItem["Title"] = selectedAccount.AccountName;
  listItem["Phone"] = selectedAccount.MainPhone;
  listItem["Email"] = selectedAccount.Email;
  listItem.Update();

  //Make the call to the SharePoint server
  context.ExecuteQueryAsync(
              succeededCallback,
              failedCallback);

}

void succeededCallback(object sender, ClientRequestSucceededEventArgs args)
{
  this.Dispatcher.BeginInvoke(() =>
  {
    MessageBox.Show(selectedAccount.AccountName +
        " has been escalated to the Senior Leadership Team");
  });

}
void failedCallback(object sender, ClientRequestFailedEventArgs args)
{
  this.Dispatcher.BeginInvoke(() =>
  {
    MessageBox.Show(
        string.Format("Failed - msg:{0} errcode:{1} stackTrace:{2}",
        args.Message, args.ErrorCode, args.StackTrace));
  });
}

code snippet 076576 Ch10_Code.zip/MainPage.xaml.cs

In the preceding snippet, you are using the client object model from the SharePoint SDK to connect CRM Online data and the custom SharePoint list through the UI. Whenever a user clicks the Escalate Account button in the UI, the code adds the customer account details from CRM Online to the SharePoint custom list.

note In the preceding code snippet, we assume that the CRM Escalations list is available at the top site collection level; hence, we refer to it using the context.Web.Lists collection. If the CRM Escalations list is created in a sub-site, modify the code snippet to load context.Web.Webs and refer to the list within the context.Web.Webs[subsiteIndex].Lists collection.

26. Right-click on the SharePoint project and select Package to bundle your SharePoint web part as a .wsp solution file. If you were using the local Azure emulator up until now, you need to deploy your project to Windows Azure before you package and publish your solution. You can upload this .wsp file to the SharePoint Online solution gallery, after which it will be available for use in SharePoint Online pages.

You now have a working Silverlight web part capable of running in SharePoint Online and able to bring data from CRM Online. You can extend this coding pattern to bring data from other LOB data sources in your organization.

Bringing It All Together

At this point, you are done coding the solution. Now it's time to build a dashboard page that hosts all the web parts you have built until now.

1. Log in to your SharePoint Online site and create a new web part page with a three-column layout (header, right column, and body), as illustrated in Figure 10.21.

2. Click the Page Ribbon menu and select Edit Page.

3. Click the Add a Web Part hyperlink. This will open the Page Tools ribbon menu, as shown in Figure 10.22.

4. Choose Web Part followed by Custom (under Categories) to select the custom web part that you built in this chapter, and then click the Add button (ensuring the drop-down next to it is set to “Body”) to place it in the body zone of the page.

5. Similar to step 4, add web parts for rest of the artifacts (e.g., the document library for accounts and the CRM Escalations custom list) that you created earlier in this chapter. After you have added all the web parts to the page, the layout of the page should resemble Figure 10.23.

The Silverlight web part will display the list of accounts from CRM momentarily. Click through those accounts to see the opportunity pipeline charts changing accordingly.

6. With an account selected, you can click the Escalate Account button, which will add it to the escalations list. Refresh the page to see the new record in the CRM Escalations web part. You can also create a simple workflow on the CRM Escalations list to e-mail the escalation team, if necessary.

Congratulations! You have now created a nice end-to-end customer information dashboard in SharePoint Online.

Summary

SharePoint Online provides a flexible platform that enables you to create composite applications rapidly. CRM Online complements SharePoint Online by providing an easy way for developers to extend it and create compelling integrated applications. Furthermore, the APIs of SharePoint and CRM are streamlined so that the methods described in this chapter can also be used to build similar applications for local, on-premises–based software. The simple solution we walked through in this chapter provides a good foundational pattern that you can build on to create much more complex, advanced solutions. For example, using the example described in this chapter, you could consider ways to extend this solution to include more heterogeneous LOB data sources, creating a one-stop shop for your customers and business users.

Additional References

Following are some additional references that you might find useful:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.22.161