现在的位置: 首页 > 综合 > 正文

WCSF中注册服务的深入研究

2012年09月15日 ⁄ 综合 ⁄ 共 4516字 ⁄ 字号 评论关闭

    众所周知, WCSF(Web Client Software Factory)框架是三层MVP结构, 对业务逻辑的操作都是通过OB(Object Builder)以服务的形式注入到当前模块或者全局模块中, 在通过依赖方式在View, Presenter和Controller中使用.

    以下代码描述了一个典型WCSF服务的使用:

    在每个模块的ModuleInitializer类中, 可以找到类似的方法:

// 注册全局模块服务
protected virtual void AddGlobalServices(IServiceCollection globalServices)
{
    // 一个注册类型为IGlobalService, 实现类型为GlobalService的服务.
    IGlobalService gService = globalServices.AddNew<GlobalService, IGlobalService>();
}

// 注册当前模块服务
protected virtual void AddModuleServices(IServiceCollection moduleServices)
{
    // 一个注册类型为IModuleService, 实现类型为ModuleService的服务.
    IModuleService mService = moduleServices.AddNew<ModuleService, IModuleService>();
}

    以上两个服务, 当程序加载时, 会自动被OB创建并维护.

    在WCSF中注册服务还可以通过Add方法和配置文件来完成, 详细可查看WCSF的帮助文档, 这里不再赘述.

    当服务被注册之后, 就可以使用依赖的方法从OB中提取相应的服务并使用, 在View, Presenter和ModuleController类中都可以使用以下代码:

private IGlobalService _globalService;
/// <summary>
/// 使用已经注册的服务
/// </summary>

[ServiceDependency]
public IGlobalService GlobalService
{
    set { _globalService = value; }
}

    该setter方法在对象被实例化后马上调用, 而value值就是通过依赖从OB中获得的IGlobalService类型的服务.

    服务还可以在构造函数中获得, 详细可查看WCSF的帮助文档.

    总结上述讨论, 有一个问题我们不得不研究下, 为什么上述属性的setter方法会被调用, 而这个value值又是从何而来?

    有一点很明确, 就是[ServiceDependency]这个属性修饰必须被加上. 如果, 我们在模块中加入一个自己定义的类, 然后对这个自定义类中也加入上述依赖服务的代码:

    /// <summary>
    /// 重写一个用于身份认证的Membership类
    /// </summary>
    ///
    public class WFFMembershipProvider : MembershipProvider
    {
        public WFFMembershipProvider()
        {
        }

        /// <summary>
        /// 依赖当前注入的一个全局服务.
        /// </summary>

        public IGlobalService _globalService = null;
        [ServiceDependency]
        public IGlobalService GlobalService
        {
            set { _globalService = value; }
        }

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

        }
        ......
    }

    这里的例子是一个用于做身份认证的类, 如果这个类被装载, 在用户登录的时候其将被初始化(其实也不用这么复杂, 直接定义一个普通类, 然后随便找个地方new一下即可), 但这个时候, 系统并不会按照预期的去调用GlobalService属性的setter方法, 也就是说, 在一个自定义类中, 不能像WCSF的标准类中那么轻易的使用[ServiceDependency].

    那么问题出在哪里? 这种情况下, 我们只能翻看WCSF的CompositeWeb库的源码(WCSF2007可通过Reflection工具, WCSF2008可直接安装源码), 大家都知道, 在Asp.net中, 客户端浏览器与服务器的交互都是通过一个个请求来完成, 而这些请求在服务器端其实就是一个个的Page对象, 在WCSF框架中, 所有的页面类都是继承于Microsoft.Practices.CompositeWeb.Web.UI.Page, 我们查看其源码, 可以发现如下方法:

protected override void OnPreInit(EventArgs e)
{
    base.OnPreInit(e);
    WebClientApplication.BuildItemWithCurrentContext(this);
}

    BuildItemWithCurrentContext方法: 从字面来看就是为当前的请求创建对象, 而这些对象应该就是指注入的服务. 于是, 我们可以在WFFMembershipProvider的Initialize方法中也加入这个代码, 再次编译运行, 发现GlobalService属性的setter方法将被调用, 因此, 我们可以认为, 在WebClientApplication.BuildItemWithCurrentContext(object)方法中就是搜索object中以[ServiceDependency](当然还有[CreateNew]和[InjectionConstructor])修饰的属性, 然后从OB中取出与之类型对应的服务, 再将这个服务赋给其setter方法.

    通过上述的讨论, 我们就可以在一个自定义的模块类中使用已注册的全局或者模块服务了. 下面, 我们提出一个新的构思, 能否在程序的任何类中都使用一个已知类型的服务? 理论上, 这个构思是可行的, 因为服务都是被维护在OB中, 但是OB的代码实在是太复杂了(很遗憾, 笔者也未完整系统的研究过OB), 因此, 笔者还是打算从WCSF框架的一些基础库下手.

    在ModuleInitializer类中的load方法中, 可以看到以下代码:

AddGlobalServices(container.Parent.Services);
AddModuleServices(container.Services);

    也就是说如果能获得某个模块的CompositionContainer对象, 就能轻易的取出其中的服务. 从WCSF的框架结构, 我们知道Module都是依附与Application的, 因此, 我们仍然从WebClientApplication入手, 查看其源码, 发现一个很令人兴奋的属性: RootContainer, (其实WebClientApplication已经提供了一个GetModuleContainer的方法, 可惜不是public的), 在这个RootContainer中我们可以获得一个全局管理模块加载的服务IModuleContainerLocatorService, 并进一步获得期望类型的服务.

    综上讨论, 我们可以写出以下的静态方法:

/// <summary>
/// 获得当前context所在模块中注入的服务.
/// </summary>

public static object GetInjectService(Type serviceType, IHttpContext context)
{
    object service = null;
    if (context.ApplicationInstance is WebClientApplication)
    {
        // 获得WebApplication对象.
        WebClientApplication app = (WebClientApplication)context.ApplicationInstance;

        // 获得用来加载模块的服务.
        IModuleContainerLocatorService locatorService = app.RootContainer.Services.Get<IModuleContainerLocatorService>();
        // 获得当前context对应模块中的container.
        CompositionContainer container = locatorService.GetContainer(context.Request.AppRelativeCurrentExecutionFilePath);

        // 从container中获得服务.
        service = container.Services.Get(serviceType);
    }
    return service;
}

如果希望获得当前正在访问的页面所在模块中的服务, context参数可使用如下值:

IHttpContext context = new Microsoft.Practices.CompositeWeb.Web.HttpContext(System.Web.HttpContext.Current)

以上方法可以进一步演变, 如果已经有了WebClientApplication或者RootContainer的情况下, 我们在获得了locatorService实例后, 可直接通过期望类型的服务所在的模块的服务器路径来获得该模块的container:

// 获得SampleModule的container.
CompositionContainer container = locatorService.GetContainer(“~/SampleModule/Web.config”);

至此, 我们就可以在WCSF的标准类, 自定义模块类, 任意类中获得已经注册的服务并使用了.

虽然WCSF框架从设计到发布已经有一段时间了, 但是在国内, 使用的人并不是很多, 也很难找到中文的帮助. 笔者希望本文能对正在使用和准备使用WCSF框架的朋友有所帮助, 如果大家有什么好的想法和心得也希望能够写出来, 一同学习交流进步.

PS: 如果转载请注明出处, 谢谢^^ 

抱歉!评论已关闭.