具体扩展了所有基于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