在上一篇文章中,我们讨论了使用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中指定即可。如下:

<asp:ScriptManager ID="ScriptManager1" runat="server" ScriptMode="Debug">
    <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' : ...
}

它通过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);
}

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);
}

可以看到,GetAllPropertiesForCurrentUser和GetPropertiesForCurrentUser中都是使用了ProfileService.GetProfile静态方法来获得结果的,我们来仔细看一下这个方法的实现。如下:

GetProfile静态方法
 1 internal static IDictionary<string, object> GetProfile(HttpContext context, string[] properties)
 2 {
 3     // 当前用户的Profile
 4     ProfileBase profile = context.Profile;
 5 
 6     // 如果没有profile,则返回null
 7     if (profile == null)
 8     {
 9         return null;
10     }
11 
12     // 作为结果返回的字典
13     IDictionary<string, object> dictResult = new Dictionary<string, object>();
14 
15     // 如果properties为null,表示需要返回所有的参数
16     if (properties == null)
17     {
18         // 枚举web.config中注册的每一个Profile属性设置
19         foreach (SettingsProperty property in ProfileBase.Properties)
20         {
21             // 获得Profile属性名称
22             string name = property.Name;
23 
24             // 如果web.config配置文件里没有定义ReadAccessProperties,
25             // 或者该Profile属性被定义在ReadAccessPropeties中。
26             if ((ProfileService._allowedGet != null) && ProfileService._allowedGet.ContainsKey(name))
27             {
28                 // 则准备输出
29                 dictResult.Add(name, profile[name]);
30             }
31         }
32 
33         // 返回结果
34         return dictResult;
35     }
36 
37     // 枚举参数的每一项,它们是需要输出的Profile信息
38     foreach (string prop in properties)
39     {
40         // 如果web.config配置文件里没有定义ReadAccessProperties,
41         // 或者该Profile属性被定义在ReadAccessPropeties中。
42         if ((ProfileService._allowedGet != null) && ProfileService._allowedGet.ContainsKey(prop))
43         {
44             // 则准备输出
45             dictResult.Add(prop, profile[prop]);
46         }
47     }
48 
49     // 返回结果
50     return dictResult;
51 }

这个方法还是非常容易理解和编写的,不需要涉及到任何序列化或者反序列化操作,那些工作完全由ASP.NET的Web Service Access机制代为完成了。

3、SetPropertiesForCurrentUser方法

这个方法的作用是保存当前用户的Profile信息,它的输入JSON字符串形式如下:

{
    'ZipCode' : ...,
    'Address.City' : ...,
    'Address.State' : ...
}

而它返回的则是正确保存的Profile数量。代码如下:

[WebMethod]
public int SetPropertiesForCurrentUser(IDictionary<string, object> values)
{
    ProfileService.CheckProfileServicesEnabled();
    return ProfileService.SetProfile(HttpContext.Current, values);
}

起关键作用的方法是ProfileService.SetProfile静态方法,我们来仔细看一下这个方法:

SetProfile静态方法
 1 internal static int SetProfile(HttpContext context, IDictionary<string, object> profileValues)
 2 {
 3     // 如果没有提供保存的Profile值,则返回0
 4     if ((profileValues == null) || (profileValues.Count == 0))
 5     {
 6         return 0;
 7     }
 8 
 9     // 获取当前用户的Profile值
10     ProfileBase profile = context.Profile;
11 
12     // 如果当前没有Profile,也返回0
13     if (profile == null)
14     {
15         return 0;
16     }
17 
18     int count = 0;
19 
20     // 获得当前的Serializer,
21     // 以获取JavaScriptTypeResolver和JavaScriptConverter的支持
22     WebServiceData data = WebServiceData.GetWebServiceData(context, context.Request.FilePath);
23     JavaScriptSerializer serializer = data.Serializer;
24 
25     // 枚举提供的每一个Profile的值
26     foreach (KeyValuePair<string, object> pair in profileValues)
27     {
28         // 获得Profile的名称
29         string name = pair.Key;
30         if ((ProfileService._allowedSet != null) && ProfileService._allowedSet.ContainsKey(name))
31         {
32             // 通过Profile名称获得在web.config中的Profile定义
33             SettingsProperty property = ProfileBase.Properties[name];
34             // 如果存在这个Profile属性
35             if (property != null)
36             {
37                 // 获得Profile属性的类型
38                 Type type = property.PropertyType;
39                 // 调用内部的ObjectConverter.ConvertObjectToType方法进行转换,
40                 // 然后赋值给相应的Profile
41                 profile[name] = ObjectConverter.ConvertObjectToType(pair1.Value, type, serializer);
42 
43                 // 已经保存的Profile属性数量加1
44                 count++;
45                 continue;
46             }
47         }
48     }
49 
50     return count;
51 }

方法也不难理解,不过可以需要对于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);
}

它直接调用了内部的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 });

