代码分析

NerdDinner分析

NerdDinner的一个优点就是接口设计非常恰当,我觉得编程最有搞头的可能就是接口设计,很多“问题代码”都是接口设计不当造成的。

创建与编辑的方法接口对比:

创建:public ActionResult Create(Dinner dinner)

编辑:public ActionResult Edit(int id, FormCollection collection)

为什么Edit不用Edit (Dinner dinner)这种看起来更简洁的写法呢?首先,Edit页最后是要保存数据的,但这些数据往往不是在页面上完全包含,比如创建日期一般就不会放在编辑页面上。所以Edit方法不能用类似Create的那种接口,而是先根据id来从数据源取出Dinner对象,然后用UpdateModel(dinner);方法更新那些在编辑页面上有的字段,然后再保存到数据源。

接口举例

比如public ActionResult ViewErrorLog(ViewErrorSearchOption searchOption)就是很好的接口设计,因为一进入这个方法就能直接用参数了,public ActionResult CalendarAdmin(FormCollection formCollection)可能是不太好的,是可以进一步改进的接口,因为首先,这个方法的功能不单一,是一碗意大利面(意大利面主要原因并不是某种编程语言(比如Basic)造成的,而是编程者的编程思想,高手即使用汇编也能写出比较容易读懂的代码),里面用了大量的formCollection["XXX"]形式的代码来一个一个获取参数,缺乏对输入参数的抽象。解决方法是从2方面,一是“拆”,即页面改造,用Ajax+PartialView对功能做拆分,二是抽象,对输入和输出参数做抽象,使参数获取和使用简单化。

当一个页面简单的时候,这个页面可能适合整体做,不需要做什么功能拆分和Ajax,比如ViewErrorLog页面。但当页面包含了搜索条件、显示搜索结果、编辑保存等众多功能时,必须拆。拆了会变成几个方块,比如BusinessBranchAdmin。不拆就会变成意大利面,比如CalendarAdmin没有拆。注意BusinessBranchAdmin代码仍然较多,主要是页面上有级联下拉框和较多的页面元素,这里面有很多都是可以进一步优化的,要解决的主要问题是功能重复的时候,代码没有设计成可重用,这其实在开发过程中是正常现象,因为不可能一下子就设计出一个最优的系统,但一直保持这样,一直不去优化,就会给进一步开发及后期维护工作带来巨大工作量和Bug,经常否定自我,优化老代码才能走的顺。

接口代码分析

BusinessBranchAdmin里分成3个方法:

1. public ActionResult BusinessBranchAdmin()方法处理带有BusinessId参数的Get请求,这是一种容错的写法,当没有参数的时候,它会显示NotFound页面,现在感觉是多此一举了,用public ActionResult BusinessBranchAdmin(decimal businessId)更合适,因为这是后台代码,后台一般人不会乱弄个Url乱搞的。正常情况下return View(formModel);

2. public ActionResult Edit(int BusinessBranchId)处理获取Get请求,它由客户端的Ajax代码showEdit方法触发,正常情况下它返回的是一个PartialViewreturn PartialView("BusinessBranchForm" , info);不过我检查代码时发现了笔误,写的是return View("BusinessBranchForm", info);不过两种写法都能正常运行,不过因为BusinessBranchForm是用户控件,所以用PartialView更合适。数据返回浏览器后由onGetDetailComplete方法处理,显示在divEdit里。

3. public ActionResult Edit(int BusinessBranchId, FormCollection formValues)处理Post请求,由客户端postEditForm方法触发,这个方法里用到了通用提交表单的postForm方法。服务器端处理成功后会return PartialView("EditSuccess");失败会设置错误信息并return PartialView("BusinessBranchForm", info);

整个页面的JS只有非常简短的,一目了然的,可望词生义的5个函数,共25行。

CalendarAdmin里有很多方法,拿最重要的前2个方法为例:

1. public ActionResult CalendarAdmin(),这个方法处理Get请求,既处理最初显示搜索条件的任务,又处理后续的根据参数查询数据的任务,以及分页逻辑等等众多功能,非常不容易看明白。有几个编码问题比较突出:一个是获取参数的GetParamters方法,里面全部是out参数,还有些方法里会改变某些参数的值,这都不是一种好的接口设计,要解决这个问题,最管用的还是“拆”字诀,然后对输入输出数据做抽象。

2. public ActionResult CalendarAdmin(FormCollection formCollection)处理Post请求,处理首次检索任务,里面有非常多与第一个方法相同的逻辑功能代码。我发现里面有一个意大利面的铁证:Goto语句,可见逻辑之复杂。解决方法还是“拆”字诀,拆才能简单化,拆才能清晰易懂。

Html结构分析

 

结构良好的:

 

CSS

   <style type="text/css">

            .outputTable

            {

                background-color: White;

                width: 100%;

            }

            .outputTable tr

            {

                height: 25px;

            }

            .outputTable th

            {

                background-color: #C0D6F6;

            }

            .outputTable td

            {

                background-color: #ECF3FD;

            }

        </style>

 

