Nowadays almost every enterprise application takes advantage of SOA platform, which in most cases means that there is some sort of web service that an application is consuming in order to enable unprecedented dynamic interaction and reduce costs and speed time to market by maximizing interface (components and various domain functionality) reuse. And also in most cases it is Web applications that takes advantage of Web services. Therefore, in .NET anyone who develops ASP.NET applications will have to integrate WCF service into his application. Most of the time it will by adding web or service reference but when developing RIA using Ajax technology there is a way to consume WCF services with Ajax client library to avoid page postback. And when adding JSON message formatting an application can respond even quicker as JSON format is a lot less overhead comparing to SOAP. That's why it is so important that every .NET developer who builds RIA should always consider using REST WCF (with JSON message response fomat) from Ajax.
Example:
An ASP.NET page that will allow user to enter mortgage fixed loan amount with note rate and number of years, and after clicking Calculate button a javascript function will call WCF service and receive values of monthly payment and total amount to pay (loan amount + interests). There will be no UpdatePanel and no service or web reference and proxy class generated by svcutil.exe tool.
1. WCF Service
- LoanService.svc
<%@ ServiceHost Language="C#" Debug="true" Service="WcfAjaxService.LoanService" CodeBehind="LoanService.svc.cs" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>
- LoanService.svc.cs
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace WcfAjaxService
{
[ServiceContract(Namespace = "WcfAjaxService")]
public class LoanService
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
public LoanPayments GetPaymentInfo(double LoanAmount, double AnnualRate, int Terms)
{
return LoanPayments.Create(LoanAmount, AnnualRate, Terms);
}
}
[DataContract]
public class LoanPayments
{
[DataMember]
public double MonthlyPayment { get; set; }
[DataMember]
public double TotalPayments { get; set; }
public static LoanPayments Create(double LoanAmount, double AnnualRate, int Terms)
{
LoanPayments payment = new LoanPayments();
double MonthlyRate = AnnualRate / 1200d;
double t = Math.Pow(1d + MonthlyRate, Terms*12);
payment.MonthlyPayment = LoanAmount * MonthlyRate * t / (t - 1d);
payment.TotalPayments = Math.Round(payment.MonthlyPayment * Terms*12,2);
payment.MonthlyPayment = Math.Round(payment.MonthlyPayment,2);
return payment;
}
}
}
- Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AjaxWcfJson._Default" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Consuming REST WCF service from Ajax</title>
<link href="Stylesheet.css" rel="stylesheet" type="text/css" />
<script language="javascript" type="text/javascript">
// <!CDATA[
var amount;
var rate;
var term;
function Submit1_onclick() {
amount=parseFloat($get("txtLoanAmount").value);
rate=parseFloat($get("txtNoteRate").value);
term=parseInt($get("txtTerms").value);
var service =new WcfAjaxService.LoanService();
service.GetPaymentInfo(amount,rate,term, onSuccess, null, null);
}
function onSuccess(result) {
$get("txtPayment").value=result.MonthlyPayment;
$get("txtTotalPayments").value=result.TotalPayments;
}
// ]]>
</script>
</head>
<body>
<form id="LoanForm" runat="server">
<div>
<asp:ToolkitScriptManager ID="ToolkitScriptMgr" runat="server">
<Services>
<asp:ServiceReference Path="http://localhost:4465/LoanService.svc" />
</Services>
</asp:ToolkitScriptManager>
<asp:RoundedCornersExtender ID="PaymentCalcPanel_RoundedCornersExtender" runat="server"
Enabled="True" TargetControlID="PaymentCalcPanel" BorderColor="#003300" Corners="All"
Radius="10">
</asp:RoundedCornersExtender>
<asp:Panel ID="PaymentCalcPanel" runat="server" Height="180px" Width="190px" BackColor="#FF6600">
<table align="center">
<tr>
<td>
Loan Amount
</td>
<td>
<asp:TextBox ID="txtLoanAmount" runat="server" CssClass="textbox"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Note Rate
</td>
<td>
<asp:TextBox ID="txtNoteRate" runat="server" CssClass="textbox"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Terms
</td>
<td>
<asp:TextBox ID="txtTerms" runat="server" CssClass="textbox"></asp:TextBox>
</td>
</tr>
<tr>
<td align="center" colspan="2" height="37px">
<input type="button" id="btnCalculate" class="button" value="Calculate" onclick="Submit1_onclick()" />
</td>
</tr>
<tr>
<td>
Payment
</td>
<td>
<asp:TextBox ID="txtPayment" runat="server" ReadOnly="True" CssClass="textbox"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Total to pay
</td>
<td>
<asp:TextBox ID="txtTotalPayments" runat="server" ReadOnly="True" CssClass="textbox"></asp:TextBox>
</td>
</tr>
</table>
</asp:Panel>
</div>
</form>
</body>
</html>
- Stylesheet.css
body
{
font: font-family: 'Times New Roman', Times, serif; font-size: small; color: #FFFFFF; font-weight:bold;
}
.button
{
background-color:Green;
font: font-family: 'Times New Roman', Times, serif; font-size: small; color: #FFFFFF; font-weight:bold;
}
.textbox
{
font: font-family: 'Times New Roman', Times, serif; font-size: small; color:Teal ; font-weight:bold;
width:80px;
text-align:right;
}
Here is the screenshot of the application is action:
Here is a list of requirements that need to be met to support consuming REST WCF with JSON by Ajax:
- WCF Service (LoanService.svc) uses factory WebScriptServiceHostFactory that automatically adds ASP.NET Ajax endpoint to a service. However, it could've been done without the factory class by simply adding serviceModel configuration to web.config file that defines <enableWebScript/> element in service endpoint, using webHttpBinding and also allowing compatibility with ASP.NET applications (either by adding to service class [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] attribute or by adding under serviceModel element <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> a
- Operation contract method uses WebGet attribute which is required to define REST WCF Service but also it allows to define response message format as JSON
- Adding reference to the WCF service has been done by adding <asp:ServiceReference path=uri/> to Services collection to ScriptManager or here ToolkitScriptManager. However, a proxy class in javaascript could've been generated by adding /js or /jsdebug to the address of WCF service
- Instantiate service class object is by using keyword new and service name with service namespace, which is why ServiceContract defines namespace (although namespace should always be changed), hence the service object is WcfAjaxService.LoanService
- Operation contract method (exposed web method) is called with the following parameters: method arguments, succeeded callback function, failed callback function and associated user context. In the example only succeeded callback was used, which is called when web service has finished executing and has returned a value
- Succeeded callback function has 1 parameter, which is an object returned by operation contract method. Since WCF service returns object in JSON format, javascript requires nothing else to be able to parse it as it contains full support for JSON objects. Therefore, object contains properties as defines by data contract of the WCF service
- To see service response as text (not an JSON object) the following line of code must be added to GetPaymentInfo (operation contract) method: WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
And this is how the JSON response message looks like:
However, if you try to run it without commenting out lines in onSuccess javascript method that access properties of result argument, you will get an error. This is because response message is not longer an JSON object but a string. But because the string is in JSON format it can be parsed to JSON object using javascript eval function. Here is an example how to parse a JSON-formatted message if outgoing response from web service was returned as plain text.
- Button is an HTML input element of type button, therefore calling WCF service doesn't require page postback and also doesn't require using UpdatePanel, unless there is other control that causes page postback, which loads content of textboxes - holding results of calculations - from ViewState
1 comments:
Thank you so much.
I've learn many in this article,especially something about C#.NET. I want to create 1d/2d barcode image in c#.net, so I believe this article will gibe me some help.