Wednesday, July 30, 2008

BDC to query a WebService using LINQ

In this post I'll describe how to connect your BDC definition to an external WebService. I'm assuming that you are using Visual Studio 2008, a SQL 2005 database hosting the Northwind database on the local server, MOSS 2007 and you've got the SharePoint SDK installed. Also this is a quick demo so I'll be connecting to debug instance of the webservice and not creating a permanent site as that's not really the point of this blog.

Stage 1: Building the WebService



Open Visual Studio 2008 and create a new WebSite Project of type "ASP.NET Web Service" called "Store". It should be noted at this stage that WCF is not fully supported in BDC although basicHTTP protocol is allowed. This will create a standard Service.asmx file with a default HelloWorld method.


Add a LINQ connection to your database using "Tools/Connect to Database" setting the server name to "(local)", Log on to the server as "Use Windows Authentication" and Database Name as "NORTHWIND". Test that the connection is working fine and Click OK.


Within your project Visual Studio, right click the Project file and select "Add New Item" and select a "Linq to SQL Classes" called Products.dbml. If you get the message regarding placing the file within the APP_Code folder, click Yes.


Within Visual Studio drag the "Product" table from the ServerExplorer to the Products design window. This should display a list of all fields in the table. This is DBML file contains all the code needed to connect to the database and query the table. Save the file and close the designer window.


Now we need to create a simple structure to pass this information back over the HTTP protocol. We do this by building a class containing only those fields we want to pass. Within your project Visual Studio, right click the Project file and select "Add New Item" and select a "Class" called ProductProxy.cs. If you get the message regarding placing the file within the APP_Code folder, click Yes. Replace the default code with the following:

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

/// <summary>
/// Summary description for Product
/// </summary>
public class ProductProxy
{
private int productIDField;
private string productNameField;
private decimal unitPriceField;
private double unitSalesPriceField;
private int unitsInStockField;

/// <remarks/>
public int ProductID {
get {
return this.productIDField;
}
set {
this.productIDField = value;
}
}

/// <remarks/>
public string ProductName {
get {
return this.productNameField;
}
set {
this.productNameField = value;
}
}
/// <remarks/>
public decimal UnitPrice
{
get
{
return this.unitPriceField;
}
set
{
this.unitPriceField = value;
}
}

/// <remarks/>
public int UnitsInStock
{
get
{
return this.unitsInStockField;
}
set
{
this.unitsInStockField = value;
}
}

/// <remarks/>
public double UnitSalePrice
{
get
{
return this.unitSalesPriceField;
}
set
{
this.unitSalesPriceField = value;
}
}

}

Now we create the web method that connects everything together. Open the Service.cs file and replace the existing code with the following:

using System;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public Service () {}

[WebMethod]
public ProductProxy[] ProductList()
{
ProductDataContext db = new ProductDataContext();
var query = from p in db.Products
select p;

ProductProxy[] prods = new ProductProxy[query.Count()];
int record = 0;

foreach (var q in query)
{
ProductProxy prod = new ProductProxy();
prod.ProductID = q.ProductID;
prod.ProductName = q.ProductName;
prod.UnitPrice = q.UnitPrice.Value;
prod.UnitsInStock = q.UnitsInStock.Value;
prod.UnitSalePrice = (double)q.UnitPrice.Value * 1.10;
prods[record] = prod;
record++ ;
}

return prods;
}

}

At this point we test the WebMethod by running the application in Visual Studio 2008 debug mode. Choose Debug/Start Debugging, if you get a message about modifying the web.config to allow debugging select "OK" or that ScriptDebugging in InternetExporer select "Yes". Once the HTML page appears, click the Service.asmx link, note the URL displayed as we'll use this later, then click the ProductList link and then the "Invoke" button. The results of the query should be displayed on screen in XML format.



Stage 2: Developing the BDC Mapping


Start the Business Data Catalog Definition Editor which is available when you install the SharePoint SDK. From the application tool bar click the "Add LOB System" then click the "Connect to Webservice" and type the URL of the debug instance noted previously (in my case it was "http://localhost:3534/Store/Service.asmx", however your port number could be different. Clicking Connect should present you with a blank deisng surface with a toolbar on the right hand side of the screen. From this toolbar click "Add Web Method" and drag the "ProductList" method onto the design surface. Finally click the OK button and choose the standard "WebServiceLobSystem" as the name and click OK.



We need to create a Method Instance in order to test that or generated mapping is operating correctly. Browse to the Entities/Entity0/Methods/ProductList/Instances node and click "Add Method Instance" on the toolbar. Choose "Finder" as the Method Instance Type and click OK. While the MethodInstance0 is selected choose "Execute" from the tool bar and click the "Execute" button from the dialogue box displayed, should show a set of results. Then close the window.


Select the top node "WebServiceLobSystem" on teh left hand side of the screen. Click "Export" from the tool bar and save the XML file to your desktop as "BDC_WS_Example".

Stage 3: Importing to SharePoint


Open SharePoint 3.0 Central Administration from the Administrative Tools folder on your sever. Browse to the Share Services Administration window and select the SharedServices that runs your site, in my case it was the default SharedService1. Then select the "Import application definition" link.

Click "Browse" and navigate to the BDC xml file we created in the previous stage and click "Import". All going well you should be told that the application was imported successfully. You will see a warning message saying "No method instance of type SpecificFinder defined for for application 'WebServiceLobSystem', entity 'Entity0'. Profile page creation skipped", which is just saying that we have not defined a method, but for it will not effect this demo. Just click OK and you should see the application details shown on screen. You are now ready to implement the BDC object inside your SharePoint site.

Browse to your SharePoint website, in my case http://sharepoint1/Pages/Default.aspx, and create a new "Blank Web Part Page" called Products. Add a new "Business Data List" webpart In any of holding areas on the page.


Open the tool pane by clicking on the link within the new webpart. Browse to the Type you wish by clicking the book icon shown. Select the "WebServiceLobSystem_Instance" which is the name provided in the previous stage and click OK. Then click OK on the SharePoint WebPart tool bar shown on the right of teh screen.


At this point the details can be seen on screen.


Now you have a working demo feel free to play around with the various settings and options within SharePoint and the BDC Editor.

The End.