Html

        <table border="0" cellpadding="3" cellspacing="2" id="divCalendarList" class="outputTable">

            <%--<caption>

                Search Result</caption>--%>

            <thead>

                <tr>

                    <th>dgtStartDateTime

                    </th>

                </tr>

            </thead>

            <tbody>

                <% foreach (SIL.RealStew.DAL.Entities.BusinessBranch c in Model.BusinessBranches)

                   { %>

                <tr>

                    <td>

                        <a href="javascript:showEdit(<%= c.BuBrId %>)">BuBrName</a>

                    </td>

                </tr>

                <% } %></tbody>

        </table>

 

结构不好的(class="bold" height="25"bgcolor="#C0D6F6"之类的完全可以写在CSS里):

 

<table border="0" cellpadding="3" bgcolor="#ffffff" cellspacing="2" id="divCalendarList" style="width: 100%">

                <tr class="bold">

                    <td height="25" bgcolor="#C0D6F6">

                        <%= Html.Resource("dgtStartDateTime")%>

                    </td>

                </tr>

                <% foreach (SIL.RealStew.BLL.Home.Model.CalendarViewEntry c in Model.CalendarList)

                   { %>

                <tr bgcolor="#ECF3FD" >

                    <td height="25">

                        <%= c.CaEnStartDate == null ? "" : ConvertDateTime.ConvertToCurrentDatetimeFormat(c.CaEnStartDate.Value.Date.ToString()) + (c.CaEnStartTime == "00:00" ? "" : "<br>" + c.CaEnStartTime)%>

                    </td>

                </tr>

                <% } %>

            </table>

 

多语言代码分析

1. Global.asax里的protected void Application_BeginRequest(object sender, EventArgs e)方法

2. LanguageEnvironmentLanguage属性

3. EnquiryLoadManager类的GetTradeDetail方法的language参数可以省去,方法里用语言的时候用Language.EnvironmentLanguage就行了,不需要从外面传进来。需要用Culture的地方,比如格式化日期、货币的地方,用CultureInfo.CurrentCulture就行了。

编程风格分析

1. 最常见的问题是超长的行,比如memberCallCentreModel.CountryList = new SelectList(countryEntityList as IEnumerable, "CountryID", "CountryName", memberCallCentreModel.SearchCountryID);

 

如果写成以下风格会更有可读性:

            memberCallCentreModel.CountryList =

                new SelectList(countryEntityList as IEnumerable

                    , "CountryID"

                    , "CountryName"

                    , memberCallCentreModel.SearchCountryID);

2. 代码(特别是Html)的缩进和空行特别乱,就不举例了

3. JS的存放,我并不认为所有的JS都应该放到单独的JS文件,首先有很多JS代码都会用到服务器端变量,直接在页面里写会更方便,假如代码太多,放在页面上就会影响视线了,不容易弄清楚页面结构,适合放在单独的JS文件里。少量代码且用到服务器端变量的适合直接放在页面上,如果不论什么都放到JS文件里,则会增加文件的数量,万一有什么修改的话,还需要打开2个文件对比着看,增加工作量。JS文件缓存可以节省流量,提高速度,但现在带宽都挺高的,一小段JS并不会带来性能问题,反而可以减少请求数,至少能在首次载入时增加速度。

4. int pageSize = int.Parse(ConfigurationManager.AppSettings["PageSize"]);这是典型的HardCoding形式,不利于维护,用我写的SolutionConfig.PageSize可以避免之。

5. 无效代码:即被注释掉的代码,是很影响视线的。我的习惯是在重构时将过时的代码注释掉,当重构完成时,删掉其中的大部分,只留极少数有参考价值的代码。然后每次看到漏删的完全没用的无效代码时,彻底删除之。

通用级连下拉框代码的分析

1. 本示例代码的数据源是Xml文件,集成到数据库里需要做适当改动

2. Web.config里配置级联下拉框的KeyKey的层次结构以及Key与数据提供类的映射。

3. 顶级下拉框基础数据提供类需要继承SelectListProviderBase

比如public class Countries : SelectListProviderBase

4. 有父亲的数据提供类要继承HasParentBase

比如CitiesSuburbsStreets

5. 取下拉框数据用DdlFormModel.CreateInstance方法,这个方法有3个重载,一个用于顶级下拉框,一个用于中间级下拉框,一个用于末级下拉框

6. 页面上引入commonddl.js

7. 页面上引用通用级联下拉框用户控件,例如:

<% Html.RenderPartial("GetDdl", Model.Countries); %>

8. 下拉框外层元素的Id命名约定:为内层下拉框Id后加Wrapper

通用分页存储过程分析

SP名称:Utility_GetOnePage

核心代码:ROW_NUMBER() OVER (order by '+@OrderBy+') as Pos

其它:都是一些拼SQL的语句

注意事项:调用这个存储过程的存储过程必须在最后有Select语句,正常有的没有问题,如果没有,需要加上(一般写成Select 1),因为要兼容Netail工具让其生成返回IDataReader的方法,否则会生成返回void的方法。

调用方法:参考SILCustom_Business_Search存储过程

 

你可能感兴趣的:(代码)