这样,我们就实现了对于范型方法调用的“后期绑定”:在执行期才决定调用哪个具体类型的范型方法。这么做会有性能损失,例如查找ConvertToType方法并构造相应的范型的MethodInfo,但是如果我们使用Dictionary<Type, MethodInfo>将type和它所对应的MethodInfo保存起来,可以在一定程度上的减少性能的损失。不过使用Method.Invoke造成的性能损失就无法避免了。

另外,我打算在接下来的文章中详细分析一下ASP.NET AJAX中提供给开发人员的序列化与反序列化能力,以及它们是如何配合JavaScriptTypeResolver与JavaScriptConverter提供一定的自定义能力。

我们现在已经知道了如何自定义服务器端的Profile Service支持,但是如果我们一直使用客户端的“标准”功能,还谈不上“自定义”或者“扩展”。那么在下一片文章中,我们一起来讨论一下自定义客户端的Profile Service支持吧。

转载于:https://www.cnblogs.com/JeffreyZhao/archive/2006/11/04/Inside_Atlas_Series__Investigate_the_Application_Services_2.html

深入Atlas系列:探究Application Services(2) - 自定义服务器端Profile Service支持相关推荐

  1. Sharepoin学习笔记—架构系列—Sharepoint服务(Services)与服务应用程序框架(Service Application Framework) 1

    Sharepoin学习笔记-架构系列-Sharepoint服务(Services)与服务应用程序框架(Service Application Framework) 1 Sharepoint服务是Sha ...

  2. 深入Atlas系列:Web Sevices Access in Atlas示例(6) - 在客户端隐藏服务器端类型信息...

    如果要在客户端指定服务器端Web Service方法所接收的参数类型,就必须在客户端通过"__type"来指定,但是这就暴露了服务器端的具体类型了,这可不太好.现在我们就来看一下应 ...

  3. WPF入门教程系列三——Application介绍(续)

    接上文WPF入门教程系列二--Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...

  4. 应用服务Application Services

    Application Services用于将域逻辑公开给表示层.使用DTO(数据传输对象)作为参数从表示层调用应用服务.它还使用域对象来执行某些特定的业务逻辑,并将DTO返回给表示层.因此,表示层与 ...

  5. SpringSecurity权限管理框架系列(六)-Spring Security框架自定义配置类详解(二)之authorizeRequests配置详解

    1.预置演示环境 这个演示环境继续沿用 SpringSecurit权限管理框架系列(五)-Spring Security框架自定义配置类详解(一)之formLogin配置详解的环境. 2.自定义配置类 ...

  6. spring boot 跨域请求_SpringBoot 系列教程 web 篇之自定义请求匹配条件 RequestCondition...

    191222-SpringBoot 系列教程 web 篇之自定义请求匹配条件 RequestCondition 在 spring mvc 中,我们知道用户发起的请求可以通过 url 匹配到我们通过@R ...

  7. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  8. 【5年Android从零复盘系列之十七】Android自定义View(12):手势绘制及GestureOverlayView事件详解(图文)

    [5年Android从零复盘系列之十七]Android自定义View(12):手势绘制及GestureOverlayView事件浅析 1.基础 掌握View体系事件分发与处理,参考Android自定义 ...

  9. STM8S系列基于STVD开发,自定义printf函数+TIM5精确延时函数模块化工程示例

    STM8S系列基于STVD开发,自定义printf函数+TIM5精确延时函数模块化工程示例

最新文章

  1. Linux vim 中文显示乱码解决方法
  2. 解决首次访问jenkins,输入默认密码之后,一直卡住问题
  3. OpenGL Texture Coordinates纹理坐标的实例
  4. 基于.NET的WebService的实现
  5. Python与数据库[2] - 关系对象映射/ORM[4] - sqlalchemy 的显式 ORM 访问方式
  6. python 获取几小时之前,几分钟前,几天前,几个月前,及几年前的具体时间
  7. 修改YUM源为本地光驱
  8. ofbiz中用 ajax 几点注意
  9. QT的QWebEngineView内存泄漏
  10. JDK 的下载与安装(非常详细!)
  11. c语言实验报告(四) 从键盘输入字符串a和字符串b,并在a串中的最小元素(不含结束符)后面插入字符串b....
  12. 量化交易员珍藏的10本书,一般人不会教你的事
  13. maxima学习笔记(一)
  14. 考拉海购成重构互联网版图的最大“变量”
  15. 电影——《小萝莉的猴神大叔》
  16. 【统计学笔记】如何判断变量间相关关系,并建立一元线性回归模型?
  17. MKOnlinePlayer在线音乐播放器
  18. 云服务器的IP是显示哪里,云服务器的ip在哪里看
  19. 2021秋招CVTE面经
  20. CSS如何添加阴影效果?

热门文章

  1. 如果我要...(??版)
  2. Girton events
  3. 和老师一定要保持沟通
  4. Google colab基本页面layout和需要注意的地方!
  5. GO To Definition的背后操作【VS2015 C#】
  6. zabbix源码安装 令人窒息的操作
  7. python-------装饰器
  8. 紧迫感:在危机中变革
  9. SQLserver2008数据表的修改操作
  10. hdu-Frosh Week(树状数组)