Create an External Content Type by Using Web Services and
BCS
-
Open your solution and select Add | New Project. Select
the SharePoint Templates category, and then select Empty
SharePoint Project. Name the project CustomerBDCModel and then click
OK. -
Select the farm-level solution and make sure that the
SharePoint site is set to the correct site. Click Finish to
create the project. -
When the project is created, right-click the project and
select Add | New Item. -
In the New Item dialog box, select the SharePoint
category, and then select the Business Data Connectivity Model
template. Provide a name for the template (such as AzureCustomers), and click OK. -
In the new ECT model, you’ll see two classes by default:
Entity1 and
Entity1Service. When modeling data from external systems, you need both
an in-memory object with which you model the data entities
(Entity1), and a service class that will
transpose that data into something that is usable within
SharePoint (Entity1Service). Rename these
classes so that their names are more descriptive (for example,
CustomerEntity and CustomerEntityService). -
Before going any further, add the service reference to the
WCF service you deployed to Windows Azure in the
previous exercise. To do this, right-click the project, select
Add Service Reference, add the service endpoint in the Address
field, and click Go. When the service definition loads, provide
a namespace (such as AzureSvcReference). Click OK to complete
the process. -
Double-click the CustomerEntity
class, and amend the class code as shown in the following bolded
code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CustomerBDCModel.AzureCustomers
{
public partial class CustomerEntity
{
public string Identifier1 { get; set; }
public string custTitle { get; set; }
public string custFirstName { get; set; }
public string custLastName { get; set; }
public string custEmail { get; set; }
public string custPhone { get; set; }
}
} -
Then open the CustomerEntityService
class, and amend the code as shown in bold here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Activation;
using System.ServiceModel;
using CustomerBDCModel.AzureSvcReference;
namespace CustomerBDCModel.AzureCustomers
{
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class CustomerEntityService
{
public static CustomerEntity ReadItem(string id)
{
BasicHttpBinding mySvcbinding = new BasicHttpBinding();
UriBuilder serviceURI = new UriBuilder(
"http://<your servicenamespace>.cloudapp.net/GetCustomers.svc");
GetCustomersClient myWCFProxy = new GetCustomersClient(
mySvcbinding, new EndpointAddress(serviceURI.Uri));
var myCustomer = myWCFProxy.getACustomer(Int32.Parse(id));
CustomerEntity entyCustomer = new CustomerEntity();
entyCustomer.Identifier1 = myCustomer[0].custEmail.ToString();
entyCustomer.custTitle = myCustomer[0].custFirstName.ToString();
entyCustomer.custFirstName = myCustomer[0].custID.ToString();
entyCustomer.custLastName = myCustomer[0].custLastName.ToString();
entyCustomer.custEmail = myCustomer[0].custPhone.ToString();
entyCustomer.custPhone = myCustomer[0].custTitle.ToString();
myWCFProxy.Close();
return entyCustomer;
}
public static List<CustomerEntity> ReadList()
{
BasicHttpBinding mySvcbinding = new BasicHttpBinding();
UriBuilder serviceURI = new UriBuilder(
"http://<yourservicenamespace>/GetCustomers.svc");
GetCustomersClient myWCFProxy = new GetCustomersClient(
mySvcbinding, new EndpointAddress(serviceURI.Uri));
var salesData = myWCFProxy.getAllCustomers();
List<CustomerEntity> mySalesInfoList = new List<CustomerEntity>();
foreach (var item in salesData)
{
CustomerEntity tempEntity = new CustomerEntity();
tempEntity.Identifier1 = item.custID;
tempEntity.custTitle = item.custTitle;
tempEntity.custFirstName = item.custFirstName;
tempEntity.custLastName = item.custLastName;
tempEntity.custEmail = item.custEmail;
tempEntity.custPhone = item.custPhone;
mySalesInfoList.Add(tempEntity);
}
myWCFProxy.Close();
return mySalesInfoList;
}
}
}
Note the two methods in the
CustomerEntityService class named
ReadItem and ReadList.
These are the two default core methods required for creating an
external list programmatically. They translate to the end-user
experiences of loading an external list in SharePoint (the
ReadList method) and clicking a specific
list item that loads in a dialog box (the
ReadItem method). In other words, these
methods define the data to be surfaced within the list item view and
list view experiences; SharePoint handles the rest. This is
powerful because it allows you to focus on constructing the data
to be surfaced in SharePoint from custom sources (in this case,
a service endpoint that resides in Windows Azure) without having
to worry about most of the UI infrastructure and other events
around that data construction. (Of course, you could create
custom dialog boxes for your lists, but the point is that it’s
not absolutely necessary that you do so.)
Note that the ReadItem method
initially configures and creates an instance of the WCF service in code. In contrast to editing the
web.config file, this method allows you to set the service
binding and endpoint in the code programmatically. You then use
a var (myCustomer) to hold the list item
data returned by the call to the
GetACustomer method—which takes the ID
passed as a parameter by the calling method and returns the
record corresponding to that entity. After you have the data
returned in the weakly typed var, it’s a
matter of creating a new CustomerEntity
class and mapping properties.
In the ReadList method, the returned
data object is a list collection of
CustomerEntity objects. Here again, you’re
calling the WCF service, iterating through the returned data,
and then using the CustomerEntity class to
construct a list collection. The list collection is the data
that creates the list view for the user when the external list
first loads. -
Now that you’re done with the coding, open the BDM Explorer by double-clicking the .bdcm file
(for example, AzureCustomers.bdcm). The BDC Explorer should
open, as shown in the following graphic, and you’ll be able to
edit the ECT in the designer. The goal here is to ensure that
the TypeDescriptors (which describe the
entity types in the ECT) match your custom class. So in this
example, you need to ensure that you have six
TypeDescriptors, all of type
string, that map in name to the
CustomerEntity class you created earlier in
the exercise. If you’re following along in code, you’ll want to
make sure that your BDC Explorer hierarchy looks the same as the
one shown in this graphic. Note the Properties window on the
right side; you can edit the name and type for each of the
TypeDescriptors. -
To add TypeDescriptors, right-click
the CustomerEntity under
returnParameter and select Add Type
Descriptor, as shown in the following graphic. This adds a node
to the entity hierarchy, which you can then edit by
clicking the type descriptor and editing the properties in the
Properties window. Add a TypeDescriptor for
each property in the CustomerEntity class,
making sure that the type name for each of the properties is set
to System.String and the name of the type
descriptor is the same as the class property. Note that if these
aren’t mapped correctly, SharePoint will throw an error when you
deploy and try to load the external list.
At this point, you are ready to deploy the ECT to
SharePoint. However, before you do so, it might help you to see
the underlying XML that you’ve just created with the .NET
Framework assembly project. To do this, right-click the .bdcm file
(for example, AzureCustomers.bdcm), select Open With, and then
select XML (Text Editor). What will open is the underlying ECT
in XML format.
In the ECT file that follows, you can see that the
LobSystem type is
DotNetAssembly (because you’re using Visual
Studio and the BDC Model template to build the ECT). You can
also see other key elements that make up this ECT, such as the
LobSystemInstance name
(AzureCustomers), the namespace,
properties, identifiers, and so on. In essence, all of the key
information for your ECT resides in this file, which corresponds
to the WSP that will be deployed to SharePoint. Furthermore, if
you were to add more methods (such as the
Create or Update
method), you would have more information in the ECT file than
what is shown here:
<?xml version="1.0" encoding="utf-8"?>
<Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog"
Name="AzureCustomers">
<LobSystems>
<LobSystem Name="AzureCustomers" Type="DotNetAssembly">
<LobSystemInstances>
<LobSystemInstance Name="AzureCustomers" />
</LobSystemInstances>
<Entities>
<Entity Name="CustomerEntity" Namespace="CustomerBDCModel.AzureCustomers"
EstimatedInstanceCount="1000" Version="1.0.0.22">
<Properties>
<Property Name="Class" Type="System.String">
CustomerBDCModel.AzureCustomers.CustomerEntityService,
AzureCustomers</Property>
</Properties>
<Identifiers>
<Identifier Name="Identifier1" TypeName="System.String" />
</Identifiers>
<Methods>
<Method Name="ReadList">
<Parameters>
<Parameter Direction="Return" Name="returnParameter">
<TypeDescriptor TypeName=
"System.Collections.Generic.IEnumerable'1
[[CustomerBDCModel.AzureCustomers.CustomerEntity,
AzureCustomers]]" IsCollection="true" Name="Entity1List">
<TypeDescriptors>
<TypeDescriptor TypeName=
"CustomerBDCModel.AzureCustomers.CustomerEntity,
AzureCustomers" Name="CustomerEntity">
<TypeDescriptors>
<TypeDescriptor TypeName="System.String"
IdentifierName="Identifier1" Name="Identifier1" />
<TypeDescriptor TypeName="System.String" Name="custTitle" />
<TypeDescriptor Name="custFirstName"
TypeName="System.String" />
<TypeDescriptor Name="custLastName"
TypeName="System.String" />
<TypeDescriptor Name="custPhone" TypeName="System.String" />
<TypeDescriptor Name="custEmail" TypeName="System.String" />
</TypeDescriptors>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Type="Finder" ReturnParameterName="returnParameter"
Default="true" Name="ReadList" DefaultDisplayName="Entity1 List" />
</MethodInstances>
</Method>
<!-- end finder method -->
<!-- start specific finder method -->
<Method Name="ReadItem">
<Parameters>
<Parameter Direction="In" Name="id">
<TypeDescriptor TypeName="System.String"
IdentifierName="Identifier1" Name="Identifier1" />
</Parameter>
<Parameter Direction="Return" Name="returnParameter">
<TypeDescriptor
TypeName="CustomerBDCModel.AzureCustomers.CustomerEntity,
AzureCustomers" Name="CustomerEntity">
<TypeDescriptors>
<TypeDescriptor TypeName="System.String"
IdentifierName="Identifier1" Name="Identifier1" />
<TypeDescriptor TypeName="System.String" Name="custTitle" />
<TypeDescriptor Name="custFirstName" TypeName="System.String" />
<TypeDescriptor Name="custLastName" TypeName="System.String" />
<TypeDescriptor Name="custEmail" TypeName="System.String" />
<TypeDescriptor Name="custPhone" TypeName="System.String" />
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Type="SpecificFinder"
ReturnParameterName="returnParameter" Default="true" Name="ReadItem"
DefaultDisplayName="Read Entity1" />
</MethodInstances>
</Method>
</Methods>
</Entity>
</Entities>
</LobSystem>
</LobSystems>
</Model>
The key point here is that you should not only feel a
sense of familiarity with the ECT, you should now have an
additional place to check in case there are errors with your ECT
in SharePoint. One of the most common errors is poorly-mapped
type descriptors and entity properties, and this XML file
provides you with another way to verify that information is
properly formatted and mapped. Fortunately, Visual Studio
automatically builds this file for you as the core ECT, so you
should use the designer experience first and this file as a
secondary way to debug your ECTs. -
To deploy the project, right-click the BDC Model project
and select Deploy. -
Open your SharePoint site, click Site Actions | View All
Site Content, and select Create. -
Click List, and then select External List. Click Create,
provide a name for the list (such as Azure
Customers), and then click the Select External Content
Type button (located to the far-right of the External Content
Type field). This loads all of the available ECTs, as shown in
the following graphic. -
Choose the AzureCustomers ECT, and
click OK. -
Click Create to complete the process. This will load the
ECT as you load the external list.
Now that you’ve worked through the two exercises, you know
that loading the ECT triggers a call to the WCF service you deployed to Windows Azure and
retrieves all the customers you created programmatically in the
service class. Figure 1 shows that the
service call that populates the external list returns 10 customers.
The chain of events follows this logic: When you call the
ReadList method, it in turn calls the
GetAllCustomers method in the WCF service.
GetAllCustomers returns the full list
collection of customers created in the service, and then
deserializes in the BDC application. This results in the external
list shown in the figure. (If you see an “Access Denied” message,
you will need to assign permissions to the ECT. You can do this by
opening SharePoint Central Administration and clicking Manage
Service Applications | Business Data Connectivity Service, selecting the ECT (for
example, CustomerEntity), clicking Set Object
Permissions, and adding a valid alias for permission
setting.)
Clicking one of the list items invokes the
ReadItem method, which in turn passes the
Identifier1 value to the WCF service and
returns a single record for display. You’ll see that single entity
displayed in the standard list item dialog box, as shown in Figure 2. You can see
that both in the external list view and the list item view, the
class properties are the field names; in reality, you’d want to
transform these to obfuscate class property names.
As mentioned earlier, you can extend the WCF service and the BDC application to support more
operations. The BDC template natively supports read item, read list,
update, create, and delete methods. You’d of course need to retrofit
your WCF service code to support these as well. These might be more
applicable if you have tied a WCF service to an instance of SQL
Azure, and if your service enables applications to perform
CRUD operations against that SQL Azure instance. Abstracting the CRUD operations
against the SQL Azure instance through a WCF service then enables
you to not only build a more powerful external list against the SQL Azure instance, but also
take advantage of the service layer to allow CRUD operations against
that data from other remote applications or devices, such
as a Windows Phone 7 or a web application.
|