zoukankan      html  css  js  c++  java
  • 最近一个小Web开发项目的总结

    一直以来很少写Web开发的具体代码,以前是专门有人写这些,我只管管就行了。后来呢,也很少做Web类型项目了,最近有两个项目,牵扯到Web开发,而现在一个人单干,也找不到人来写这些了,只得自己憋手蹩脚的摸索。下面是这段时间的摸索总结。

    一、代码生成器确实好用

    以前是很反代码生成器的,但真的使用过后觉得很好用——简单、直接且控制力强。我用的是园子里李天平(http://ltp.cnblogs.com/)的动软代码生成器。在此,先感谢一下。不过,在感谢李天平之前,我必须先感谢一下郭嘉。

    动软的好处是简单、直接、开源。针对我的应用,我做了以下改进:

    (1)Image的处理问题。代码生成器生成的操作Image部分的代码有小问题,不能插入全部的Image对象,这个手动修改了下。

    (2)生成的代码没有 partial 关键字,不方便扩展。我修改成产生的所有类都是partial类。这样做有什么好处呢?就是对于每个类,你可以在不修改生成的源文件的情况下,申明某个类实现了某些接口,以对代码进行复用。

    例如,假设Album、News、Knowledge这三个类都具有某些相同的属性(ID,Title,AccountId,ViewCount,CreateTime,UpdateTime,等),在代码生成器生成的文件之外,另建一个目录,放一个Interface.cs的文件:

    interface
     1 public interface IContent 
     2 
     3     Guid Id { getset; } 
     4     String Title { getset; } 
     5     String AccountId { get; } 
     6     String Caption { get; } 
     7     Int32 ViewCount { getset; } 
     8     DateTime CreateTime { getset; } 
     9     DateTime UpdateTime { getset; } 
    10 
    11 
    12 public interface IContentVoteable : IContent 
    13 
    14     Int32 VoteCount { getset; } 
    15 
    16 
    17 public partial class Album : IContent 
    18 
    19     public String Caption { get { return "相册"; } } 
    20 
    21 
    22 public partial class News : IContent 
    23 
    24     public String Caption { get { return "资讯"; } } 
    25 
    26 
    27 public partial class Knowledge : IContent 
    28 
    29     public String Caption { get { return "手记"; } } 
    30 }
    31 

    这样就可以对生成的类进行类型“指派”,可用于泛型代码来简化编程。并且这种“指派”是强类型的,可在编译器检查错误的。

    二、分页

    这是一个老话题了。以前我直接用GridView和ObjectDataSource,结果是当需求变化了痛苦不堪。比如,客户要求用 VS2005 ……

    本着拿来主义的原则,拿来了园子里张子阳(http://www.cnblogs.com/jimmyzhang/)的分页代码。感谢张子阳。在感谢张子阳之前,再感谢一下郭嘉。

    具体的开发过程中做了一些思考,做出了以下改变:

    (1)没有使用分页存储过程,而是在C#代码中进行封装,纳入我的基础库里面。这样做的理由是:

    (a)分页代码比较简单,分页的数据库操作比较耗时,作为存储过程对性能提高不明显;

    (b)存储过程使用前需要添加进数据库,而在C#端使用则可以省略这一步骤。

    (c)方便调用、修改和封装。以下是我写的C#端的数据库分页代码:

    代码
     1 public static DataTable GetPagerData(String tableName, String returnColumns, String where, String orderColumnName, Boolean orderDesc, String keyColumnName, Int32 pageSize, Int32 pageIndex, params SqlParameter[] whereParams) 
     2 
     3     if (pageIndex == 1
     4     { 
     5         return GetTopData(pageSize, tableName, returnColumns, where, orderColumnName, orderDesc, RemoveNull(whereParams)); 
     6     } 
     7     else 
     8     { 
     9         String sql = String.Format("select top {0} {1} from {2} where {3} and {6} not in ( select top {7} {6} from {2} where {3} order by {4} {5} )  order by {4} {5}", pageSize, returnColumns, tableName, where, orderColumnName, orderDesc == true ? "desc" : String.Empty, keyColumnName, pageSize * (pageIndex - 1)); 
    10         return Query(sql, RemoveNull(whereParams)).Tables[0]; 
    11     } 
    12 
    13 
    14 public static Int32 GetDataCount(String tableName, String whereparams SqlParameter[] whereParams) 
    15 
    16     String sql = String.Format("select count(*) from {0} where {1}" , tableName, where); 
    17     Int32 result = (Int32)GetSingle(sql, RemoveNull(whereParams)); 
    18     return result; 
    19 
    20 
    21 public static DataTable GetTopData(Int32 top, String tableName, String returnColumns, String where, String orderColumnName, Boolean orderDesc, params SqlParameter[] whereParams) 
    22 
    23     String sql = String.Format("select top {0} {1} from {2} where {3} order by {4} {5}", top, returnColumns, tableName, where, orderColumnName, orderDesc == true ? "desc" : String.Empty); 
    24     return Query(sql, whereParams).Tables[0]; 
    25 
    26 private static SqlParameter[] RemoveNull(SqlParameter[] whereParams) 
    27 
    28     List<SqlParameter> list = new List<SqlParameter>(); 
    29     foreach (SqlParameter sq in whereParams) 
    30     { 
    31         if (sq != null) list.Add(sq); 
    32     } 
    33     return list.ToArray(); 
    34 }

     在这段代码中,我直接将分页和Top集成在一起了,取第一页时,使用的是Top,以优化性能。同时,也省掉Top调用。这样做的好处,在下面能体现出来。

    (2)对于分页部分的代码,我默认进行一下处理:

    (a)url的page参数指现在的pageIndex;

    (b)url的count参数指查询结果的总数量;

    “count” 和 “page ”也可以指定为其它字符串。当是第一页时,去调用 GetDataCount 方法,获得count参数,这个参数直接传递给第二页,第三页……的url,避免查看第二页第三页时,重复获得count,影响性能。

    三、封装和复用

    通过上面的处理,就很方便对代码进行封装和复用了。

    还是以上面的Album,News和Knowledge来说,这三个,编辑都可以从后台加精、推荐、设为热点、锁定、审核通过与否等操作。如何用最简单的代码来实现前台和后台所需要的以下需求呢:

    (1)获得全部加精的对象;获得N天内加精的对象;以上可以按不同的排序方式排序;获得审核通过的对象,获得N天内审核通过的对象;

    (2)获得全部推荐的对象;获得N天内推荐的对象;以上可以按不同的排序方式排序;获得审核通过的对象,获得N天内审核通过的对象;

    (3)获得全部热点的对象;获得N天内热点的对象;以上可以按不同的排序方式排序;获得审核通过的对象,获得N天内审核通过的对象;

    (4)获得最新的对象;获得N天内审核通过的对象;

    (5)获得浏览量最多的对象;获得N天内浏览量最多的对象;获得审核通过的对象,获得N天内审核通过的对象;

    (6)以上检索可指定帐号,也可以是全体帐号。

    ……

    这里假设Album,News和Knowledge在数据库里相关列的列名都是一致的,EnableStatus代表审核通过与否,0代表待审核,1代表审核通过,-1代表审核未通过;IsLocked,IsHot,IsChoiced,IsMarked等编辑的操作项,分别代表是否锁定,是否热门,是否推荐和是否精华。News和Knowledge还有个CategoryId的列,代表所属类别。

    以News为例子,代码为:

    代码
     1 public partial class NewsService : Orc.HairFashion.BLL.News 
     2 
     3     public static NewsService Instance; 
     4 
     5     static NewsService() 
     6     { 
     7         Instance = new NewsService(); 
     8     } 
     9 
    10     public static void UpdateColumnStatus(ICollection<Guid> ids, String columName, int status) 
    11     { 
    12         DbHelper.UpdateColumnStatus(ids, "News", columName, status); 
    13     } 
    14 
    15     #region 分页查找 
    16 
    17     protected static DataTable GetPagerData(String where, String orderColumnName, Int32 pageSize, Int32 pageIndex, params SqlParameter[] whereParams) 
    18     { 
    19         return DbHelper.GetPagerData("News""*"where, orderColumnName, true"Id", pageSize, pageIndex, whereParams); 
    20     } 
    21 
    22     protected static Int32 GetDataCount(String whereparams SqlParameter[] whereParams) 
    23     { 
    24         return DbHelper.GetDataCount("News"where, whereParams); 
    25     } 
    26 
    27     public static List<News> SelectData(String accountId, String mark, String mode, Int32 categoryId, Int32 inDays, String orderColumnName, Int32 pageSize, Int32 pageIndex) 
    28     { 
    29         DataTable table = GetPagerData(" 1=1 " + Util.BuildSqlByMark(mark) + Util.BuildSqlByMode(mode) + Util.BuildSqlByCategoryId(categoryId) + Util.BuildSqlByCreateInDays(inDays) + Util.BuildSqlByAccountId(accountId), orderColumnName, pageSize, pageIndex, Util.BuildSqlParameterByAccountId(accountId)); 
    30         return NewsService.Instance.DataTableToList(table); 
    31     } 
    32 
    33     public static Int32 SelectCount(String accountId, String mark, String mode, Int32 categoryId, Int32 inDays) 
    34     { 
    35         return GetDataCount(" 1=1 " + Util.BuildSqlByMark(mark) + Util.BuildSqlByMode(mode) + Util.BuildSqlByCategoryId(categoryId) + Util.BuildSqlByCreateInDays(inDays) + Util.BuildSqlByAccountId(accountId), Util.BuildSqlParameterByAccountId(accountId)); 
    36     } 
    37 
    38     #endregion 
    39 }

    就只用这简简单单的代码就可以了。这里各个查询选项是“正交”的。accountId 为 null或Empty,则检索全部。mark代表编辑的查询类型,是全部,还是精华还是热门还是最新……,实际上这里最好是传入enum。mode代表审核状态,最好使用enum。这两处没用enum是历史遗留问题。categoryId代表类别id,如果为负数则检索全部的类别,inDays代表检索多少天内的数据,负数代表全部,orderColumnName 是排序列,我默认全部desc了,pageSize 是页大小,pageIndex是页序号。

    这部分代码是在我动软代码生成器的源代码,使它生成带partial的类之前修改的。如果现在写,使用泛型的话,可以将上面的代码进一步简化。

    然后,写一个公用的控件类,以复用共有代码:

    BaseListControl
     1 public abstract class BaseListControl : System.Web.UI.UserControl 
     2 
     3     private Boolean m_showPager = false
     4     private Int32 m_pageSize = 12
     5     private Boolean m_orderByVoteCount = true
     6     private Int32 m_inDays = 7
     7     private String m_caption = ""
     8     private String m_viewCaption = "点击"
     9     private String m_voteCaption = "票数"
    10     private String m_orderBy = "CreateTime"
    11     private String m_mark = ""
    12     private String m_mode = ""
    13 
    14     public Int32 Count = 0
    15     public Boolean ShowPager { get { return m_showPager; } set { m_showPager = value; } } 
    16     public Int32 PageSize { get { return m_pageSize; } set { m_pageSize = value; } } 
    17     public String Caption { get { return m_caption; } set { m_caption = value; } } 
    18     public String ViewCaption { get { return m_viewCaption; } set { m_viewCaption = value; } } 
    19     public String VoteCaption { get { return m_voteCaption; } set { m_voteCaption = value; } } 
    20     public String OrderBy { get { return m_orderBy; } set { m_orderBy = value; } } 
    21     public String Mark { get { return m_mark; } set { m_mark = value; } } 
    22     public String Mode { get { return m_mode; } set { m_mode = value; } } 
    23     public Int32 InDays { get { return m_inDays; } set { m_inDays = value; } } 
    24 
    25     protected void Page_Load(object sender, EventArgs e) 
    26     { 
    27         if (Page.IsPostBack == false
    28         { 
    29             OnPageLoad(); 
    30             InitPager(); 
    31             BindData(); 
    32         } 
    33     } 
    34 
    35     protected abstract PagerControl GetPager(); 
    36     protected abstract Int32 GetResultCount(); 
    37     protected virtual void OnPageLoad() 
    38     { 
    39     } 
    40 
    41     protected void InitPager() 
    42     { 
    43         PagerControl pager = GetPager(); 
    44         if (ShowPager == false
    45         { 
    46             if(pager!=null
    47                 pager.Visible = false
    48         } 
    49         else 
    50         { 
    51             Int32 count = PageUtil.GetCurrentCount(this.Page); 
    52             if (count < 0) count = GetResultCount(); 
    53 
    54             if(pager!=null
    55                 pager.UrlManager = new Orc.Util.AspDotNet.DefaultUrlManager(count, PageSize, "page""count"); 
    56         } 
    57     } 
    58 
    59     protected void BindData() 
    60     { 
    61         Int32 page = PageUtil.GetCurrentPage(this.Page); 
    62         if (page < 1) page = 1
    63         BindData(page); 
    64     } 
    65 
    66     protected abstract void BindData(Int32 page); 
    67 }

    然后是具体的控件:

    Controls_NewsList
     1 public partial class Controls_NewsList : BaseListControl 
     2 
     3     private Int32 m_categoryId = -1
     4     public Int32 CategoryId 
     5     { 
     6         get { return m_categoryId; } 
     7         set { m_categoryId = value; } 
     8     } 
     9 
    10     protected override Orc.Util.AspDotNet.PagerControl GetPager() 
    11     { 
    12         return this.pager; 
    13     } 
    14 
    15     protected override int GetResultCount() 
    16     { 
    17         return NewsService.SelectCount(nullthis.Mark, this.Mode, this.CategoryId, this.InDays); 
    18     } 
    19 
    20     protected override void BindData(int page) 
    21     { 
    22         this.rpList.DataSource = NewsService.SelectData(nullthis.Mark, this.Mode, this.CategoryId, this.InDays, this.OrderBy, this.PageSize, page); 
    23         this.rpList.DataBind(); 
    24     } 
    25 }

    这样一封装就超级好用,可以显示pager也可以不显示,可以用在分页里,也可以用在栏目的首页,且代码量几乎降低到最低,而性能几乎提高到最高,还可以根据各种参数进行缓存。

    四、抱怨

    尽管使用了这些技巧,Web开发还是太累。一个月坐下来还没做其它项目三五天赚的钱多,并且技术支持比其它类型项目难得多。做一个Web项目后悔一次,今后,尽量不做了,除非价格很高或自己用。

  • 相关阅读:
    MVC对session或cookie保存的值在js中做处理
    JQuery判断是否是移动端
    C# Guid 和 JQuery Guid
    JQuery Cookie操作
    DES置换表加密
    RSA的基础运算
    minikatz免杀之msf加载bin文件
    minikatz免杀之Out-EncryptedScript加密
    vue 多选框
    小程序加载更多,上拉刷新
  • 原文地址:https://www.cnblogs.com/xiaotie/p/1723364.html
Copyright © 2011-2022 走看看