WCF and ASP.NET AJAX Integration
WCF and ASP.NET AJAX Integration
This post is part of WCF REST Presentation I show last week at Microsoft.
I played with the WCF and ASP.NET AJAX integration, and created a sample web site which by can integrate WCF service with Microsoft Virtual Earth.
The Service Contract
The following contract is simple service contract with one method GetLocation(). The namespace "Sample.Services" will be used later as a prefix to the generated proxy.
AspNetCompatibilityRequirementsMode allows the service to use ASP.NET features, such as accessing HttpContext object or identity impersonation.
[ServiceContract(Namespace = "Sample.Services")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MapService
{
[OperationContract]
public Location GetLocation(Target target)
{
Double latitude = 0;
Double longitude = 0;
if (target.Name == "Starbucks Manhattan")
{
latitude = 40.70456;
longitude = -74.01317;
}
Location location = new Location
{
Latitude = latitude,
Longitude = longitude
};
return location;
}
}
[DataContract]
public class Location
{
[DataMember]
public Double Latitude
{
get;
set;
}
[DataMember]
public Double Longitude
{
get;
set;
}
}
[DataContract]
public class Target
{
[DataMember]
public String Name
{
get;
set;
}
}
The Script
The interesting point here is how you access the service itself - by adding the namespace prefix (if not exist use default Tempuri.org).
You can also notice how I'm sending a complex parameter 'target' which is not a basic type, but complex - I use JSON directly .
var map;
var index = 0;
function LoadMap()
{
map = new VEMap('VEMap');
map.LoadMap();
}
function WebServiceCallBack(response)
{
AddPushPin(response);
}
function AddPushPin(point)
{
var pin = new VEPushpin(
index,
new VELatLong(point.Latitude, point.Longitude),
null,
point.Name,
point.Description);
index++;
map.AddPushpin(pin);
map.PanToLatLong(new VELatLong(point.Latitude, point.Longitude));
}
function OnGetTarget()
{
var target = { "Name" : $get("target").value };
Sample.Services.MapService.GetLocation(target, WebServiceCallBack, OnError);
}
function OnError(result)
{
alert(result.get_message());
}
Proxy Generation
Try the following URL and you will get the proxy itself:
http://localhost:49814/VirtualEarth/MapService.svc/js
Notice that the proxy created with static methods with the same signature as the methods on the service itself, and as additional, adds callback, error handler and a user context.
Type.registerNamespace('Sample.Services');
Sample.Services.MapService=function() {
Sample.Services.MapService.initializeBase(this);
this._timeout = 0;
this._userContext = null;
this._succeeded = null;
this._failed = null;
}
Sample.Services.MapService.prototype={
_get_path:function() {
var p = this.get_path();
if (p) return p;
else return Sample.Services.MapService._staticInstance.get_path();},
GetLocation:function(target,succeededCallback, failedCallback, userContext) {
return this._invoke(this._get_path(), 'GetLocation',false,{target:target},succeededCallback,failedCallback,userContext); }}
Sample.Services.MapService.registerClass('Sample.Services.MapService',Sys.Net.WebServiceProxy);
Sample.Services.MapService._staticInstance = new Sample.Services.MapService();
Sample.Services.MapService.set_path = function(value) { Sample.Services.MapService._staticInstance.set_path(value); }
Sample.Services.MapService.get_path = function() { return Sample.Services.MapService._staticInstance.get_path(); }
Sample.Services.MapService.set_timeout = function(value) { Sample.Services.MapService._staticInstance.set_timeout(value); }
Sample.Services.MapService.get_timeout = function() { return Sample.Services.MapService._staticInstance.get_timeout(); }
Sample.Services.MapService.set_defaultUserContext = function(value) { Sample.Services.MapService._staticInstance.set_defaultUserContext(value); }
Sample.Services.MapService.get_defaultUserContext = function() { return Sample.Services.MapService._staticInstance.get_defaultUserContext(); }
Sample.Services.MapService.set_defaultSucceededCallback = function(value) { Sample.Services.MapService._staticInstance.set_defaultSucceededCallback(value); }
Sample.Services.MapService.get_defaultSucceededCallback = function() { return Sample.Services.MapService._staticInstance.get_defaultSucceededCallback(); }
Sample.Services.MapService.set_defaultFailedCallback = function(value) { Sample.Services.MapService._staticInstance.set_defaultFailedCallback(value); }
Sample.Services.MapService.get_defaultFailedCallback = function() { return Sample.Services.MapService._staticInstance.get_defaultFailedCallback(); }
Sample.Services.MapService.set_path("/VirtualEarth/MapService.svc");
Sample.Services.MapService.GetLocation= function(target,onSuccess,onFailed,userContext) {Sample.Services.MapService._staticInstance.GetLocation(target,onSuccess,onFailed,userContext); }
var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;
if (typeof(Target) === 'undefined') {
var Target=gtc("Target:http://schemas.datacontract.org/2004/07/
Target.registerClass('Target');
}
if (typeof(Location) === 'undefined') {
var Location=gtc("Location:http://schemas.datacontract.org/2004/07/
Location.registerClass('Location');
}
The ASP.NET Page
<html xmlns="http://www.w3.org/1999/xhtml >
<head id="Head1" runat="server">
<title></title>
</head>
<body onload="LoadMap();">
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=5 />
<asp:ScriptReference Path="mapscripts.js" />
</Scripts>
<Services>
<asp:ServiceReference Path="MapService.svc" />
</Services>
</asp:ScriptManager>
<div style='width: 800px; height: 500px; position: relative; overflow: hidden;' id='VEMap'></div>
Enter target: <input type="text" id="target" />
<input type="button" value="get target" onclick="OnGetTarget()" /><br />
<br />
</form>
</body>
</html>
The Service File
The WebScriptServiceHostFactory adds ASP.NET AJAX endpoint the the service, and all of this without a line of configuration!!
When you specify the WebScriptServiceHostFactory, it creates a ServiceHost with the appropriate binding and behavior for integration with ASP.NET AJAX.
<%@ ServiceHost Language="C#" Debug="true" Service="MapService" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>
Conclusion
To summarize the WCF and ASP.NET AJAX Integration:
- Full support in client stack
- Provides proxy generation
- Supports GET and POST only
- Does not support UriTemplates
- No configuration using WebScriptServiceHostFactory
- And finally, works in ASP.NET medium trust
The source code of WCF and ASP.NET AJAX Integration.