在我之前介绍的混合式开发框架中,其界面是基于 Winform 的实现方式,后台使用 web API、WCF 服务以及直接连接数据库的几种方式混合式接入,在 Web 项目中我们也可以采用这种方式实现混合式的接入方式,虽然 Web API 或者 WCF 方式的调用,相对直接连接数据库方式,响应效率上略差一些,不过扩展性强,也可以调动更多的设备接入,包括移动应用接入,网站接入,Winfrom 客户端接入,这样可以使得服务逻辑相对独立,负责提供接口即可。这种方式中最有代表性的就是当前 Web API 的广泛应用,促进了各个接入端的快速开发和独立维护,极大提高了并行开发的速度和效率。在企业中,我们可以合理规范好各种业务服务的 Web API 接口,各个应用接入端可以独立开发,也可以交给外包团队进行开发即可。
从一开始,我们的 Web API 的设计目的就是为了给各种不同的应用进行接入的,例如需要接入 Winform 客户端、APP 程序、网站程序、以及微信应用等等,由于 Web API 层作为一个公共的接口层,我们就很好保证了各个界面应用层的数据一致性。
上图介绍了各种应用在 Web API 的接口层之上,一般情况下,我们这层的接口都是提供标准的各种接口,以及对身份的认证处理等等,在 Web API 层更多考虑的业务范畴的相关接口,而在各个界面层,考虑的是如何对 Web API 进行进一步的封装,以方便调用,虽然 Winform 和 Web 调用 Web API 的机制有所不同,不过我们还是可以对 Web API 的客户端封装层进行重用的。
在 Winfrom 界面调用混合式接入的接口方式,它的示意图如下所示,主要的思路是通过一个统一的门面层 Facade 接口层进行服务提供,以及客户端调用的封装处理接口。
随着 Web API 层的广泛使用,这种方式带来了非常大的灵活性,通过在框架层面对各个层的基类进行封装,可以大大简化所需的编码,以及提供统一、丰富的基础接口供调用。
由于 Winform 调用 Web API 的时候,客户端对 Web API 层进行了一个简单的包装,这种方式可以简化对 Web API 接口的使用,只需要通过调用封装类,并传入相关的参数就可以获得序列化后的对象(包括基础对象和自定义类对象)。
这种封装的方式,由于对基类的统一实现,以及提供对 URL 地址、参数的组装等处理,非常利于 Winform 界面后台代码进行调用 ,加快 Winfom 界面功能的开发。例如我们从进一步细化的架构图上,可以看到整体各个层的一些基类(绿色部分)。
在基于 Web API 层的构建上,我们提供了 Web API 服务层的提供了 BaseApiController 和 BusinessController<B, T> 的 Web API 控制器基类对常规的业务处理进行封装;在 Web API 服务调用层上,我们提供了 BaseApiService<T> 的基类进行封装常规接口;
同时提供 IBaseService<T> 的 Facade 门面层的统一接口,以及 CallerFactory<T> 的调用方式供 Winform 后台代码进行接口调用。
这种在 Web API 的基础上进行接口的封装,可以极大简化接口的调用,同时也可以提供给 Web 端的后台控制器使用,非常便于使用,下面就介绍在 Web 项目中进行混合式接入的实现过程。
参照 Winform 混合式接入的方式,我们也可以利用这种方式应用于 Web 框架上,具体的分层关系如下所示。
上图整合了两种非常常用的接入方式:Web API 服务接入、直接连接数据库的接入,一种具有非常强大的特性,一种具有快速的访问效率,各有其应用场景,我们在不同的业务环境进行配置,使其适应我们实际的应用即可,一般情况下,我们建议采用 Web API 方式进行构建整个业务系统的生态链。
Web API 的接口调用,可以通过两种方式进行,一种是采用纯 JS 框架,类似 AngularJS 的方式,通过其控制器进行相关接口的调用;还有一种方式,采用 Asp.NET 的 MVC 方式,前端界面通过 JS 调用后端的控制器实现数据处理,具体逻辑有后端逻辑控制器进行 Web API 的处理,我们这里采用后者,以实现较为弹性的处理。
相对 Winform 来说,Web 上的混合方式接入相对复杂一些,虽然 Winform 的界面类似 Web 的 MVC 中的视图 html 代码,Winform 后台逻辑代码类似视图的控制器对象,但是确实麻烦一些,相当于我们还需要在 Web 界面的后台控制器 Controller 上在封装下相关的处理接口。
在整个基于混合式接入方式的 Web 项目中,对于 Web API 接口的使用,整个项目的结构如下所示。
有了这些图示的说明,我们应该对整体有一个大概的了解,对于进一步的细节问题,我们可能依然不是太清楚,需要以具体的项目代码工程进行介绍。
我们可以考虑的是多种数据库的接入支持,如 SQLServer、Oracle、SQLite、Access,或者 PostgreSQL 的支持,这些都是基于关系型数据库的支持,具有很好的可替代性和标准一致性。
它们可以通过遵循统一的 SQL 或者部分自定义的 SQL 语句进行,或者通过存储过程实现,均可以实现相应的功能。
对于数据库不同的支持方案,我这里采用了 Enterprise Library 的数据库访问组件进行一致性的支持,这样可以降低各个不同数据库模型的处理,统一使用这种组件访问方式,实现不同数据库的访问。
业务逻辑层,是有几个不同的层进行综合的使用。如项目中的核心层如下所示,包括了业务逻辑层 BLL、数据访问层 DAL(不同的实现层)、数据访问接口层 IDAL、以及传递数据的 Entity 实体层。
这些模块,在各个层上都有标准的基类用来实现对接口或者功能的封装处理。
如 BLL 层的继承关系如下
- /// <summary>
- /// 基于BootStrap的图标
- /// </summary>
- public class BootstrapIcon: BaseBLL
如 IDAL 层的继承关系如下
- /// <summary>
- /// 基于BootStrap的图标
- /// </summary>
- public interface IBootstrapIcon: IBaseDAL
基于 Oracle 的数据访问层在 DALOracle 里面,我们看到起继承关系如下。
- /// <summary>
- /// 基于BootStrap的图标
- /// </summary>
- public class BootstrapIcon: BaseDALOracle,
- IBootstrapIcon
实体层继承关系如下所示。
- /// <summary>
- /// 基于BootStrap的图标
- /// </summary>
- public class BootstrapIconInfo: BaseEntity
这些模块,由于有了基类的封装处理,多数逻辑不用再重写代码,关于它们具体的内容,可以参考之前的开发框架介绍文章了解,这里不再赘述,主要用来介绍其他模块层的继承关系。
Web API 如果业务模块比较多,可以参考我上篇随笔《》使用 Area 区域对业务进行分类管理,一般情况下,我们为每个 Web API 的接口类提供了基类的管理,和我们其他模块的做法一样。
- /// <summary>
- /// 所有接口基类
- /// </summary>
- [ExceptionHandling] public class BaseApiController: ApiController
以及
- /// <summary>
- /// 本控制器基类专门为访问数据业务对象而设的基类
- /// </summary>
- /// <typeparam name="B">业务对象类型</typeparam>
- /// <typeparam name="T">实体类类型</typeparam>
- public class BusinessController: BaseApiController where B: class where T: WHC.Framework.ControlUtil.BaseEntity,
- new()
这样,基本的增删改查等常规接口,我们就可以在基类里面直接调用业务逻辑类实现数据的处理,具体的业务子类这不需要重写这些接口实现了。
- /// <summary>
- /// 查询数据库,检查是否存在指定ID的对象
- /// </summary>
- /// <param name="id">对象的ID值</param>
- /// <returns>存在则返回指定的对象,否则返回Null</returns>
- [HttpGet] public virtual T FindByID(string id, string token) {
- //如果用户token检查不通过,则抛出MyApiException异常。
- //检查用户是否有权限,否则抛出MyDenyAccessException异常
- base.CheckAuthorized(AuthorizeKey.ViewKey, token);
- T info = baseBLL.FindByID(id);
- return info;
- }
对于 HttpGet 和 HttpPost 的约定,我们对于常规的获取数据,使用前者,如果对数据发生修改,或者需要复杂类型的参数,使用 POST 方式处理。
子类的继承关系如下所示
- /// <summary>
- /// 权限系统中用户信息管理控制器
- /// </summary>
- public class UserController: BusinessController
这样这个 UserController 就具有了基类的一切功能,只需要实现一些特定的接口处理即可。
例如我们可以定义一个新的 Web API 接口,如下所示。
- /// <summary>
- /// 通过用户名称获取用户对象
- /// </summary>
- /// <param name="userName">用户名称</param>
- /// <param name="token">访问令牌</param>
- /// <returns></returns>
- [HttpGet] public UserInfo GetUserByName(string userName, string token) {
- //令牌检查,不通过则抛出异常
- CheckResult checkResult = CheckToken(token);
- return BLLFactory.Instance.GetUserByName(userName);
- }
这样对于 Web API 架构来说,控制器的整个继承关系大概如下所示。
如果使用 Area 区域来对业务模块进行分类,那么整个 Web API 项目的结构如下所示,各个业务区域分开,有利于对业务模块代码的维护,其中 BaseApiController 和 BusinessController 则是对常规 Web API 接口的封装处理。
为了实现 Winform 混合式框架和 Web 混合式框架的共同使用 Web API 服务的封装层,那么我们需要独立一个 Web API 封装层,也就是 ***Caller 层,包含了直接访问数据库方式、Web API 服务接口访问方式,或者加上 WCF 服务访问方式等的封装层。
这个层的目的是动态读取 Web API 接口的 URL 地址,以及封装对 Web API 接口访问的繁琐细节,是调用者能够简单、快速的访问 Web API 接口。
整个 Web API 封装层的架构,就是基于 Facade 接口层进行不同的适配,如直接访问数据库方式、Web API 服务访问方式的适配处理,以便在客户端调用的时候,自动从不同的接口实现实例化对象,从不同方式来获取所需要的接口数据。
对于用户 User 对象来说,我们来举一个例子来说明 Caller 层之间的继承关系。
在 Facade 层的接口定义如下所示。
- public interface IUserService: IBaseService
在 WebAPI 的 Caller 层实现类代码如下所示。
- /// <summary>
- /// 基于WebAPI方式的Facade接口实现类
- /// </summary>
- public class UserCaller: BaseApiService,
- IUserService
对于直接连接方式,实现类的代码如下所示。
- /// <summary>
- /// 基于传统Winform方式,直接访问本地数据库的Facade接口实现类
- /// </summary>
- public class UserCaller: BaseLocalService,
- IUserService
这样我们整理下它们关系如下图所示。
对于不同的业务模块,我们基于对应不同的 Facade 层接口实现不同的 Caller 层,这样即使有很多项目模块,我们单独维护起来也方便很多,在 Winform 客户端或者 Web 端调用 Caller 层的时候,需要引入对应的 Caller 层项目,以及业务核心层 Core。
例如我们需要在使用的时候,同时引入 Core 层和 Caller 层,如下是项目中的部分引用关系。
这个 Web 界面层,主要就是消费 Facade 层接口实现,用来获取数据展示在界面上的,我们界面上通过 HTML + JS Ajax 的方式,实现从 MVC 控制器接口获取数据,那么我们为了方便,依旧在控制器层进行抽象,以便对常规的方法抽到基类里面,这样子类代码就不用重复了。
这样的改变,对于我们已有的 MVC 项目来说,视图处理代码不需要任何改变,只需要控制器对数据访问的处理调整即可,从而实现 MVC 普通方式获取数据的界面层,顺利转换到基于 Web API + 直接访问数据库两者合一的混合式方式上。
原先直接访问数据库的 MVC 视图控制器的设计,基本上类似于 Web API 中控制器的设计过程,如下所示。
而对于 MVC 的 Web 界面层,以混合式方式来访问数据,我们需要引入一个新的控制器来实现适配处理。
这样构建出来的继承关系图,和上面 Web 的 MVC 控制器类似。
不同的是,里面调用的任何访问数据的方法,从原来 BLLFactory<T> 到 CallerFactorry<T> 的转换了,这样就实现了从简单的直接访问数据库方式,切换到混合式访问数据的方式,在 Web 框架里面,可以配置为直接访问数据库,也可以配置为通过 Web API 方式访问数据,非常方便。
例如继承关系类的代码如下所示。
- /// <summary>
- /// 基于混合访问方式的用户信息控制器类
- /// </summary>
- public class UserController: ApiBusinessController
其中对于 Web 界面端的控制器,使用混合式访问方式的后台控制器代码如下所示。
- /// <summary>
- /// 根据角色获取对应的用户
- /// </summary>
- /// <param name="roleid">角色ID</param>
- /// <returns></returns>
- public ActionResult GetUsersByRole(string roleid) {
- ActionResult result = Content("");
- if (!string.IsNullOrEmpty(roleid) && ValidateUtil.IsValidInt(roleid)) {
- List roleList = CallerFactory.Instance.GetUsersByRole(Convert.ToInt32(roleid));
- result = ToJsonContent(roleList);
- }
- return result;
- }
也就是从传统的 BLLFactory<User> 转换为了 CallerFactory<IUserService>,整体性的接口变化很小,很容易过渡到混合式方式的访问。
在 Web 界面端的视图里面,我们基本上就是根据 HTML + Ajax 的 Javascript 方式实现数据的交互处理的,包括显示数据,提交修改等等操作。
同样我们可以通过 JS 的函数进行抽象,把基本的处理函数,放到一个类库里面,方便界面层使用,然后引入 JS 文件即可。
- @ * 脚本引用放在此处可以实现自定义函数自动提示 * @ < script src = "~/Scripts/CommonUtil.js" > </script>/
如下面所示,是调用 JS 自定义函数实现列表数据的绑定操作。
- $("#Dept_ID").on("change",
- function(e) {
- var deptid = $("#Dept_ID").val();
- BindSelect("PID", "/User/GetUserDictJson?deptId=" + deptid);
- });
或者删除的 JS 代码如下所示
- var postData = {
- ids: ids
- };
- $.post("/User/ConfirmDeleteByIds", postData,
- function(json) {
- var data = $.parseJSON(json);
- if (data.Success) {
- showTips("删除选定的记录成功");
- Refresh(); //刷新页面数据
- } else {
- showTips(data.ErrorMessage);
- }
- });
以及对一些 JS 列表树,以及下拉列表,都可以采用 JS 函数实现快速的处理,如下所示。
- var treeUrl = '/Function/GetFunctionJsTreeJsonByUser?userId=' + info.ID;
- bindJsTree("jstree_function", treeUrl);
- $('#lbxRoles').empty();
- $.getJSON("/Role/GetRolesByUser?r=" + Math.random() + "&userid=" + info.ID,
- function(json) {
- $.each(json,
- function(i, item) {
- $('#lbxRoles').append('' + item.Name + '');
- });
- });
以上就是我从整个基于混合式访问的 Web 项目进行讲解介绍,贯穿了整个数据传输的路线和调用路线,当然其中还有很多细节方面有待细讲,以及需要一些比较巧妙的整合封装处理,整个目的就是希望借助混合式的访问思路,实现多种数据接入方式的适配整合,以及最大程度简化子类代码的编写,并且通过利用代码生成工具对整体框架的各个层代码的生成,我们关心的重点转移到如何实现不同业务的接口上来,从而使得我们能够快速开发复杂的应用,而且又能合理维护好各个项目的代码。
一句话总结整个开发:简单、统一、高效。
来源: http://www.cnblogs.com/wuhuacong/p/5850482.html