Excel Services Interface
Excel Services provides two interfaces , Web Interface that produces DHTML in the browser window and does not require the client to be installed.
The second interface and the one I'm interested in for the purpose of this sample is the Web Services Interface.
I'll create a simple windows forms application to see how things work and if it could actually replace a bespoke system in relation to performance.
Create a Web Reference
In typical Model View Controller(MVC) style I have created a windows form with a separate assembly that houses all the interaction logic with the Web Service. In Visual Studio I simply added a reference by Selecting References -> Add Web Reference and in the URL location I provided the detail of my site (hosted on a Virtual PC connected with the Loop back Adapter).
http://{ServerName}:{PortNumber}/sites/{SiteCollection}/_vti_bin/excelservice.asmx?WSDL
Once the reference is created you need to add the using Directive into your class. This should take the form of your default name space extended by the name you assigned to the Web Reference.
Done , and ready to move on !
Create the View Interface
For communication between my windows form(View) and the assembly (Controller) I have created an Interface that allows me to retrieve and set values on the form. Very basic but it makes sense to do it in this fashion as this application can now easily be migrated to a Web Form, a bespoke Web Services or might deliver a service as part of an application.
Authentication
Because we have one Computer , my laptop , in a a different domain from my Virtual PC I needed to find a way of providing credentials. For this I created a simple internal Class called creds that implements the interface
System.Net.ICredentials
Required when you call the Web Service. See below
/// <summary>
/// Class that represents Basic Authentication Credentials (Machines are not on the same Domain)
/// </summary>
internal class creds : System.Net.ICredentials
{
#region ICredentials Members
/// <summary>
/// Default Implementation
/// </summary>
/// <param name="uri"></param>
/// <param name="authType"></param>
/// <returns></returns>
public System.Net.NetworkCredential GetCredential(Uri uri, string authType)
{
return new System.Net.NetworkCredential(@"Administrator", "Password1", "win2003-sql2005");
}
#endregion
}
Getting the Web Service to do stuff
Next we create the Excel Service object
ExcelService es = new ExcelService();
we then set the Credentials property on the Excel Services object just created, thus passing the detail contained in the internal class for Authentication.
es.Credentials = (
new creds() as System.Net.ICredentials);
Excel Services maintains session detail for the purpose of caching external detail etc. I'll attempt to discuss this later, but it essentially to enhance performance. Therefore we need to maintain the session ID when we want to execute logic in the Session. For this , and experimental reasons , i maintain the ID in my windows form by setting on the Interface I have created earlier on.
if (loanDetail.SessionID == null)
{
loanDetail.SessionID = es.OpenWorkbook(m_targetWorkbookPath,
string.Empty, string.Empty, out outStatus);
}
We are now ready to interact with the workbook, in the following section i explore 2 of 3 options to set values in the workbook.
Setting values on the Workbook
One option I would probably never use is to set range coordinates
RangeCoordinates
rangeCoordinates = new RangeCoordinates();
you can see how this could get messy very quickly and error prone.
Named Ranges created in the workbook can be used from code as I have done. Options are to set the ranges like parameters one at a time , or pass the whole range as an array. When setting the synchronously that means a web call for every Named Range. I experienced problems when setting a number of values asynchronously where some values are not set by the time I calculate the workbook.
Option 1
//Set the Values one at a time
//Sync web call for each value
//es.SetCellA1(sessionId, "Calculator", "Loan_Amount", loanDetail.LoanAmount);
//es.SetCellA1(sessionId, "Calculator", "Deposit", loanDetail.Deposit);
//es.SetCellA1(sessionId, "Calculator", "Interest_Rate", (loanDetail.Interest/100) );
//es.SetCellA1(sessionId, "Calculator", "Age", loanDetail.Age);
//es.SetCellA1(sessionId, "Calculator", "Period_Y", loanDetail.Term);
Option 2
//Set the values at the same time
//One Sync Call for the range
object[] array = new object[] { new object[] { loanDetail.LoanAmount.ToString() },
new object[]{loanDetail.Deposit.ToString()},
new object[]{(loanDetail.Interest/100)},
new object[]{loanDetail.Age.ToString()},
new object[]{loanDetail.Term.ToString()} };
I found the latter more efficient and could even be abstracted into classes that would make a big solution less error prone.
After setting the named range
es.SetRangeA1(loanDetail.SessionID,
"Calculator", "LoanValues", array);
es.CalculateWorkbook(loanDetail.SessionID,
CalculateType.Recalculate);
I can get the values I require back and set them on my windows form
//loanDetail.Repayments
loanDetail.Repayments =
float.Parse(es.GetCellA1(loanDetail.SessionID, "Calculator", "Repayments", true, out calculatedStatus) as string);
Summary
Thats my take on Excel Web services and how it could be used in Enterprise applications. I would recommend using named ranges and would certainly use a scalable architecture using patterns such as MVC , Facade and Service Layer. Find the solution and use it to experiment like I have. Good luck !
Excel_Calculator.zip (236.13 KB)