在上一篇文章中,我们讨论了使用 ASP.NET AJAX默认的Profile Service。一般来说,它已经能够迎合大多数应用的需要了。不过除此之外,ASP.NET AJAX还提供了让我们自定义Profile Service的机制。要自定义Profile Service,一般来说要分为两步:
一、在ScriptManager中指定Profile Service的Path
在ASP.NET AJAX的客户端脚本中,如果没有使用Sys.Services.ProfileService.set_path方法来指定一个提供Profile Service的地址,就会使用默认的地址,它会使ASP.NET AJAX的Profile Service使用程序集中特定的类。一般来说,我们不需要手动调用Sys.Services.ProfileService.set_path方法,只 需要在ScriptManager中指定即可。如下:
打开页面后,可以在页面中发现如下的JavaScript代码:
出现“/”是因为我测试的页面在根目录下。因此,Profile Service就会使用指定的Web Service,而不是默认的Web Service类。
二、实现自己的Web Service类
指定了自己的Web Service类,自然就要实现自己的类了。事实上,我们要实现的就是3个方法。就这个方面来说,ASP.NET AJAX中Profile Service使用的默认的Web Service类Microsoft.Web.Profile.ProfileService是我们绝佳的参考。因此,我们在这里分析一下这些方法,对于 我们的自定义工作是非常有帮助的。
可能需要注意的一点是,我们在实现这些方法时,从理论上来讲参数类型不用完全和 Microsoft.Web.Profile.ProfileService中的方法完全相同。ASP.NET AJAX的能够根据参数的类型尽可能地将获得的JSON字符串转换成需要的类型。不过事实上,似乎 Microsoft.Web.Profile.ProfileService里那些方法的参数选择已经是非常合理的。另外,由于客户端Profile Service代码不太容易修改(事实上客户端也不是不能扩展,最极端的情况,不就是我们自己实现一个ProfileService吗?),为了保持返回 的JSON字符串能够被正确处理,这些方法的返回值一般来说可以不变。
1、GetAllPropertiesForCurrentUser方法
这个方法的作用是获得当前用户所有的Profile信息,它没有输入的参数,返回的JSON字符串形式如下:
它通过GroupName.ProfileName的形式来表示Profile Group,Group中的每一个Profile需要分别列出,而“...”则表示对应Profile值的JSON字符串。
在Microsoft.Web.Profile.ProfileService里,这个方法的代码如下:
2、GetPropertiesForCurrentUser方法
这个方法的作用是获得当前用户指定的Profile信息,它的输入JSON字符串形式如下:
它的返回值的JSON字符串和GetAllPropertiesForCurrentUser相同,就不再赘述了。
在类中,这个方法的代码如下:
可以看到,GetAllPropertiesForCurrentUser和GetPropertiesForCurrentUser中都是使用了ProfileService.GetProfile静态方法来获得结果的,我们来仔细看一下这个方法的实现。如下:
这个方法还是非常容易理解和编写的,不需要涉及到任何序列化或者反序列化操作,那些工作完全由ASP.NET的Web Service Access机制代为完成了。
3、SetPropertiesForCurrentUser方法
这个方法的作用是保存当前用户的Profile信息,它的输入JSON字符串形式如下:
而它返回的则是正确保存的Profile数量。代码如下:
起关键作用的方法是ProfileService.SetProfile静态方法,我们来仔细看一下这个方法:
方法也不难理解,不过可以需要对于ASP.NET AJAX的序列化与反序列化能力有一定了解,例如第22和23行构造了一个JavaScriptSerializer的目的是使用包含在 WebServiceData内的JavaScriptTypeResolver信息,以此获得从字符串形式的type描述到确定type类型的映射关 系。这其实是一个非常有用的特性,不过有点让人想不通的是,在目前的WebServiceData中由于没有方法添加自定义的 JavaScriptTypeResolver,因此这个功能的效用为0。难道未来的版本会有办法利用到这个特性?拭目以待吧,虽然我觉得比较困难。
我们也可以看到,这个方法中使用了内部的ObjectConverter.ConvertObjectToType方法来将一个嵌套的 Dictionay和List转换为指定的类型。如果我们要使用这个方法,应该怎么做呢?事实上,ASP.NET AJAX提供了我们一定的序列化与反序列化能力。请看JavaScirptSerializer的公有实例方法ConvertToType< T>的实现:
它直接调用了内部的ObjectConverter.ConverObjectToType方法,这不就是我们所需要的功能吗?
等一下!别高兴的太早!请注意,这是一个范型方法!我们这里只能获得一个Profile属性的类型对象,不能在编译期指定调用哪种具体类型的范型方法, 也就是说,我们不能在这里简单地使用这个范型方法。我们在这里需要对范型方法的调用进行“后期绑定”,因为只有在执行期才能获得范型的类型。
后期绑定?这不就是Reflection提供的功能吗?因此,我们可以使用.NET Framework 2.0的Reflection机制,它已经对于范型类型提供了支持。在这里,我们可以这么做:
这样,我们就实现了对于范型方法调用的“后期绑定”:在执行期才决定调用哪个具体类型的范型方法。这么做会有性能损失,例如查找 ConvertToType方法并构造相应的范型的MethodInfo,但是如果我们使用Dictionary<Type, MethodInfo>将type和它所对应的MethodInfo保存起来,可以在一定程度上的减少性能的损失。不过使用 Method.Invoke造成的性能损失就无法避免了。
另外,我打算在接下来的文章中详细分析一下ASP.NET AJAX中提供给开发人员的序列化与反序列化能力,以及它们是如何配合JavaScriptTypeResolver与JavaScriptConverter提供一定的自定义能力。
一、在ScriptManager中指定Profile Service的Path
在ASP.NET AJAX的客户端脚本中,如果没有使用Sys.Services.ProfileService.set_path方法来指定一个提供Profile Service的地址,就会使用默认的地址,它会使ASP.NET AJAX的Profile Service使用程序集中特定的类。一般来说,我们不需要手动调用Sys.Services.ProfileService.set_path方法,只 需要在ScriptManager中指定即可。如下:
<
asp:ScriptManager
ID
="ScriptManager1"
runat
="server"
ScriptMode
="Debug"
>
< ProfileService Path ="CustomProfileService.asmx" />
</ asp:ScriptManager >
< ProfileService Path ="CustomProfileService.asmx" />
</ asp:ScriptManager >
打开页面后,可以在页面中发现如下的JavaScript代码:
Sys.Services.ProfileService.set_path('
/
CustomProfileService.asmx');
出现“/”是因为我测试的页面在根目录下。因此,Profile Service就会使用指定的Web Service,而不是默认的Web Service类。
二、实现自己的Web Service类
指定了自己的Web Service类,自然就要实现自己的类了。事实上,我们要实现的就是3个方法。就这个方面来说,ASP.NET AJAX中Profile Service使用的默认的Web Service类Microsoft.Web.Profile.ProfileService是我们绝佳的参考。因此,我们在这里分析一下这些方法,对于 我们的自定义工作是非常有帮助的。
可能需要注意的一点是,我们在实现这些方法时,从理论上来讲参数类型不用完全和 Microsoft.Web.Profile.ProfileService中的方法完全相同。ASP.NET AJAX的能够根据参数的类型尽可能地将获得的JSON字符串转换成需要的类型。不过事实上,似乎 Microsoft.Web.Profile.ProfileService里那些方法的参数选择已经是非常合理的。另外,由于客户端Profile Service代码不太容易修改(事实上客户端也不是不能扩展,最极端的情况,不就是我们自己实现一个ProfileService吗?),为了保持返回 的JSON字符串能够被正确处理,这些方法的返回值一般来说可以不变。
1、GetAllPropertiesForCurrentUser方法
这个方法的作用是获得当前用户所有的Profile信息,它没有输入的参数,返回的JSON字符串形式如下:
{
'ZipCode' : ...,
'Address.City' : ...,
'Address.State' : ...
}
'ZipCode' : ...,
'Address.City' : ...,
'Address.State' : ...
}
它通过GroupName.ProfileName的形式来表示Profile Group,Group中的每一个Profile需要分别列出,而“...”则表示对应Profile值的JSON字符串。
在Microsoft.Web.Profile.ProfileService里,这个方法的代码如下:
[WebMethod]
public IDictionary < string , object > GetAllPropertiesForCurrentUser()
{
ProfileService.CheckProfileServicesEnabled();
return ProfileService.GetProfile(HttpContext.Current, null );
}
public IDictionary < string , object > GetAllPropertiesForCurrentUser()
{
ProfileService.CheckProfileServicesEnabled();
return ProfileService.GetProfile(HttpContext.Current, null );
}
2、GetPropertiesForCurrentUser方法
这个方法的作用是获得当前用户指定的Profile信息,它的输入JSON字符串形式如下:
['ZipCode', 'Address.City', 'Address.State']
它的返回值的JSON字符串和GetAllPropertiesForCurrentUser相同,就不再赘述了。
在类中,这个方法的代码如下:
[WebMethod]
public IDictionary < string , object > GetPropertiesForCurrentUser( string [] properties)
{
ProfileService.CheckProfileServicesEnabled();
return ProfileService.GetProfile(HttpContext.Current, properties);
}
public IDictionary < string , object > GetPropertiesForCurrentUser( string [] properties)
{
ProfileService.CheckProfileServicesEnabled();
return ProfileService.GetProfile(HttpContext.Current, properties);
}
可以看到,GetAllPropertiesForCurrentUser和GetPropertiesForCurrentUser中都是使用了ProfileService.GetProfile静态方法来获得结果的,我们来仔细看一下这个方法的实现。如下:
GetProfile静态方法
这个方法还是非常容易理解和编写的,不需要涉及到任何序列化或者反序列化操作,那些工作完全由ASP.NET的Web Service Access机制代为完成了。
3、SetPropertiesForCurrentUser方法
这个方法的作用是保存当前用户的Profile信息,它的输入JSON字符串形式如下:
{
'ZipCode' : ...,
'Address.City' : ...,
'Address.State' : ...
}
'ZipCode' : ...,
'Address.City' : ...,
'Address.State' : ...
}
而它返回的则是正确保存的Profile数量。代码如下:
[WebMethod]
public int SetPropertiesForCurrentUser(IDictionary < string , object > values)
{
ProfileService.CheckProfileServicesEnabled();
return ProfileService.SetProfile(HttpContext.Current, values);
}
public int SetPropertiesForCurrentUser(IDictionary < string , object > values)
{
ProfileService.CheckProfileServicesEnabled();
return ProfileService.SetProfile(HttpContext.Current, values);
}
起关键作用的方法是ProfileService.SetProfile静态方法,我们来仔细看一下这个方法:
SetProfile静态方法
方法也不难理解,不过可以需要对于ASP.NET AJAX的序列化与反序列化能力有一定了解,例如第22和23行构造了一个JavaScriptSerializer的目的是使用包含在 WebServiceData内的JavaScriptTypeResolver信息,以此获得从字符串形式的type描述到确定type类型的映射关 系。这其实是一个非常有用的特性,不过有点让人想不通的是,在目前的WebServiceData中由于没有方法添加自定义的 JavaScriptTypeResolver,因此这个功能的效用为0。难道未来的版本会有办法利用到这个特性?拭目以待吧,虽然我觉得比较困难。
我们也可以看到,这个方法中使用了内部的ObjectConverter.ConvertObjectToType方法来将一个嵌套的 Dictionay和List转换为指定的类型。如果我们要使用这个方法,应该怎么做呢?事实上,ASP.NET AJAX提供了我们一定的序列化与反序列化能力。请看JavaScirptSerializer的公有实例方法ConvertToType< T>的实现:
public
T ConvertToType
<
T
>
(
object
obj)
{
return (T) ObjectConverter.ConvertObjectToType(obj, typeof (T), this );
}
{
return (T) ObjectConverter.ConvertObjectToType(obj, typeof (T), this );
}
它直接调用了内部的ObjectConverter.ConverObjectToType方法,这不就是我们所需要的功能吗?
等一下!别高兴的太早!请注意,这是一个范型方法!我们这里只能获得一个Profile属性的类型对象,不能在编译期指定调用哪种具体类型的范型方法, 也就是说,我们不能在这里简单地使用这个范型方法。我们在这里需要对范型方法的调用进行“后期绑定”,因为只有在执行期才能获得范型的类型。
后期绑定?这不就是Reflection提供的功能吗?因此,我们可以使用.NET Framework 2.0的Reflection机制,它已经对于范型类型提供了支持。在这里,我们可以这么做:
JavaScriptSerializer serializer
=
new
JavaScriptSerializer();
Type type = value.GetType() ; // 获得所需的Profile属性的Type对象
MethodInfo info = typeof(JavaScriptSerializer).GetMethod( " ConvertToType " ).MakeGenericMethod(type );
return info.Invoke(serializer, new object [] { value });
Type type = value.GetType() ; // 获得所需的Profile属性的Type对象
MethodInfo info = typeof(JavaScriptSerializer).GetMethod( " ConvertToType " ).MakeGenericMethod(type );
return info.Invoke(serializer, new object [] { value });
这样,我们就实现了对于范型方法调用的“后期绑定”:在执行期才决定调用哪个具体类型的范型方法。这么做会有性能损失,例如查找 ConvertToType方法并构造相应的范型的MethodInfo,但是如果我们使用Dictionary<Type, MethodInfo>将type和它所对应的MethodInfo保存起来,可以在一定程度上的减少性能的损失。不过使用 Method.Invoke造成的性能损失就无法避免了。
另外,我打算在接下来的文章中详细分析一下ASP.NET AJAX中提供给开发人员的序列化与反序列化能力,以及它们是如何配合JavaScriptTypeResolver与JavaScriptConverter提供一定的自定义能力。
我们现在已经知道了如何自定义服务器端的Profile Service支持,但是如果我们一直使用客户端的“标准”功能,还谈不上“自定义”或者“扩展”。那么在下一片文章中,我们一起来讨论一下自定义客户端的Profile Service支持吧。
本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/60667,如需转载请自行联系原作者