In this blog-post, I’ll walk through how you can create a custom Azure service and then integrate this service with the Business Connectivity Services (BCS), a new feature in SharePoint 2010. The process you’ll follow is:
- Create the custom Azure service;
- Deploy the Azure service into your hosted account;
- Create a new BCS application; and
- Deploy the BCS application into SharePoint 2010.
After you deploy the BCS application to SharePoint, you’ll see the service calls from your Azure service manifest in an external list.
Let’s get started.
Creating the Custom Azure Service
To create the Azure service, you must have your development environment set up. This means having SharePoint, Visual Studio 2010 and Azure tools and SDK all installed. You can optionally install Silverlight as well, but it is not needed for this exercise.
- Open VS 2010, and click File, New, Project and select Cloud.
- Select Windows Azure Cloud Service, provide a name for your service and click OK.
- Select the WCF Worker Role and add this to the right-hand pane, and click OK.
- When VS 2010 creates the project structure, right-click the project and select Add and then Class.
- Call the class Customer and ensure the class is structured as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WCFServiceWebRole1
{
public class Customer
{
public string tempCustID { get; set; }
public string tempCustTitle { get; set; }
public string tempCustFirstName { get; set; }
public string tempCustLastName { get; set; }
public string tempCustEmail { get; set; }
public string tempCustPhone { get; set; }
}
}
- In the Service1.svc class, add the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFServiceWebRole1
{
public class Service1 : IService1
{
List<Customer> myCustomerList = new List<Customer>();
public string[] getACustomer(int custID)
{
generateCustomerData();
string strCustID = custID.ToString();
string[] returnCustomer = new string[6];
var returnListOfData = (from customer in myCustomerList
where customer.tempCustID == strCustID
select customer).ToArray();
foreach (var cust in returnListOfData)
{
returnCustomer[0] = cust.tempCustID;
returnCustomer[1] = cust.tempCustTitle;
returnCustomer[2] = cust.tempCustFirstName;
returnCustomer[3] = cust.tempCustLastName;
returnCustomer[4] = cust.tempCustEmail;
returnCustomer[5] = cust.tempCustPhone;
};
return returnCustomer;
}
public List<Customer> getAllCustomers ()
{
generateCustomerData();
List<Customer> returnListOfCustomers = new List<Customer>();
var returnListOfData = (from customer in myCustomerList
select customer).ToArray();
foreach (var cust in returnListOfData)
{
Customer tempCustomer = new Customer();
tempCustomer.tempCustID = cust.tempCustID;
tempCustomer.tempCustTitle = cust.tempCustTitle;
tempCustomer.tempCustFirstName = cust.tempCustFirstName;
tempCustomer.tempCustLastName = cust.tempCustLastName;
tempCustomer.tempCustEmail = cust.tempCustEmail;
tempCustomer.tempCustPhone = cust.tempCustPhone;
returnListOfCustomers.Add(tempCustomer);
};
return returnListOfCustomers;
}
private void generateCustomerData()
{
Customer cust1 = new Customer();
cust1.tempCustTitle = "Dr.";
cust1.tempCustID = "1";
cust1.tempCustFirstName = "Jane";
cust1.tempCustLastName = "Doe";
cust1.tempCustEmail = "jane.doe@acme.com";
cust1.tempCustPhone = "425-332-1092";
myCustomerList.Add(cust1);
Customer cust2 = new Customer();
cust2.tempCustTitle = "Dr.";
cust2.tempCustID = "2";
cust2.tempCustFirstName = "John";
cust2.tempCustLastName = "Doe";
cust2.tempCustEmail = "john.doe@acme.com";
cust2.tempCustPhone = "425-367-2928";
myCustomerList.Add(cust2);
Customer cust3 = new Customer();
cust3.tempCustTitle = "Dr.";
cust3.tempCustID = "3";
cust3.tempCustFirstName = "Ken";
cust3.tempCustLastName = "James";
cust3.tempCustEmail = "kenj@blueyonder.com";
cust3.tempCustPhone = "425-009-2890";
myCustomerList.Add(cust3);
Customer cust4 = new Customer();
cust4.tempCustTitle = "Mrs.";
cust4.tempCustID = "4";
cust4.tempCustFirstName = "Kerry";
cust4.tempCustLastName = "Smith";
cust4.tempCustEmail = "ksmith@fabrikam.com";
cust4.tempCustPhone = "425-289-1002";
myCustomerList.Add(cust4);
Customer cust5 = new Customer();
cust5.tempCustTitle = "Ms.";
cust5.tempCustID = "5";
cust5.tempCustFirstName = "Kendra";
cust5.tempCustLastName = "Anouille";
cust5.tempCustEmail = "kendra@acme.com";
cust5.tempCustPhone = "425-555-3391";
myCustomerList.Add(cust5);
Customer cust6 = new Customer();
cust6.tempCustTitle = "Mrs.";
cust6.tempCustID = "6";
cust6.tempCustFirstName = "Shanique";
cust6.tempCustLastName = "Smith";
cust6.tempCustEmail = "ssmith@acme.com";
cust6.tempCustPhone = "425-998-2091";
myCustomerList.Add(cust6);
Customer cust7 = new Customer();
cust7.tempCustTitle = "Dr.";
cust7.tempCustID = "7";
cust7.tempCustFirstName = "Ahmed";
cust7.tempCustLastName = "Singh";
cust7.tempCustEmail = "ahmeds@contoso.com";
cust7.tempCustPhone = "425-334-0900";
myCustomerList.Add(cust7);
Customer cust8 = new Customer();
cust8.tempCustTitle = "Dr.";
cust8.tempCustID = "8";
cust8.tempCustFirstName = "Sam";
cust8.tempCustLastName = "James";
cust8.tempCustEmail = "sam.james@contoso.com";
cust8.tempCustPhone = "425-442-8910";
myCustomerList.Add(cust8);
Customer cust9 = new Customer();
cust9.tempCustTitle = "Mr.";
cust9.tempCustID = "9";
cust9.tempCustFirstName = "Steve";
cust9.tempCustLastName = "Smith";
cust9.tempCustEmail = "steve.smith@acme.com";
cust9.tempCustPhone = "425-223-0657";
myCustomerList.Add(cust9);
Customer cust10 = new Customer();
cust10.tempCustTitle = "Dr.";
cust10.tempCustID = "10";
cust10.tempCustFirstName = "Yeo";
cust10.tempCustLastName = "Park";
cust10.tempCustEmail = "y.park@contoso.com";
cust10.tempCustPhone = "425-221-0288";
myCustomerList.Add(cust10);
}
}
}
- Amend the Service contract to look like the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFServiceWebRole1
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string[] getACustomer(int custID);
[OperationContract]
List<Customer> getAllCustomers();
}
}
- When you’re done, click Build to ensure the solution builds successfully.
- Now right-click the project and select Publish.
- Visual Studio will open your Windows Azure developer portal.
- You can use one of your existing services or create a new service by clicking New Service. Click Upgrade if you’re upgrading an existing service.
- Browse to the ..\bin\Publish directory to upgrade the service package file and the cloud service configuration file.
- Click Deploy and then when the service deploys, click Run.
- The service will display Ready and provide you with a valid URL when ready to use.
- Navigate to the URL (appending it with Service1.svc – e.g. http://contoso.cloup.net/Service1.svc).
At this point, you have built and deployed a custom Azure service. The service is self-contained—meaning that it does not leverage anything external to itself (such as an entity data model). For starters, this is good and a simple way to get yourself up and running. In the long-term, it is not exactly realistic as you’ll likely need to deploy dependencies alongside the Azure service or application. An example would be leveraging SQL Azure as your back-end data store thus integrating a data connection or entity data model with the deployed service.
Note: There is a local cloud development fabric that allows you to build and deploy the service. I’ve found it useful to create a test client app to make sure that your service works the way you want it to work—e.g. simple WinForm app—before you deploy your custom service.
Important Note: With the current release of Azure, there are two fixes you need to make sure you have when building custom services for SharePoint. The first is to install the Azure QFE fix, so you can run services on your server. You can find more information about it here: http://code.msdn.microsoft.com/wcfazure/Wiki/View.aspx?title=KnownIssues&referringTitle=Home. To save you some time, you need to: 1) install the QFE first (you can download the Windows 2008 R2 version here), and 2) you need to ensure you amend the configuration settings in your web.config service file (before you deploy it) to include the following:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="LoadBalancedBehavior">
<serviceMetadata httpGetEnabled="true" />
<useRequestHeadersForMetadataAddress>
<defaultPorts>
<add scheme="http" port="81" />
<add scheme="https" port="444" />
</defaultPorts>
</useRequestHeadersForMetadataAddress>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
With the Azure service complete (i.e. steps 1 & 2 complete), you are now ready to move onto steps 3 & 4 in the development process.
Consuming the Azure Service in SharePoint
In this part of the post, you’re going to use the Visual Studio BDC Metadata Model template to integrate with your custom Azure service. What’s interesting about this integration is that once you leverage the BDC template, you can also add other templates to the solution—such as Silverlight projects—to consume the data model you’re deploying into SharePoint.
Let’s continue with the walkthrough.
- Open Visual Studio 2010 and click File, New, and Project.
- Select SharePoint and then select Business Data Connectivity Model.
- Provide a name and location then click OK.
- Open the Entity1.cs file and replace the code with the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AzureBDCDataModel.AzureCustomers
{
public partial class Entity1
{
public string Identifier1 { get; set; }
public string tempCustTitle { get; set; }
public string tempCustFirstName { get; set; }
public string tempCustLastName { get; set; }
public string tempCustEmail { get; set; }
public string tempCustPhone { get; set; }
}
}
- Right-click Service Reference and select Add Service Reference.
- Browse to the Azure service URL by adding the URL in the Service Address field and clicking Go.
- Click the service and add a name for your service and click Add Reference.
- Open the Entity1Service.cs file and replace the code with the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using myAzureSvc = AzureBDCDataModel.AzureCustomerService;
namespace AzureBDCDataModel.AzureCustomers
{
public class Entity1Service
{
public static Entity1 ReadItem(string id)
{
myAzureSvc.Service1Client myWCFProxy = new myAzureSvc.Service1Client();
string[] returnedData = new string[6];
returnedData = myWCFProxy.getACustomer(Int32.Parse(id)).ToArray();
Entity1 entity1 = new Entity1();
entity1.Identifier1 = returnedData[0];
entity1.tempCustTitle = returnedData[1];
entity1.tempCustFirstName = returnedData[2];
entity1.tempCustLastName = returnedData[3];
entity1.tempCustEmail = returnedData[4];
entity1.tempCustPhone = returnedData[5];
myWCFProxy.Close();
return entity1;
}
public static List<Entity1> ReadList()
{
myAzureSvc.Service1Client myWCFProxy = new myAzureSvc.Service1Client();
var salesData = myWCFProxy.getAllCustomers();
List<Entity1> mySalesInfoList = new List<Entity1>();
foreach (var item in salesData)
{
Entity1 tempEntity = new Entity1();
tempEntity.Identifier1 = item.tempCustID;
tempEntity.tempCustTitle = item.tempCustTitle;
tempEntity.tempCustFirstName = item.tempCustFirstName;
tempEntity.tempCustLastName = item.tempCustLastName;
tempEntity.tempCustEmail = item.tempCustEmail;
tempEntity.tempCustPhone = item.tempCustPhone;
mySalesInfoList.Add(tempEntity);
}
myWCFProxy.Close();
return mySalesInfoList;
}
}
}
- Open the BDC Explorer and ensure the TypeDescriptors in the Entity1 node map to the Entity1 object—see the following figure. You add TypeDescriptors by right-clicking nodes and selecting Add TypeDescriptor. Make sure that when you add the TypeDescriptor, the name maps to that in the Entity1 class and the type also matches (e.g. string variable is System.String).
- Right-click the project and select Deploy.
- When the project deploys successfully, navigate to your SharePoint site.
- Click Site Actions and View All Site Content. Select Create and then click Lists.
- Select External List and then click Create.
- Provide a name for the external list, and then browse to the external content types.
- You will find the new external content type you just added in the list of external content types.
- After you select one of the external content types, click OK. What should result should be similar to below—a list of fictional customer names and contact information.
At this point, you have built and deployed a simple Azure service, and you have further consumed that service within a BCS application and then deployed that into SharePoint. A couple of notes. You may find that when you deploy the BCS application into SharePoint that when you first load the external list it may not display. This is because BCS has a discrete set of permissions available for you to control read/write permissions for that list.
To ensure you have the appropriate level of permissions, navigate to SharePoint Central Administration and select Manage Service Applications and Business Data Connectivity Service.
Here, you will see a list of the services you have deployed into SharePoint. Click on the one you just deployed, and then select Set Object Permissions.
You can then add the permissions here by adding an alias (Active Directory alias) in the appropriate field and clicking Add. You then associate the specific levels of permission with that person.
Now in this example, you’re calling a service that only has read-only operations. However, you will leverage SQL Azure at some point (which has a separate set of username and password credentials) and in this case would also need to create a service instance in the Secure Store Service (SSS) to manage your communication with SQL Azure.
Note: I will provide some details in a future post on this, but in the meantime, you can check out an earlier post where I’ve got some rough demo notes: http://blogs.msdn.com/b/steve_fox/archive/2010/06/09/sharepoint-2010-amp-windows-azure-how-they-play-together.aspx.
So, that’s it for your first custom Azure service integration. While you used the BDC Metadata model in this post, remember that there are a number of other ways to integrate with SharePoint. In fact, once you build and deploy the custom Azure service you could then take that same service and integrate it with other applications and SharePoint artifacts. For example, you could create a Visual Web part project and then call the getAllCustomers method and bind the results to a Datagrid object. The diagram below illustrates just some of the more common ways of integrating with SharePoint 2010.
You’re going to see as I post more on Azure and SharePoint that there is a ton of potential here when marrying these two technologies. And as more companies evolve towards the cloud, you will see many different solutions that play well with SharePoint.
Happy Coding!