• 【Yom框架】漫谈个人框架的设计之三:业务接口+UI层的设计(基于Castle实现的Repository)


    Repository层设计的文章见:【http://www.cnblogs.com/yomho/p/3297042.html

      一、概要设计

    上面Reposity 应该为 Repository

    特此更正,也不打算作图更正了。

      二、业务Server层

    业务层Server是承Repository层,启UI层的重要层,

    UI层的数据和Repository层的数据传递必须经过它

    业务层的扩展非常必要

    所以采用IServer<TEntity>的设计方式

    接口设计如下:

     1 namespace Yom.NFramework2_0
     2 {
     3     public interface IServer<TEntity, TPrimaryKey>
     4         where TEntity : IEntity
     5         where TPrimaryKey : IComparable
     6     {
     7         TEntity FindBy(TPrimaryKey primaryKey);
     8         IEnumerable<TEntity> FindAll();
     9         IEnumerable<TEntity> FindAll<TWhere>(TWhere[] where) where TWhere : IWhere;
    10         IEnumerable<TEntity> FindAll<TWhere, TOrder>(TWhere[] where, TOrder[] order)
    11             where TWhere : IWhere
    12             where TOrder : IOrder;
    13         IEnumerable<TEntity> FindAll<TWhere, TOrder>(int pageIndex, int pageSize, TWhere[] where, TOrder[] order, out int count)
    14             where TWhere : IWhere
    15             where TOrder : IOrder;
    16         void Add(TEntity entity);
    17         void Delete(TEntity entity);
    18         void DeleteAll();
    19         void DeleteAll<TWhere>(TWhere[] where) where TWhere : IWhere;
    20         void DeleteAll(string where);
    21         void DeleteAll(IEnumerable<TPrimaryKey> pkValues);
    22         void Update(TEntity entity);
    23         bool Exists(TPrimaryKey primaryKey);
    24 
    25         TPrimaryKey PrimaryKeyPropertyName
    26         {
    27             get;
    28         }
    29     }
    30 }

    Server层和Repository的接口设计大同小异

    大同:方法大致相同

    小异:实现方式不同,Server是IServer<TEntity>,Repository是IRepository,Server的如此设计是为了业务扩展。

    其中Server层要多了一个获取主键属性名称的方法。

    Server层依附Repository的实现如下:

     1 namespace Yom.NFramework2_0
     2 {
     3     public abstract class ISinglePrimaryKeyServer<TEntity> : IServer<TEntity, String>
     4         where TEntity : IEntity
     5     {
     6         #region IServer<TEntity,string> 成员
     7 
     8         public TEntity FindBy(string primaryKey)
     9         {
    10             return RepositoryFactory.Instance.FindBy<TEntity>(primaryKey);
    11         }
    12 
    13         public IEnumerable<TEntity> FindAll()
    14         {
    15             return RepositoryFactory.Instance.FindAll<TEntity>();
    16         }
    17         public IEnumerable<TEntity> FindAll<TWhere>(TWhere[] where) where TWhere : IWhere
    18         {
    19             return RepositoryFactory.Instance.FindAll<TEntity>(where as IWhere[]);
    20         }
    21         public IEnumerable<TEntity> FindAll<TWhere, TOrder>(TWhere[] where, TOrder[] order)
    22             where TWhere : IWhere
    23             where TOrder : IOrder
    24         {
    25             return RepositoryFactory.Instance.FindAll<TEntity>(where as IWhere[], order as IOrder[]);
    26         }
    27         public IEnumerable<TEntity> FindAll<TWhere, TOrder>(int pageIndex, int pageSize, TWhere[] where, TOrder[] order, out int count)
    28             where TWhere : IWhere
    29             where TOrder : IOrder
    30         {
    31             return RepositoryFactory.Instance.FindAll<TEntity>(pageIndex, pageSize, where as IWhere[], order as IOrder[], out count);
    32         }
    33 
    34         public void Add(TEntity entity)
    35         {
    36             RepositoryFactory.Instance.Add<TEntity>(entity);
    37         }
    38 
    39         public void Delete(TEntity entity)
    40         {
    41             RepositoryFactory.Instance.Delete<TEntity>(entity);
    42         }
    43 
    44         public void DeleteAll()
    45         {
    46             RepositoryFactory.Instance.DeleteAll<TEntity>();
    47         }
    48 
    49         public void DeleteAll<TWhere>(TWhere[] where)
    50             where TWhere : IWhere
    51         {
    52             RepositoryFactory.Instance.DeleteAll<TEntity>(where as IWhere[]);
    53             
    54         }
    55         public void DeleteAll(string where)
    56         {
    57             RepositoryFactory.Instance.DeleteAll<TEntity>(where);
    58         }
    59 
    60         public void DeleteAll(IEnumerable<string> pkValues)
    61         {
    62             RepositoryFactory.Instance.DeleteAll<TEntity>(pkValues);
    63         }
    64 
    65         public void Update(TEntity entity)
    66         {
    67             RepositoryFactory.Instance.Update<TEntity>(entity);
    68         }
    69 
    70         public bool Exists(string primaryKey)
    71         {
    72             return RepositoryFactory.Instance.Exists<TEntity>(primaryKey);
    73         }
    74 
    75         public abstract string PrimaryKeyPropertyName
    76         {
    77             get;
    78         }
    79         #endregion
    80     }
    81 }

    当到用特定的ORM扩展的时候

    只需要实现

    1 public abstract string PrimaryKeyPropertyName
    2         {
    3             get;
    4         }


    就可以了

    用Castle实现如下

     1 namespace Yom.NFramework2_0.CastleExtend
     2 {
     3     public class SinglePrimaryKeyServerBase<TEntity> : Yom.NFramework2_0.ISinglePrimaryKeyServer<TEntity>
     4         where TEntity : EntityBase
     5     {
     6          string GetSinglePrimaryKeyName() {
     7             System.Reflection.PropertyInfo[] pis = typeof(TEntity).GetProperties();
     8             if (pis != null && pis.Length > 0) {
     9                 object[] customAttributes;
    10                 foreach (System.Reflection.PropertyInfo pi in pis) {
    11                     customAttributes = pi.GetCustomAttributes(typeof(Castle.ActiveRecord.PrimaryKeyAttribute), true);
    12                     if (customAttributes != null && customAttributes.Length > 0) {
    13                         string result = (customAttributes[0] as Castle.ActiveRecord.PrimaryKeyAttribute).Column;
    14                         if (!string.IsNullOrEmpty(result)) {
    15                             return result;
    16                         }
    17                         return pi.Name;
    18                     }
    19                 }
    20             }
    21             return null;
    22         }
    23 
    24          public override string PrimaryKeyPropertyName
    25         {
    26             get { return GetSinglePrimaryKeyName(); }
    27         }
    28     }
    29 }

    这样一个完整的基于Castle的ServerBase就准备好了

      三、组合控件UI层

    UI层一般有List和Edit这2个常用的控件

    这2个控件是SkinnedControlBase的子类

    在UI的设计的时候,实现了SkinnedControlBase控件

    大家也许想特想知道这控件有什么功能

    其实这个控件是实现框架里UI和组装层(实际就是项目)分离的重要控件

    这个控件的设计思想请见【http://www.cnblogs.com/yomho/archive/2013/03/10/2953132.html

    简单说就是:

    UI的业务逻辑和表现数据的HTML代码(html代码写在项目里)完全分离

    组合控件分为自定义控件和用户控件两个部分:

    1、自定义控件封装后台代码(负责后台操作);

    2、用户控件负责前台HTML代码(实现布局和样式以及客户端验证)。

    在用aspx页面承载组合控件时候,会结合组合控件中自定义控件的后台逻辑代码用户控件的Html代码展示数据给客户操作。

    这种分离是约定下分离的,就像MVC里面视图和控制器名称的约定一样,

    当然也可以个性化配置,实现特定的开发的目录结构,

    就像MVC里的Area的设计概念,可以根据项目,对控制器和视图的映射路径进行个性化配置

    虽然SkinnedControlBase有点像MVC的思想,但是却完全不是这种MVC概念。

      四、Project项目的功能模块的组装

    实现项目的时候,需要费力开发的层次有:Entity层和Server层以及UI层.

    这三个层次开发的时候是有顺序的:按Entity - > Server - > UI 层的顺序逐步细化

    当有了UI层,比如有XXX/List.cs这个控件后,就可以开始实施Views/XXX/Lit.ascx这个控件了

    XXX/List.cs和Views/XXX/Lit.ascx是相互绑定的:业务逻辑在UI层实现,对应表现数据的Html代码就在ascx里表现

    真正加载数据的时候是在aspx里放组合控件

    说了这么多,大家一定和头疼

    下面给出一个简单的实现样例,比如我要实现部门的增删查改:

    先Entity: YOM_DEPARTMENT : Yom.NFramework2_0.CastleExtend.EntityBase

    然后Server:YOM_DEPARTMENTServer : Yom.NFramework2_0.CastleExtend.ServerBase<Yom.WbTest.Entity.YOM_DEPARTMENT.YOM_DEPARTMENT>

    最后2个UI:

    List

     1 namespace Yom.WbTest.UI.YOM_DEPARTMENT
     2 {
     3     public class List : Yom.NFramework2_0.CastleExtend.Web.WebForm.UI.List.SinglePrimaryKey.CustomSkinnedListBase<Yom.WbTest.Entity.YOM_DEPARTMENT.YOM_DEPARTMENT , Server.YOM_DEPARTMENT.YOM_DEPARTMENTServer>
     4     {
     5         protected override Yom.NFramework2_0.IWhere[] Where
     6         {
     7             get {
     8                 return new NFramework2_0.IWhere[] { 
     9                     new Yom.NFramework2_0.CastleExtend.WhereBase(){ Instance=Yom.NFramework2_0.CastleExtend.WhereBase.Like(this.searchObject["DEPARTMENT_NAME"].PropertyName,this.searchObject["DEPARTMENT_NAME"].Value.ToString(),NHibernate.Expression.MatchMode.Anywhere)}
    10                 };
    11             }
    12         }
    13     }
    14 }

    Edit

    1 public class Edit : Yom.NFramework2_0.CastleExtend.Web.WebForm.UI.Edit.CustomSkinnedEditBase<Yom.WbTest.Entity.YOM_DEPARTMENT.YOM_DEPARTMENT,Yom.WbTest.Server.YOM_DEPARTMENT.YOM_DEPARTMENTServer>
    2     {
    3         protected override bool IsValid(out string msg)
    4         {
    5             return base.IsValid(out msg);
    6         }
    7     }

    其中IsValid负责服务器端的数据验证,如果返回False则不会执行保存操作。

    在真正做项目的时候,按照约定排版如下:

    Edit.ascx

     1 <%@ Control Language="C#" AutoEventWireup="true" %>
     2 
     3 <table>
     4 
     5     <tr>
     6         <td style=" 10%; white-space:nowrap">部门名称:</td>
     7         <td>
     8             <asp:TextBox ID="DEPARTMENT_NAME" runat="server" CssClass="input"></asp:TextBox></td>
     9     </tr>
    10     <tr>
    11         <td style=" 10%; white-space:nowrap">部门编号:</td>
    12         <td>
    13             <asp:TextBox ID="DEPARTMENT_CODE" runat="server" CssClass="input"></asp:TextBox></td>
    14     </tr>
    15     <tr>
    16         <td style=" 10%; white-space:nowrap">排序:</td>
    17         <td>
    18             <asp:TextBox ID="ORDER_ID" runat="server" Text="0" CssClass="input"></asp:TextBox></td>
    19     </tr>
    20     <tr>
    21         <td style=" 10%; white-space:nowrap">部门描述:</td>
    22         <td>
    23             <asp:TextBox ID="DEPARTMENT_DESC" runat="server" TextMode="MultiLine" CssClass="textarea"></asp:TextBox></td>
    24     </tr>
    25     <tr>
    26         <td style=" 10%; white-space:nowrap">是否禁用:</td>
    27         <td>
    28             <asp:RadioButtonList ID="IS_DISABLED" runat="server" 
    29                 RepeatDirection="Horizontal" RepeatLayout="Flow">
    30                 <asp:ListItem Value="1">是</asp:ListItem>
    31                 <asp:ListItem Selected="True" Value="0">否</asp:ListItem>
    32             </asp:RadioButtonList>
    33         </td>
    34     </tr>
    35 </table>
    36 <asp:Button ID="bSubmit" runat="server" Text="保存" /><input type="button" value="返回" onclick="window.location='List.aspx'" />

    List.ascx(实现了点击创建时间,升降开关式排序功能)

     1 <%@ Control Language="C#" AutoEventWireup="true" %>
     2 <script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>
     3 <asp:Panel ID="pSearcher" runat="server">
     4 部门名称: 
     5     <asp:TextBox ID="DEPARTMENT_NAME" runat="server" PropertyName="DEPARTMENT_NAME"></asp:TextBox><asp:Button
     6         ID="btnSearch" runat="server" Text="搜索" />   
     7 </asp:Panel>
     8 <div style=" display:block;"><a href="Edit.aspx">新增</a></div>
     9 <asp:Repeater ID="lvList" runat="server">
    10     
    11     <HeaderTemplate>
    12         <table class="gridview" border="0" cellpadding="0" cellspacing="1" width="100%">
    13         <tr>
    14         <th><input type="checkbox" title="全选/全消" onclick="$(':checkbox.cbSelectItem').attr('checked',$(this).attr('checked'))" /></th>
    15             <th>
    16                 部门名称</th><th>部门编号</th><th>部门描述</th><th><asp:LinkButton ID="LinkButton4" runat="server" CommandName="Order" CommandArgument="CREATE_DATE">创建时间</asp:LinkButton></th><th>操作</th>
    17         </tr>
    18     </HeaderTemplate>
    19     <ItemTemplate>
    20         <tr>
    21         <th><%# string.Format("<input type="checkbox" name="cbSelectItem" class="cbSelectItem" value="{0}" />", Eval("DEPARTMENT_ID"))%></th>
    22             <td style=" text-align:center"><%# Eval("DEPARTMENT_NAME")%></td>
    23             <td style=" text-align:center"><%# Eval("DEPARTMENT_CODE")%></td>
    24             <td style=" text-align:center"><%# Eval("DEPARTMENT_DESC")%></td>
    25            
    26             <td style=" text-align:center"><%# string.Format("{0:yyyy-MM-dd}", Convert.ToDateTime(Convert.ToString(Eval("CREATE_DATE"))))%></td>
    27             <td style=" text-align:center">
    28                 <a href="Edit.aspx?id=<%# Eval("DEPARTMENT_ID")%>">编辑</a>|<a href="Edit.aspx?action=delete&id=<%# Eval("DEPARTMENT_ID")%>" onclick="return confirm('确定要删除吗?')">删除</a></td>
    29         </tr>
    30     </ItemTemplate>
    31     <FooterTemplate>
    32         </table>
    33     </FooterTemplate>
    34 </asp:Repeater>

    其中Edit.ascx 和List.ascx 不涉及后台代码和任何业务逻辑,可以完全交给美工

    如果设计和开发的时候约定好,美工可先行开发Edit.ascx 和List.ascx

    下面是aspx页面承载组装控件:

    Edit.aspx

    <%@ Page Language="C#" AutoEventWireup="true" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
        <yom:YOM_DEPARTMENT.Edit ID="edit" runat="server">
            </yom:YOM_DEPARTMENT.Edit>
        </div>
        </form>
    </body>
    </html>

    List.aspx

     1 <%@ Page Language="C#" AutoEventWireup="true" %>
     2 
     3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     4 
     5 <html xmlns="http://www.w3.org/1999/xhtml">
     6 <head runat="server">
     7     <title></title>
     8 </head>
     9 <body>
    10     <form id="form1" runat="server">
    11     <div>
    12     <yom:YOM_DEPARTMENT.List ID="list" runat="server">
    13         </yom:YOM_DEPARTMENT.List>
    14     
    15     </div>
    16     </form>
    17 </body>
    18 </html>

     有些细心的朋友可能已经发现:

    无论是ascx还是aspx页面都没有code behind的cs文件

    这个就和MVC2里aspx和ascx页面的作用一样了

    只作为Html的承载文件,并没有什么cs文件去写这些文件的后台代码

    否则无法实现后台代码和前台代码分离

      五、总结

    1、框架的设计可以很好的支持技术开发人员和美工的分工,且美工知道表结构的情况下可以先行开发;

    2、模块化组装的开发模式,在框架成熟的情况下,可以避免页面业务逻辑复杂化,减少bug的出现;

    3、开发流程化,减少组织代码的复杂性

    4、规范了编程的代码,新人在熟悉后,可以快速进入开发和维护旧项目的状态;

    5、换一个UI层和一种Project,可以快速进行项目类型的转换。

      六、关于框架

    1、框架的重构,难免有bug未发现,所以还得经过一些项目的开发测试;

    2、框架现在只封装了asp.net Webform的开发,后续将接入Winform和MVC等的开发模块;

    3、框架没有接入缓存层,所以并不适合百万大数据的项目开发,目前只能应付一些数据量不大的项目;

    4、易用性方面解决了旧框架的弊端,有没有新的弊端还需要后续的使用测试。

  • 相关阅读:
    [原]如何在Android用FFmpeg+SDL2.0之同步视频
    鹤山市五泉酒厂 (“侨乡情”酒 )
    买酒的网站(转)
    泥鳅、黄鳝有关技术。
    国产 冰葡萄酒 主要生产地、品牌
    对症治疗过敏性鼻炎,依巴斯汀比氯雷他定更有效
    魔筎精粉制作 ----新化联系人
    拉肚子使用的常见药物
    一些WCF DS 的资料(参考)
    SHAREPOINT 2013 + PROJECT 2013 资料网站
  • 原文地址:https://www.cnblogs.com/yomho/p/3308307.html
Copyright © 2020-2023  润新知