想一想还挺有意思的,从内容代码混杂,脚本横行的asp时代,到“CodeBehind” 的服务控件端编程时代,再到现在各种丰富和强大js库的兴起,各种眼花缭乱的js插件,“胖客户端”的流行,不得不感慨一下,怎么玩着玩着又玩回去了。不过这种“回去”不是倒退,而是顺应发展趋势,同时也是重拾本源,毕竟所有web呈现和控制都是html+css+js【当然其中要排除flash、Silverlight这些“异类”】。相信不久的将来Html5会使前端应用带来巨大的变化。
不废话了,从本篇开始进入正题,一步步的介绍Smart UI开发框架的使用。
首先,Smart UI与目前大多数“胖客户端”应用【半服务端+半前端】有所不同,所有UI层全部都是htm+js实现,不存在服务端内容。因此开发模式会传统的不一样,而且对基础技能的要求(对比asp.net)也会高些。只有比较好的掌握了html、css、js、jquery、json这些知识,才能充分发挥纯前端开发的强大优势。
本篇以一个新闻增删改查的例子大概的说明一下Smart UI的开发流程:
- 第一步,数据库设计
表:News
N_ID 【int】:主键自增id
N_Title 【nvarchar(50)】: 新闻标题
N_Centent【nvarchar(1000)】: 新闻内容
N_Date【Datetime】: 发布时间
- 第二步,创建业务数据类BRNews
首先将数据库生成实体文件,【工具在NBearLite.QueryColumnsGenerator项目生成的exe文件】,然后在放到Bll层中,实例在Demo/Modal/目录下。实体列的访问namespace为:StructModals.
然后在Bll层创建业务实现类BRNews,实例在Demo/BR目录,内容如下:
{
/// <summary>
/// 执行Function方法
/// </summary>
/// <param name="fnCode"> 功能编号 </param>
/// <param name="postContent"> post的数据 </param>
/// <param name="error"> 自定义返回错误信息 </param>
/// <returns> ReponseData </returns>
public static object RunFunction( string fnCode, string postContent, ref string error)
{
object objReponseData = null ;
switch (fnCode)
{
case " fn_ns01 " :
objReponseData = SelectNews(postContent);
break ;
case " fn_ns02 " :
objReponseData = SaveNews(postContent);
break ;
case " fn_ng03 " :
objReponseData = GetNewsByID(postContent);
break ;
case " fn_nd04 " :
objReponseData = DelNewsByIDs(postContent);
break ;
}
return objReponseData;
}
/// <summary>
/// 查询方法
/// </summary>
/// <param name="postContent"> 查询参数 </param>
/// <returns> 返回列表查询对象(ResponseListData) </returns>
public static object SelectNews( string postContent)
{
UISearchHelper ma = new UISearchHelper(StructModals.News, postContent); // 创建查询类
ma.Select();
if (ma.HasCondition) // 判断是否有查询条件
ma.Where(StructModals.News.N_Title, EConditionOperate.Contains); // 添加查询项
return ma.GetResponseListData();
}
/// <summary>
/// 保存方法
/// </summary>
/// <param name="postContent"> 更新参数 </param>
/// <returns> 是否保存成功(bool) </returns>
public static object SaveNews( string postContent)
{
UIUpdateHelper saveHelper = new UIUpdateHelper(StructModals.News, postContent); // 创建更新类
if (saveHelper.HasValues) // 判断是否有更新参数
return saveHelper.AddUpdateColumn(StructModals.News.N_Title, StructModals.News.N_Centent) // 自动获取值的更新字段
.AddUpdateColumn(StructModals.News.N_Date, DateTime.Now) // 手动设置值更新字段
.Save(); // 保存方法,会自动判断是否有主键,从而进行insert或是update操作
return false ;
}
/// <summary>
/// 根据ID获取内容的方法
/// </summary>
/// <param name="postContent"> ID值 </param>
/// <returns> 单个对象(DataTable) </returns>
public static object GetNewsByID( string postContent)
{
return DBHelper.GetOneDataByID(StructModals.News, postContent); // 根据ID获取单条数据
}
/// <summary>
/// 根据ID串批量删除的方法
/// </summary>
/// <param name="postContent"> ID串 </param>
/// <returns> 是否删除成功(bool) </returns>
public static object DelNewsByIDs( string postContent)
{
if ( ! string .IsNullOrEmpty(postContent))
return DBHelper.DeleteByIDs(StructModals.News, postContent) > 0 ; // 根据主键id串批量删除方法,有事务方法重载
return false ;
}
}
BRNews类中,所有方法都为静态,返回参数都为object,其中RunFunction方面则是根据前台传递fncode匹配各个方法,case的每个对象都对应着一个方法,所有方法的参数都一样,一个postData(前台传递的数据),一个error(可选参数,自定义错误)
其他四个方法,分别对应着增删改差四个功能,除了列表翻页的查询是返回ResponseListData固定格式外,其他方法的返回值可以为任何类型。
在编写BR类时,大多数情况,开发人员不用关心前台Post过来的数据,通过实例化UISearchHelper【查询帮助类】和UIUpdateHelper【更新帮助类】,开发人员只需要关心查询那些字段,条件是什么,更新那些字段,帮助类会自动的解析和匹配数据。
- 第三步,注册BRNews类
完成BRNews后,还需要将其注册到功能适配器FnAdapter类中【Smart UI按模块划分目录,每个模块下的目录都存在一个FnAdapter,实例中是Demo/FnAdapter.cs】,FnAdapter内容如下:
{
public static object RunFuntion( string BRName, PostData postData, ref string error)
{
switch (BRName)
{
case " BRNews " :
return BRNews.RunFunction(postData.No, postData.Body, ref error);
case " BRUsers " :
return BRUsers.RunFunction(postData.No, postData.Body, ref error);
case " BRProduct " :
return BRProduct.RunFunction(postData.No, postData.Body, ref error);
case " BRQuestion " :
return BRQuestion.RunFunction(postData.No, postData.Body, ref error);
default :
return null ;
}
}
}
FnAdapter中只有一个RunFunction方法,UIHandle下的统一数据接口PostHandle会调用该方法。每个FnAdapter都需要注册到PostHandle中【当然也不是必须的,可以略过,直接通过BR类与PostHandle对应,不过不推荐这样使用,因为当BR类比较多时,通过FnAdapter多加一次检索性能上会提很多,而且也会更加容易维护和管理】。
PostHandle类:
{
protected override object GetResponseData(PostData postData, FnCode fncode, ref string error)
{
return RunFuntion(postData, fncode, ref error); ;
}
public static object RunFuntion(PostData postData, FnCode fncode, ref string error)
{
switch (fncode.Module)
{
case " UIDemo " :
return FnAdapter_Demo.RunFuntion(fncode.ClassName, postData, ref error);
case " SmartUI " :
return FnAdapter.RunFuntion(fncode.ClassName, postData, ref error);
default :
return null ;
}
}
}
PostHandle类继承抽象类PostHttpHandler,重写GetResponseData方法,传入解析过的前台Post过来的数据,返回请求的数据。各个模块的FnAdapter配置在RunFunction方法中。
【注:在整个后台编码中没有用任何接口去进行约束,在此我考虑的是相关的内容结构相对比较简单,为了所有业务方法都能static化,所以没有进行约束。另外,关于配置,如果想更加简洁还有一种方案,在UI层配置fncode的时候,加入BLL的dll信息,使用反射匹配每一个后台方法,这样就只需要在一个位置配置,不过性能的代价比较高,所以没有采用。】
至此,所有后台内容完成,接下来就到重点部分 — 前台功能实现,今天电脑死了两次,写了两遍都没保存,郁闷了。唉,没心情继续了,况且前台的内容比较多,等明天在继续。