ESRI Silverlight API 和 Runtime SDK for WPF 的 await async 扩展

.NET4.5 最激动人心的特性就是异步开发的极大简化,在新一代的Windows Store应用程序中大量使用了这种方法。无奈Esri Silverlight API 很久没有更新了,所以自己写了一个扩展,扩展了ArcGIS Silverlight API 3.1, ArcGIS runtime for WPF 10.1版本所有的异步方法,使之可以使用.NET 4.5 最新的 await async 关键字。

具体扩展了所有基于TaskBase 基类的所有Task,如下图:




例如,GeometryService 对象中含有10多个几何操作的异步方法:


这些方法所计算出来的结果都需要在回调函数中获得,如下图:


 本扩展库将其所有Async 的异步-回调函数方法 扩展成为了基于Task的方法,使得.NET 的异步编程更加简单,逻辑更加清晰,完全消除了 Event  CallBack,进一步降低了开发难度。

来看看具体的代码,传统的使用GeometryService进行投影操作一般是如下操作


geometryService = new GeometryService("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
geometryService.ProjectCompleted += geometryService_ProjectCompleted;
geometryService.Failed += geometryService_Failed;
MapPoint inputMapPoint = new MapPoint(x, y, new SpatialReference(4326));
geometryService.ProjectAsync(new List<Graphic>() { new Graphic() { Geometry = inputMapPoint } }, MyMap.SpatialReference, inputMapPoint);

上面的代码,我们必须在回调函数 geometryService_ProjectCompleted 方法中才能获得结果,使用了扩展库之后,以上的代码可以简化为:


geometryService = new GeometryService("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
MapPoint inputMapPoint = new MapPoint(x, y, new SpatialReference(4326));
var result = await geometryService.ProjectAsyncTask(new List<Graphic>(){ new Graphic() { Geometry = inputMapPoint } }, MyMap.SpatialReference, inputMapPoint);

通过await 之后,直接就可以获得投影结果。


除了TaskBase 类之外API中还有一些需要回调函数的方法,这里也做了相应扩展,例如,对于动态地图服务 ,ArcGISDynamicMapServiceLayer 中有一个GetAllDetails 方法,这个方法的参数是一个 Action,传统的调用方法如下:

(map.Layers[0] as ArcGISDynamicMapServiceLayer).GetAllDetails(onCompleted);


这里给调用动态地图的GetAllDetails 方法,传的参数是一个满足Signature :  void  fun(IDictionary<int, ESRI.ArcGIS.Client.FeatureService.FeatureLayerInfo> arg1, Exception arg2) 的delegate 实例。
这样我们在代码的另外一个部分就必须定义一个名为onCompleted的函数,如下


private void onCompleted(IDictionary<int, ESRI.ArcGIS.Client.FeatureService.FeatureLayerInfo> arg1, Exception arg2)
{
	var info = arg1;
}


在这个回调函数中,我们才能获得子图层的元数据信息。
使用了扩展库之后,这个方法就变得非常直观自然,如下

var info = await (map.Layers[0] as ArcGISDynamicMapServiceLayer).GetAllDetailsTask();

这样,直接通过await GetALLDetailsTask 方法就可以获得图层的元数据信息。


下载链接 http://download.csdn.net/detail/crazyxhz/6372107

使用方法:把对应平台的文件夹中的dll 全部加入项目工程中,然后加上命名空间引用:

using ESRI.ArcGIS.Client.AsyncExtension;
API就可以使用基于Task的方法了。

本扩展支持Silverlight5 , .NET 4.0  .NET 4.5, 其中,Silverlight5 和.net 4.0 需要安装微软的Bcl库才能支持await async   下载的压缩包中已经包含了所需的BCL 库文件,官方地址 http://www.nuget.org/packages/Microsoft.Bcl.Async/,  另外编译环境需要vs2012以上,因为C# compiler5.0 才支持 await 操作符和 async关键字

下面来看一下具体的实现,对于GeometryService QueryTask 这样基于Event的异步操作,我们的方式是给其加扩展方法:

public static Task<QueryResult> ExecuteAsyncTask(this QueryTask qt, Query query)
		{
			var tcs = new TaskCompletionSource<QueryResult>();
			EventHandler<QueryEventArgs> handler = null;
			EventHandler<TaskFailedEventArgs> failHandler = null;
			var result = new QueryResult();
			handler += (s, e) =>
			{
				result.Succeed = true;
				result.ExceededTransferLimit = e.ExceededTransferLimit;
				result.FeatureSet = e.FeatureSet;
				result.UserState = e.UserState;
				tcs.TrySetResult(result);
				qt.ExecuteCompleted -= handler;
			};
			failHandler += (s, e) =>
			{
				result.Succeed = false;
				result.Error = e.Error;
				result.UserState = e.UserState;
				tcs.TrySetResult(result);
				qt.Failed -= failHandler;
			};
			qt.ExecuteCompleted += handler;
			qt.Failed += failHandler;
			qt.ExecuteAsync(query);
			return tcs.Task;
		}


拿Query的扩展来说,首先要注意,一个查询请求发出去之后,我们有可能查询成功,有可能查询失败,这两种情况都需要考虑到,若是进入ExecuteCompleted 事件中,我们获得的查询放在QueryEventArgs 这个类型的对象中,若是进入Failed 事件中,我们获得的错误信息是保存在TaskFailedEventArgs 这个类型的对象中。要考虑到这两种可能,我们有一种思路就是让这个扩展方法返回EventArgs 对象( TaskFailedEventArgs 和QueryEventArgs 共同的基类 ),这样我们查询完成后,把获得的EventArgs 对象转化为QueryEventArgs,若转化结果为空,则说明查询失败,返回的是TaskFailedEventArgs,反之,查询成功,可以获得QueryEventArgs 中的各种Property。

考虑到这样实现使用中会比较蛮烦,故这里定义了一个Helper Class ,叫做QueryResult如下:

public class QueryResult
	{
		public bool Succeed { get; set; }
		public bool ExceededTransferLimit { get; set; }
		public FeatureSet FeatureSet { get; set; }
		public object UserState { get; set; }
		public Exception Error { get; set; }
	}

这就是把两个EventArgs简单的结合了一下,然后添加了一个bool变量来判断查询是否成功,这样我们在获取了查询结果之后,只需要判断一下这个bool变量的值,然后使用对应的结果就可以了。

还有一点需要指出的是关于Event Handler的注册,这里没有直接使用Lambda Expression,因为若是直接使用Lambda的话就没有办法取消注册,那么每进行一次查询,这个lambda就会多注册一次,所以这里采用了把Evenhandler 写出来,在设定完Task返回结果之后立刻取消自己在Event Delegate中的注册,防止多次被调用


再来看一下基于Delegate 的Task实现

public static Task<GenerateCredentialResult> GenerateCredentialAsyncTask(this IdentityManager im, string url, string userName, string password, IdentityManager.GenerateTokenOptions generateTokenOptions = null)
		{
			var tcs = new TaskCompletionSource<GenerateCredentialResult>();
			var result = new GenerateCredentialResult();
			im.GenerateCredentialAsync(url, userName, password,
				(cre, err) =>
				{
					if (err != null)
					{
						result.Succeed = false;
						result.Error = err;
					}
					else
					{
						result.Succeed = true;
						result.Credential = cre;
					}
					tcs.SetResult(result);
				},
			generateTokenOptions);
			return tcs.Task;
		}


IdentifyManager的获取密码的操作,原理也是一样的,只是没有Event的注册。

具体源码可以参考https://code.csdn.net/crazyxhz/silverlightapiextension




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值