- 浏览: 320216 次
最新评论
-
ax003d:
你好,我使用这种方法,在gridview里显示图片,当图片数量 ...
ScrollView嵌套GridView、ListView的解决办法 -
yunzhu:
Firefox下有Xmarks书签同步插件,很好用,可以方便地 ...
开发人员应该具备的工具----欲善其事,先利其器 -
mjlixm:
Android入门:ContentProvider 学习了! ...
Android入门:ContentProvider -
bluesky329:
很好收益匪浅!
一个软件工程师的经验之谈 -
qinglongyun:
每天工作4小时的程序员
组合云: SQL Azure + Bing Maps
公告:本博客为微软云计算中文博客的镜像博客。部分文章因为博客兼容性问题,会影响阅读体验。如遇此情况,请访问原博客。
SQL Azure最新版本中最激动人心的一个功能当属spatial data,也就是对几何和地理数据类型的支持。这项功能为地理位置相关应用程序的存储问题提供了一个简化统一方案。当然,绝大多数地理位置相关应用程序都需要显示地图。为了达成这个目标,微软还提供了另一个云服务:Bing Maps。
本文将引导大家结合SQL Azure和Bing Maps这两个云服务,创建一个简单的旅游计划系统。我们假设你已经熟悉以下技术:
- T-SQL,尤其是如何创建表和存储过程
- ADO.NET Entity Framework,尤其是function imports
- WCF Data Services,尤其是reflection provider
- HTML andjQuery,尤其是AJAX和DOM操作
Bing Maps开发相关知识并不是一个先决条件,因为本文会指导大家学习。ASP.NET和Silverlight的知识也不需要,因为我们会创建一个纯html客户端。
相关信息
本文相关的示例代码可以自这边下载。
一个更完整的示例即将在1Code上发布,届时我们会更新这条信息。
本文的英文版即将在Community Goodies上发布。届时我们会更新这条信息。
Spatial data
如果你没听说过spatial data这个术语,没有关系。很简单,spatial data就是表示几何或者地理信息的数据
在中学的地理课上,我们都学习过如何用纬度和经度来表示地球上的一个位置。在许多场合下,我们完全可以在一张数据库的表中建两个列,分别存储经纬度信息。
但是,也有不少场景需要更强大的功能。例如,怎样存储一个国家的边界(大致上可以看作一条折线)?如何计算地球上两点间的距离(直线)?就算你只需要对某一个位置进行操作,你也可以将该位置设想成一个点,而不是分开的纬度和经度���个数值。
在过去,人们开发出了很多不同的解决方案来应对以上问题。每种方案都有自己的优缺点,但是没有一个是完美的。不同的程序使用不用的解决方案,也会对互操作性造成影响。
为了解决这个问题,我们必须采用一套标准。目前世界上使用最广泛的标准是由Open Geospatial Consortium (OGC)定制的OpenGIS。在OpenGIS中,几何和地理数据可以有多种表示方法,例如Well-known binary (WKB),Well-known text (WKT),以及Geography Markup Language (GML)。
WKB
WKB 提供了一种高效的方案来存储spatial数据。数据以二进制方式存放,因此占的空间很小,而且计算机也很容易对这些数据进行计算。
在SQL Azure中(以及SQL Server以及一些常见的第三方数据库提供商,例如Oracle和DB2),WKB正是spatial数据内部存放的格式。也正是因为这个原因,不同数据库间的互操作变得简单了。
可是,用肉眼来读WKB并不容易。它是针对机器设计的,而不是针对人设计的。因此,我们不准备在本文中讨论二进制格式。如果你感兴趣,你可以参照标准规约。为了让人类更容易读懂spatial数据,还有其它格式被开发出来。
WKT
WKT比WKB好懂多了。例如:
- POINT(2 3)
- LINESTRING(2 3, 7 8)
- POLYGON((2 3, 7 8, 3 5, 2 3))
如你所见,这些数据很好的解释了它们自己是什么。
然而,数据是用文本形式存储的,因此计算机在计算数据是效率相对较低,而且占用的存储空间也较大(每个字符至少要占8位)。幸运的是,SQL Azure提供了简便方法在WKB和WKT之间切换。因此在大多数场合下,数据都用WKB来存储,而WKT通常被用于将数据展示给人类。
有关WKT的更多信息,请参考这篇Wiki文章,以及标准规约。
GML
GML是另一种是用文本来表示spatial数据的方案。它通常比WKT长,但是它使用了另外一个标准:xml。因此程序可以很轻松的使用标准的xml库(例如LINQ to XML)来解析GML。本文不会详细讨论GML。如果你对它感兴趣,请参考http://en.wikipedia.org/wiki/Geography_Markup_Language。
在SQL Azure中使用spatial数据
创建表和列
SQL Server 2008为spatial数据引入了两种新的数据类型:Geometry和Geography。现在SQL Azure完全支持它们了。如果你从未接触过spatial数据,我们推荐你完成SQL Server training course中的spatial数据相关课程。
Spatial数据类型就是单纯的数据类型。因此在创建一个列时,你可以像使用nvarchar(50)一样使用它们。
CREATETABLE[dbo].[Travel](
[ID] [uniqueidentifier]NOTNULL,
[Place] [nvarchar](200)NOTNULL,
[GeoLocation] [geography]NOTNULL,
[Time] [datetime]NOTNULL,
CONSTRAINT[PK_Travel]PRIMARYKEYCLUSTERED
(
[ID]ASC
),
CONSTRAINT[IX_Travel]UNIQUENONCLUSTERED
(
[Place]ASC,
[Time]ASC
)
)
请注意GeoLocation列的数据类型是geography。
创建数据
数据是以二进制存储的,但是你并不需要了解二进制格式。你可以使用Microsoft.SqlServer.Types.dll程序集中的类型来创建数据。这个程序集是一个SQL CLR程序集。就是说它既可以在托管语言中使用,又可以在T-SQL中使用。
例如,在C#中根据经纬度创建一个geography对象:
SqlGeographysqlGeography=SqlGeography.Point(latitude,longitude,4326);
上述代码中的4326是geography数据规定的使用参数,没有特殊含义。
在T-SQL中使用WKT创建一个geography对象:
Geography::STGeomFromText(@GeoLocation,4326)
你可以选择任何你喜欢的编程序言。但是通常,如果一个对象只是临时创建(例如创建一个临时对象用于计算两点间的距离),我们直接在应用程序级别用托管语言写代码,而不需要经过数据库。尤其是当数据库位于云端而不是本地时。
如果数据要被存储至数据库,使用T-SQL编写的存储过程可以提高性能。此外,目前Entity Framework并不直接支持spatial数据。所以如果我们选择Entity Framework用于数据访问层,我们必须提供存储过程,并在EF代码中访问。
以下是我们的旅游计划程序中的InsertIntoTravel存储过程。请注意STGeomFromText方法的调用:
CREATEPROCEDURE[dbo].[InsertIntoTravel]
@IDuniqueidentifier,
@Placenvarchar(200),
@GeoLocationvarchar(max),
@Timedatetime
AS
BEGIN
InsertIntoTravel
Values(@ID,@Place,Geography::STGeomFromText(@GeoLocation,4326),@Time)
END
同样,这是UpdateTravel存储过程:
CREATEPROCEDURE[dbo].[UpdateTravel]
@IDuniqueidentifier,
@PlaceNVarchar(50),
@GeoLocationNVarchar(max),
@Timedatetime
AS
BEGIN
Update[dbo].[Travel]
Set[Place]=@Place,
[GeoLocation]=Geography::STGeomFromText(@GeoLocation,4326),
[Time]=@Time
WhereID=@ID
END
这是DeleteFromTravel存储过程:
CREATEPROCEDURE[dbo].[DeleteFromTravel]
@IDuniqueidentifier
AS
BEGIN
DeleteFromTravelWhereID=@ID
END
有关spatial数据的更多信息,请访问http://msdn.microsoft.com/en-us/library/bb933876.aspx。
在Entity Framework访问spatial数据
刚刚已经说过了,目前Entity Framework并不直接提供对spatial数据的支持。但是它真的是一个很好很强大的O/R mapping框架,仅仅因为不支持spatial数据就把Entity Framework彻底扔掉是一种很不明智的选择。我们来找找看在你的EF模型中使用spatial数据的方法。
如果你针对上述Travel表创建一个EF模型,你会发现GeoLocation列并不存在于模型中,而且你无法手工在存储模型中创建一个spatial类型的属性。然而,EF是支持二进制数据的,而刚才也说过,SQL Azure在内部就是用二进制格式来表示数据的。因此我们可以在数据库中创建一个视图,把spatial数据转化成二进制数据。
CREATEVIEW[dbo].[TravelView]
AS
SELECTID,Place,CAST(GeoLocationASvarbinary(MAX))ASGeoLocation,Time
FROMdbo.Travel
现在针对该视图常见一个EF模型,你会发现一切正常。
EF中的视图都是只读的。不过我们刚才已经创建过了必要的存储过程,因此我们只需要通过function imports将存储过程导入到EF模型中,并且调用它们来保存修改。以下是EF的ObjectContext类的分部类代码。请注意在SaveChanges方法中,我们仅仅是在调用存储过程:
internalpartialclassTravelEntities
{
internalList<TravelEntity>InsertedEntities=newList<TravelEntity>();
internalList<TravelEntity>UpdatedEntities=newList<TravelEntity>();
internalList<TravelEntity>DeletedEntities=newList<TravelEntity>();
publicoverrideintSaveChanges(System.Data.Objects.SaveOptionsoptions)
{
try
{
foreach(variteminthis.InsertedEntities)
{
this.InsertIntoTravel(Guid.NewGuid(),item.Place,item.GeoLocationText,item.Time);
}
foreach(variteminthis.UpdatedEntities)
{
this.UpdateTravel(item.ID,item.Place,item.GeoLocationText,item.Time);
}
foreach(variteminthis.DeletedEntities)
{
this.DeleteFromTravel(item.ID);
}
}
catch(Exceptionex)
{
thrownewEntitySqlException("Anerroroccurredwhenmodifyingdatainthedatabase.Pleaserefertoinnerexceptionformoredetail.",ex);
}
return0;
}
}
GeoLocationText是在entity类的分部类中定义的一个属性。它代表了地理位置的WKT形式,不过并不会被存到数据库中。
internalpartialclassTravelEntity
{
internalstringGeoLocationText{get;set;}
}
将spatial数据公开成web services
目前我们的程序还很简单,但是我们必须考虑将来可能的扩展。因此我们一定要采用SOA。此外,我们打算创建一个AJAX客户端,AJAX客户端无法直接和Entity Framework打交道,必须通过一个web service。既然我们是在暴露数据,WCF Data Services自然是首选项。
制定一个data contract(数据契约)
我们可以直接使用EF模型作为data contract,但是这种方案至少有两个问题:第一,大多数客户端程序并不关心我们如何存储数据。它们希望使用常见的Latitude和Longitude属性,而不是WKT或WKB。许多客户端程序开发人员可能根本不知道spatial数据。第二,如果键来我们的模型发生了变化,data contract也不得不变化,那么所有的客户端程序都可能会停止运行。所以我们有必要创建一个对客户端程序而言更有好的data contract:
[DataServiceKeyAttribute("ID")]
publicclassTravel
{
publicGuidID{get;set;}
publicstringPlace{get;set;}
publicDateTimeTime{get;set;}
publicdoubleLatitude{get;set;}
publicdoubleLongitude{get;set;}
}
创建一个reflection provider
在WCF Data Services中使用我们自己的data contract,就意味着我们不得不创建一个reflection provider,而不能依赖于内置的object context provider。不管怎样,我们都需要reflection provider的,因为目前object context provider并不支持EF entity类中没有被映射到模型上的自定义属性。所以事实上我们并不需要比原来多学太多的代码……
本文不会深入探讨如何创建reflection provider,毕竟这和云开发并没有太大的关系。你可以参考http://msdn.microsoft.com/en-us/library/dd723653.aspx上的详细信息。你还可以阅读这一系列深入探讨reflection provider的博客文章。简而言之,就是要创建一个实现了IUpdateable接口的类,并且实现必要的方法。以下是完整的代码。我们把data contract (Travel)转换成了EF entity (TravelEntity),并将存储数据的工作委任给EF。
publicclassTravelDataServiceContext:IUpdatable
{
privateTravelEntities_entityFrameworkContext=newTravelEntities();
privateList<Travel>_travels;
privateList<Travel>_insertedTravels=newList<Travel>();
privateList<Travel>_updatedTravels=newList<Travel>();
privateList<Travel>_deletedTravels=newList<Travel>();
publicTravelDataServiceContext()
{
this._travels=newList<Travel>();
foreach(varentityinthis._entityFrameworkContext.TravelEntitySet)
{
LatLonggeoLocation=this.WKBToLatLong(entity.GeoLocationBinary);
this._travels.Add(newTravel(){ID=entity.ID,Place=entity.Place,Time=entity.Time,Latitude=geoLocation.Latitude,Longitude=geoLocation.Longitude});
}
}
publicIQueryable<Travel>Travels
{
get
{
returnthis._travels.AsQueryable<Travel>();
}
}
#regionIUpdatableMembers
publicvoidAddReferenceToCollection(objecttargetResource,stringpropertyName,objectresourceToBeAdded)
{
thrownewNotImplementedException();
}
publicvoidClearChanges()
{
thrownewNotImplementedException();
}
publicobjectCreateResource(stringcontainerName,stringfullTypeName)
{
try
{
Typet=Type.GetType(fullTypeName,true);
objectresource=Activator.CreateInstance(t);
if(resourceisTravel)
{
this._insertedTravels.Add((Travel)resource);
}
returnresource;
}
catch(Exceptionex)
{
thrownewInvalidOperationException("Failedtocreateresource.Seetheinnerexceptionformoredetails.",ex);
}
}
publicvoidDeleteResource(objecttargetResource)
{
if(targetResourceisTravel)
{
Traveltravel=(Travel)targetResource;
this._deletedTravels.Add(travel);
}
}
publicobjectGetResource(IQueryablequery,stringfullTypeName)
{
objectresource=query.Cast<object>().SingleOrDefault();
if(fullTypeName!=null&&resource.GetType().FullName!=fullTypeName)
{
thrownewApplicationException("Unexpectedtypeforthisresource.");
}
returnresource;
}
publicobjectGetValue(objecttargetResource,stringpropertyName)
{
thrownewNotImplementedException();
}
publicvoidRemoveReferenceFromCollection(objecttargetResource,stringpropertyName,objectresourceToBeRemoved)
{
thrownewNotImplementedException();
}
publicobjectResetResource(objectresource)
{
if(resourceisTravel)
{
this._updatedTravels.Add((Travel)resource);
}
returnresource;
}
publicobjectResolveResource(objectresource)
{
returnresource;
}
publicvoidSaveChanges()
{
foreach(Traveltinthis._insertedTravels)
{
TravelEntityentity=newTravelEntity(){Place=t.Place,Time=t.Time,GeoLocationText=this.LatLongToWKT(t.Latitude,t.Longitude)};
this._entityFrameworkContext.InsertedEntities.Add(entity);
}
foreach(Traveltinthis._updatedTravels)
{
TravelEntityentity=newTravelEntity(){ID=t.ID,Place=t.Place,Time=t.Time,GeoLocationText=this.LatLongToWKT(t.Latitude,t.Longitude)};
this._entityFrameworkContext.UpdatedEntities.Add(entity);
}
foreach(Traveltinthis._deletedTravels)
{
//Fordelete,weonlyneedID.
TravelEntityentity=newTravelEntity(){ID=t.ID};
this._entityFrameworkContext.DeletedEntities.Add(entity);
}
this._entityFrameworkContext.SaveChanges();
}
publicvoidSetReference(objecttargetResource,stringpropertyName,objectpropertyValue)
{
thrownewNotImplementedException();
}
publicvoidSetValue(objecttargetResource,stringpropertyName,objectpropertyValue)
{
try
{
varproperty=targetResource.GetType().GetProperty(propertyName);
if(property==null)
{
thrownewInvalidOperationException("Invalidproperty:"+propertyName);
}
property.SetValue(targetResource,propertyValue,null);
}
catch(Exceptionex)
{
thrownewInvalidOperationException("Failedtosetvalue.Seetheinnerexceptionformoredetails.",ex);
}
}
#endregion
privatestringLatLongToWKT(doublelatitude,doublelongitude)
{
SqlGeographysqlGeography=SqlGeography.Point(latitude,longitude,4326);
returnsqlGeography.ToString();
}
privateLatLongWKBToLatLong(byte[]wkb)
{
using(MemoryStreamms=newMemoryStream(wkb))
{
using(BinaryReaderreader=newBinaryReader(ms))
{
SqlGeographysqlGeography=newSqlGeography();
sqlGeography.Read(reader);
returnnewLatLong(){Latitude=sqlGeography.Lat.Value,Longitude=sqlGeography.Long.Value};
}
}
}
}
publicstructLatLong
{
publicdoubleLatitude;
publicdoubleLongitude;
}
创建AJAX程序显示地图
现在服务已经准备就绪,下一步就是创建客户端程序了。Bing Maps针对AJAX和Silverlight都提供了SDK。针对其它客户端,你也可以直接使用SOAP或REST SDK。本文主要演示如何创建一个AJAX客户端,Silverlight客户端实现起来要比AJAX客户端容易多了,你应该很快就能自动上手。
展示地图
创建地图之前,你必须自https://www.bingmapsportal.com/注册一个帐号。在注册过程中,你会获得一个密码用于你的应用程序中。
varmap; varmapCredential='your credential';
第一步是将Bing Maps相关脚本引用到HTML文件中:
<scripttype="text/javascript"src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx"></script>
然后加载地图:
$(document).ready(LoadMap);
functionLoadMap()
{
map=newVEMap('MainMap');
map.SetCredentials(mapCredential);
//Cannotuseonclicksinceitfireswhenpanningthemap.
//map.AttachEvent('onclick',Map_OnClick);
map.AttachEvent('onmousedown',Map_OnMouseDown);
map.AttachEvent('onmouseup',Map_OnMouseUp);
map.AttachEvent("onmouseover",Map_OnMouseOver);
map.LoadMap(newVELatLong(31,121));
}
VEMap类的构造函数使用一个参数,它是一个HTML元素,表示地图的承载体。你可以简单使用一个div:
<divid="MainMap"style="width:100%;height:600px;"/>
往地图上加pushpin
VEMap支持标准的DOM事件,例如onmousedown,从而使得我们能够为地图提供交互功能。你并不需要些代码来实现标准的操作,例如平移和缩放。撰写事件处理程序更多地是为了提供自定义行为。例如,当用户点击地图时,我们可以在点击的位置创建一个pushpin 。请注意标准操作,例如平移,也会触发onclick事件,因此通常我们会选择处理onmousedown/up事件。
下述代码演示了如何在点击位置穿件一个pushpin。部分代码调用了Bing Maps REST service,我们会在下一章节中介绍。现在,请注意怎样获得点击位置。e.MapX/Y返回的是地图坐标系中的xy坐标。我们需要通过PixelToLatLong方法将它转换成纬度和经度。
varmouseDownLocation;
functionMap_OnMouseDown(e) { mouseDownLocation=newVEPixel(e.mapX,e.mapY); } functionMap_OnMouseUp(e) { varpixel=newVEPixel(e.mapX,e.mapY); //Onlyaddapushpiniftheuserisnotpanningthemap. if(mouseDownLocation!=null&&mouseDownLocation.x==pixel.x&&mouseDownLocation.y==pixel.y) { varlatLong=map.PixelToLatLong(pixel); //InvoketheLocationRESTservicetoobtaininformationoftheclickedplace. $.ajax( { url:'http://dev.virtualearth.net/REST/v1/Locations/'+latLong.Latitude+','+latLong.Longitude+'?o=json&jsonp=LocationCallback&key='+mapCredential, dataType:'jsonp', jsonp:'LocationCallback', success:LocationCallback }); } }
functionLocationCallback(result) { if(result.resourceSets.length>0) { varresourceSet=result.resourceSets[0]; if(resourceSet.resources.length>0) { varresource=resourceSet.resources[0]; //Code related to data manipulation omitted.
//Addapushpin.
varpoint=newVEShape(VEShapeType.Pushpin,newVELatLong(resource.point.coordinates[0],resource.point.coordinates[1]));
point.SetTitle(resource.name);
map.AddShape(point);
//Code related to data manipulation omitted.
}
}
}
接下来,我们调用Bing Maps REST service取得点击位置的更详细的信息。在回调函数中,创建一个pushpin并且显示到地图上。为了达成这个目的,使用适当的数据创建一个VELatLong对象,并且调用VEMap.AddShape。你可以使用SetTitle方法设置在用户鼠标移到该pushpin上之时展示的数据。在这个例子中,我们显示该位置的名称(例如城市名)。
使用Bing Maps REST Services
Bing Maps同时提供了SOAP和REST服务。既然我们使用的是AJAX客户端,我们自然选择REST服务。Bing Maps REST Services同时支持XML和JSON。同理,在AJAX客户端中我们通常选择JSON。
上述代码已经展示了如何调用简单的REST API。注意到o这个query string的值是json,意味着我们希望服务以JSON格式返回数据。Bing Maps REST Services支持JSONP,因此你可以自AJAX客户端跨域访问服务。此外,尽管在这边没有演示,但其实Bing Maps REST Services也针对Silverlight和Flash提供了cross domain policy files。最后,注意每个请求都必须提供密码。你可以自https://www.bingmapsportal.com/注册以获取密码。
有关Bing Maps REST Services的更多信息,请参考http://msdn.microsoft.com/en-us/library/ff701713.aspx。有关SOAP Services的信息可以自http://msdn.microsoft.com/en-us/library/cc980922.aspx取得。
有关Bing Maps AJAX SDK的更多信息,请参考http://msdn.microsoft.com/en-us/library/bb429619.aspx和http://www.microsoft.com/maps/isdk/ajax/。有关Silverlight SDK的信息可以自http://msdn.microsoft.com/en-us/library/ee681884.aspx和http://www.microsoft.com/maps/isdk/silverlight/取得。
和WCF Data Services集成
现在你知道了如何显示地图以及创建pushpin。下一步当然就是将数据存储至/加载自SQL Azure数据库了,使用刚才创建的WCF Data Services。
调用REST API
别忘了WCF Data Services就是REST services。你可以使用标准的jQuery像访问一般的REST API那样调用它。用GET查询,用POST插入,用PUT更新,用DELETE删除。
$.ajax(
{
type:'GET',
url:dataServiceUri+'?$orderby=Time',
dataType:'json',
success:LoadDataCompleted
});
functionPostToDS() { varitem=SearchItems(this); $.ajax( { type:'POST', url:dataServiceUri, contentType:'application/json;charset=utf-8', data:JSON.stringify(item.Value), datatype:'json' }); } functionPutToDS() { varitem=SearchItems(this); $.ajax( { type:'PUT', url:dataServiceUri+"(guid'"+this+"')", contentType:'application/json;charset=utf-8', data:JSON.stringify(item.Value), datatype:'json' }); } functionDeleteFromDS() { $.ajax( { type:'DELETE', url:dataServiceUri+"(guid'"+this+"')", contentType:'application/json;charset=utf-8', datatype:'json' }); }
在POST和PUT操作中,你需要提供符合OData标准的数据。为了简化数据序列化的工作,我们使用标准的JSON stringifier。
自定义服务操作
现在我们为程序添加一个新功能。当我们在列表中选择一个地点时,如果将鼠标移到地图上的某个pushpin,就显示两地间的距离。为了达成这个目标,首先要在WCF Data Services中创建一个自定义服务操作。
[WebGet]
publicdoubleDistanceBetweenPlaces(doublelatitude1,doublelatitude2,doublelongitude1,doublelongitude2)
{
SqlGeographygeography1 =SqlGeography.Point(latitude1, longitude1, 4326);
SqlGeographygeography2 =SqlGeography.Point(latitude2, longitude2, 4326);
returngeography1.STDistance(geography2).Value;
}
上述代码又一次使用了spatial数据来计算两地间的距离。
在客户端,我们可以轻松调用这个操作,并使用VEShape.SetDescription和VEMap.ShowInfoBox将距离展示在地图上:
varurl = dataServiceUri +'/DistanceBetweenPlaces?'
+'latitude1='+ selectedItem.Value.Latitude
+'&longitude1='+ selectedItem.Value.Longitude
+'&latitude2='+ latlong.Latitude
+'&longitude2='+ latlong.Longitude;
$.ajax(
{
type:'GET',
url: url,
dataType:'json',
success:function(result)
{
pushpin.SetDescription('Distance between '+ selectedItem.Value.Place +' and '+ pushpin.Title +': '+ result.d.DistanceBetweenPlaces);
map.ShowInfoBox(pushpin);
}
});
更多信息
当然啦,要创建一个完整的程序,还有很多工作要做。例如在客户端创建��维护数据,展示数据,跟踪数据状态,等等。这些代码比较长,而且大多数代码都和SQL Azure以及Bing Maps没有直接关系,因此我们不准备贴在这里。你可以随时参考示例代码。如果你觉得理解代码有困难,请参考这篇博客文章,他会告诉你如何在AJAX客户端访问WCF Data Services。当然,基本的DOM,CSS,以及jQuery UI的知识也会很有帮助的。
结论
在这片博客文章中,我们看到了如何组合SQL Azure和Bing Maps的威力,创建一个旅游计划系统,展示一张地图,并且把旅游景点位置信息存放至SQL Azure。当多个云被组合起来之时,我们就可以创建新时代的用户体验,并且获得一个统一的数据存储中心。
相关推荐
Azure+OpenAI+(ChatGPT)试用攻略及企业Azure+OpenAI指南
细数选择云数据库SQL Azure的六大理由.pdf
微软发布了自己的全新云计算产品Windows Azure Platform。历经数年的技术沉淀,Windows Azure Platform 已经成为一个技术领先、开放的平台,基于不同语言开发的应用程序都可以运行在这个平台上面,并通过标准协议...
级别1:部署Azure Rails和Ember Todo应用程序这是有关随着DevOps成熟度的提高在Azure上部署Rails应用程序的博客系列的一部分。 请参阅:todo:add_link 该Todo应用程序运行的余烬前端取决于Rails后端。 它说明了...
Microsoft SQL Azure Database提供了一种在云中...在本次的技术手册中,我们将为您奉上最完整的SQL Azure教程,从入门到深层次了解微软云数据库的点点滴滴。通过对本教程的学习,相信您能最快地掌握SQL Azure的知识。
kateshdmade-small-online-shop:Spring+ HIBERNATE + AZURE SQL BBDD +上传项目图像的AZURE存储
Learning Azure Functions: Creating Azure Functions in Visual Studio 2017
SQL Azure DataSync SQL Azure DataSync
Azure 管理员通常在一个较大团队中致力于实现组织的云基础结构。 还要与其他角色协调配合,以提供 Azure 网络、安全、数据库、应用程序开发和 DevOps 解决方案。 你应该熟悉以下内容: 操作系统 网络 服务器 虚拟...
測驗 AZ-900: Microsoft Azure Fundamentals
Microsoft JDBC Driver 3.0 for SQL Server and SQL Azure Microsoft JDBC Driver 3.0 for SQL Server and SQL Azure
Vue Azure地图 Vue Azure Maps是用于Vue.js的库,它集成了Azure Maps。 它提供了一些开箱即用的Vue组件,并且还支持自定义组件。 该库依赖于Azure Maps Control Web SDK,后者使用Mapbox呈现高性能的WebGL。 Vue ...
第 4 天 — Azure 开放 AI:设置 Azure AI 搜索解决方案
Azure Functions打字稿样板 该项目有助于设置在使用Azure Functions + TypeScript开发项目时遇到的许多事情。 配套 ,打字稿ORM ,密码加密 ,JWT令牌身份验证, X-Authorization: Bearer 身份验证X-Authorization...
使用Azure网站+ SQL Azure数据库的示例Fabrikam Fiber应用程序。 包括2个Azure RM部署模板和一个Visual Studio部署项目。 先决条件 此项目要求您安装Azure SDK 2.6,并将其与Visual Studio 2013或2015 RC一起使用。
微软文档:Microsoft Azure 架构良好的框架-1118页
AzureFunctionsImageEditor:使用Azure Functions + Blob存储更改图像筛选器的应用程序
店铺管理系统 C ++控制台应用程序作为我学习的一部分, 我开发了一个控制台项目, 通过使用:C ++,Azure DevOps,Visual Studio。
快速入门:使用 Azure 空间定位点创建 Unity Android 应用-教程使用到的资源
健康管理云平台