Are you aware of Script# (http://www.codeplex.com/scriptsharp) a framework that will allow the compilation of C# code to JavaScript. This therefore provides you with the ability of using Visual Studio IDE to create and maintain your JavaScript via C# code.
So, how does ASP.NET AJAX fit in? We'll ASP.NET AJAX is an excellent framework but I am more interested in the following features:
Exposing ASP.NET Web Service to Client Script
http://www.asp.net/ajax/documentation/live/tutorials/
ExposingWebServicesToAJAXTutorial.aspx
Calling ASP.NET Web Services from Client Script
http://www.asp.net/AJAX/Documentation/Live/tutorials/
ConsumingWebServicesWithAJAXTutorial.aspx
These above features allow you to call your Web Services from JavaScript. The web services can return JSON which is lighter than XML thus reducing traffic.
One thing I noticed though is that is was difficult to consume an ASP.NET AJAX exposed Web Service using Script#. I had to write my own wrapping code.
An ASP.NET Web Service can expose itself as JavaScript via http://myserver/SampleService.asmx/js which can be included in your page for example look at my SampleService.asmx
Type.registerNamespace('SampleNs');SampleNs.SampleService=function() {SampleNs.SampleService.initializeBase(this);this._timeout = 0;this._userContext = null;this._succeeded = null;this._failed = null;}SampleNs.SampleService.prototype={GetSample:function(succeededCallback, failedCallback, userContext) {return this._invoke(SampleNs.SampleService.get_path(), 'GetSample',false,{},succeededCallback,failedCallback,userContext); },GetSampleById:function(id,succeededCallback, failedCallback, userContext) {return this._invoke(SampleNs.SampleService.get_path(), 'GetSampleById',false,{id:id},succeededCallback,failedCallback,userContext); }}SampleNs.SampleService.registerClass('SampleNs.SampleService',Sys.Net.WebServiceProxy);SampleNs.SampleService._staticInstance = new SampleNs.SampleService();SampleNs.SampleService.set_path = function(value) { SampleNs.SampleService._staticInstance._path = value; }SampleNs.SampleService.get_path = function() { return SampleNs.SampleService._staticInstance._path; }SampleNs.SampleService.set_timeout = function(value) { SampleNs.SampleService._staticInstance._timeout = value; }SampleNs.SampleService.get_timeout = function() { return SampleNs.SampleService._staticInstance._timeout; }SampleNs.SampleService.set_defaultUserContext = function(value) { SampleNs.SampleService._staticInstance._userContext = value; }SampleNs.SampleService.get_defaultUserContext = function() { return SampleNs.SampleService._staticInstance._userContext; }SampleNs.SampleService.set_defaultSucceededCallback = function(value) { SampleNs.SampleService._staticInstance._succeeded = value; }SampleNs.SampleService.get_defaultSucceededCallback = function() { return SampleNs.SampleService._staticInstance._succeeded; }SampleNs.SampleService.set_defaultFailedCallback = function(value) { SampleNs.SampleService._staticInstance._failed = value; }SampleNs.SampleService.get_defaultFailedCallback = function() { return SampleNs.SampleService._staticInstance._failed; }SampleNs.SampleService.set_path("/SampleWebSite/SampleService.asmx");SampleNs.SampleService.GetSample= function(onSuccess,onFailed,userContext) {SampleNs.SampleService._staticInstance.GetSample(onSuccess,onFailed,userContext); }SampleNs.SampleService.GetSampleById= function(id,onSuccess,onFailed,userContext) {SampleNs.SampleService._staticInstance.GetSampleById(id,onSuccess,onFailed,userContext); }var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;if (typeof(SampleNs.SampleService_Sample) === 'undefined') {SampleNs.SampleService_Sample=gtc("SampleNs.SampleService+Sample");SampleNs.SampleService_Sample.registerClass('SampleNs.SampleService_Sample');}
with the WSDL looking like http://myserver/SampleService.asmx?wsdl
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://sampleService/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://sampleService/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://sampleService/">
<s:element name="GetSample">
<s:complexType />
</s:element>
<s:element name="GetSampleResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="GetSampleResult" type="tns:Sample" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="Sample">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="Id" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="OtherSamples" type="tns:ArrayOfSample" />
</s:sequence>
</s:complexType>
<s:complexType name="ArrayOfSample">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="Sample" nillable="true" type="tns:Sample" />
</s:sequence>
</s:complexType>
<s:element name="GetSampleById">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="id" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetSampleByIdResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="GetSampleByIdResult" type="tns:Sample" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="GetSampleSoapIn">
<wsdl:part name="parameters" element="tns:GetSample" />
</wsdl:message>
<wsdl:message name="GetSampleSoapOut">
<wsdl:part name="parameters" element="tns:GetSampleResponse" />
</wsdl:message>
<wsdl:message name="GetSampleByIdSoapIn">
<wsdl:part name="parameters" element="tns:GetSampleById" />
</wsdl:message>
<wsdl:message name="GetSampleByIdSoapOut">
<wsdl:part name="parameters" element="tns:GetSampleByIdResponse" />
</wsdl:message>
<wsdl:portType name="SampleServiceSoap">
<wsdl:operation name="GetSample">
<wsdl:input message="tns:GetSampleSoapIn" />
<wsdl:output message="tns:GetSampleSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetSampleById">
<wsdl:input message="tns:GetSampleByIdSoapIn" />
<wsdl:output message="tns:GetSampleByIdSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="SampleServiceSoap" type="tns:SampleServiceSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="GetSample">
<soap:operation soapAction="http://sampleService/GetSample" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetSampleById">
<soap:operation soapAction="http://sampleService/GetSampleById" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="SampleServiceSoap12" type="tns:SampleServiceSoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="GetSample">
<soap12:operation soapAction="http://sampleService/GetSample" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetSampleById">
<soap12:operation soapAction="http://sampleService/GetSampleById" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SampleService">
<wsdl:port name="SampleServiceSoap" binding="tns:SampleServiceSoap">
<soap:address location="http://localhost:51750/SampleWebSite/SampleService.asmx" />
</wsdl:port>
<wsdl:port name="SampleServiceSoap12" binding="tns:SampleServiceSoap12">
<soap12:address location="http://localhost:51750/SampleWebSite/SampleService.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
My Proposal / Solution
I have a work in progress solution that isn't distributed/published yet because I am looking for the right channels to assist me but effectively I can leverage the Script# features and generate the client code that will describe the definition of a Web Service. This means that you'll get intellisense support with returned objects from your web services; neat hey?
From the above WSDL definition the following code is generated which can be compiled with your Script# project.
// Generated using ASP.NET AJAX Generator
namespace SampleNs {
using System;
/// <remarks/>
[Imported()]
public partial class SampleService {
/// <remarks/>
// No method body is intended
[PreserveCase()]
public static void GetSample(SampleSuccessCallbackDelegate successClassbackHandler, SampleFailureCallbackDelegate failureCallbackHandler) {
}
/// <remarks/>
// No method body is intended
[PreserveCase()]
public static void GetSampleById(int id, SampleSuccessCallbackDelegate successClassbackHandler, SampleFailureCallbackDelegate failureCallbackHandler) {
}
}
/// <remarks/>
[Imported()]
public partial class Sample {
/// <remarks/>
[PreserveCase()]
public int Id;
/// <remarks/>
[PreserveCase()]
public string Name;
/// <remarks/>
[PreserveCase()]
public string Description;
/// <remarks/>
[PreserveCase()]
public Sample[] OtherSamples;
}
[Imported()]
public delegate void SampleSuccessCallbackDelegate(Sample sampleResult, object eventArgs);
[Imported()]
public delegate void SampleFailureCallbackDelegate(object eventArgs);
}
which allows you to write code like:
private void Example(){
SampleService.GetSample(
new SampleSuccessCallbackDelegate(
this.SuccessCallbackDelegate
),
new SampleFailureCallbackDelegate(
this.FailureCallbackDelegate)
);
}
/// <summary>
/// This method is called on a success
/// </summary>
/// <param name="sample">The object being returned</param>
/// <param name="args">Ignor for now; will have mapped object later</param>
private void SuccessCallbackDelegate(
Sample sample, object args){
//Notice the object is known type and we can access their properties
Script.Alert(
string.Concat(
sample.Name,
"- ",
sample.Description)
);
}
/// <summary>
/// This method is called on a failure
/// </summary>
/// <param name="error"></param>
private void FailureCallbackDelegate(object error) {
//Alert the failure
Script.Alert("Failed");
}
Using Script# ,ASP.NET AJAX Web Services and the generator we can easly write code to consume our web services thus leveraging Script#.
Technical Detail
Using the .NET Framework ServiceDescription and DiscoveryClientProtocol I was able to generate a standard Web Service Client proxy and then manipulate the CodeDom to generate a C# script file that could be compiled with the Script# library to generate JavaScript.
Project and release here http://www.codeplex.com/AJAXCodeGeneration