.NET平台下一个小Web开发项目总结

开发 后端
本文是关于.NET平台下,一个小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的文件:

  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. }  

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

二、分页

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

在这段代码中,我直接将分页和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.     #region 分页查找    
  15.     protected static DataTable GetPagerData(String where, String orderColumnName, Int32 pageSize, Int32 pageIndex, params SqlParameter[] whereParams)   
  16.     {   
  17.         return DbHelper.GetPagerData("News""*", where, orderColumnName, true"Id", pageSize, pageIndex, whereParams);   
  18.     }    
  19.     protected static Int32 GetDataCount(String where, params SqlParameter[] whereParams)   
  20.     {   
  21.         return DbHelper.GetDataCount("News", where, whereParams);   
  22.     }    
  23.     public static List<News> SelectData(String accountId, String mark, String mode, Int32 categoryId, Int32 inDays, String orderColumnName, Int32 pageSize, Int32 pageIndex)   
  24.     {   
  25.         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));   
  26.         return NewsService.Instance.DataTableToList(table);   
  27.     }   
  28.     public static Int32 SelectCount(String accountId, String mark, String mode, Int32 categoryId, Int32 inDays)   
  29.     {   
  30.         return GetDataCount(" 1=1 " + Util.BuildSqlByMark(mark) + Util.BuildSqlByMode(mode) + Util.BuildSqlByCategoryId(categoryId) + Util.BuildSqlByCreateInDays(inDays) + Util.BuildSqlByAccountId(accountId), Util.BuildSqlParameterByAccountId(accountId));   
  31.     }   
  32.     #endregion   

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

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

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

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

然后是具体的控件:

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

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

四、抱怨

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

原文标题:最近一个小Web开发项目的总结

链接:http://www.cnblogs.com/xiaotie/archive/2010/04/28/1723364.html

【编辑推荐】

  1. 添加设置ASP.NET Web时出现问题
  2. 实战ASP.NET大规模网站架构:Web加速器
  3. Visual Studio 2010升级Web开发功能
  4. 浅谈C#实现Web代理服务器的几大步骤
  5. 详解ASP.NET MVC 2自定义验证
     
责任编辑:彭凡 来源: 博客园
相关推荐

2014-09-05 09:45:46

2016-03-01 13:48:36

MVVMios快速开发

2011-10-31 10:17:47

开发平台

2012-03-29 09:22:56

云计算私有云开发

2015-10-29 09:35:12

BAT趋势数据

2020-11-15 23:23:21

JavaScriptAPI开发

2011-05-18 10:18:10

2013-09-22 11:19:06

云平台移动游戏

2014-06-17 10:57:09

2019-03-19 19:19:19

Facebook微信转型

2019-01-15 10:02:06

Kubernetes开源工具微服务

2013-01-17 10:09:50

JavaSpring

2011-03-17 15:01:11

Oracle

2021-08-11 18:23:08

数据平台IT

2023-12-07 09:44:29

2014-11-12 10:26:25

Bootstrap

2009-03-28 09:22:12

MID移动OS

2015-08-03 14:06:44

2022-02-13 15:49:15

WebAssemblKubernetes容器

2023-12-11 09:20:15

点赞
收藏

51CTO技术栈公众号