【Yom框架】漫谈个人框架的设计之三:业务接口+UI层的设计(基于Castle实现的Repository)
Repository层设计的文章见:【http://www.cnblogs.com/yomho/p/3297042.html】
一、概要设计

上面Reposity 应该为 Repository
特此更正,也不打算作图更正了。
二、业务Server层
业务层Server是承Repository层,启UI层的重要层,
UI层的数据和Repository层的数据传递必须经过它
业务层的扩展非常必要
所以采用IServer<TEntity>的设计方式
接口设计如下:
namespace Yom.NFramework2_0
{
public interface IServer<TEntity, TPrimaryKey>
where TEntity : IEntity
where TPrimaryKey : IComparable
{
TEntity FindBy(TPrimaryKey primaryKey);
IEnumerable<TEntity> FindAll();
IEnumerable<TEntity> FindAll<TWhere>(TWhere[] where) where TWhere : IWhere;
IEnumerable<TEntity> FindAll<TWhere, TOrder>(TWhere[] where, TOrder[] order)
where TWhere : IWhere
where TOrder : IOrder;
IEnumerable<TEntity> FindAll<TWhere, TOrder>(int pageIndex, int pageSize, TWhere[] where, TOrder[] order, out int count)
where TWhere : IWhere
where TOrder : IOrder;
void Add(TEntity entity);
void Delete(TEntity entity);
void DeleteAll();
void DeleteAll<TWhere>(TWhere[] where) where TWhere : IWhere;
void DeleteAll(string where);
void DeleteAll(IEnumerable<TPrimaryKey> pkValues);
void Update(TEntity entity);
bool Exists(TPrimaryKey primaryKey); TPrimaryKey PrimaryKeyPropertyName
{
get;
}
}
}
Server层和Repository的接口设计大同小异
大同:方法大致相同
小异:实现方式不同,Server是IServer<TEntity>,Repository是IRepository,Server的如此设计是为了业务扩展。
其中Server层要多了一个获取主键属性名称的方法。
Server层依附Repository的实现如下:
namespace Yom.NFramework2_0
{
public abstract class ISinglePrimaryKeyServer<TEntity> : IServer<TEntity, String>
where TEntity : IEntity
{
#region IServer<TEntity,string> 成员 public TEntity FindBy(string primaryKey)
{
return RepositoryFactory.Instance.FindBy<TEntity>(primaryKey);
} public IEnumerable<TEntity> FindAll()
{
return RepositoryFactory.Instance.FindAll<TEntity>();
}
public IEnumerable<TEntity> FindAll<TWhere>(TWhere[] where) where TWhere : IWhere
{
return RepositoryFactory.Instance.FindAll<TEntity>(where as IWhere[]);
}
public IEnumerable<TEntity> FindAll<TWhere, TOrder>(TWhere[] where, TOrder[] order)
where TWhere : IWhere
where TOrder : IOrder
{
return RepositoryFactory.Instance.FindAll<TEntity>(where as IWhere[], order as IOrder[]);
}
public IEnumerable<TEntity> FindAll<TWhere, TOrder>(int pageIndex, int pageSize, TWhere[] where, TOrder[] order, out int count)
where TWhere : IWhere
where TOrder : IOrder
{
return RepositoryFactory.Instance.FindAll<TEntity>(pageIndex, pageSize, where as IWhere[], order as IOrder[], out count);
} public void Add(TEntity entity)
{
RepositoryFactory.Instance.Add<TEntity>(entity);
} public void Delete(TEntity entity)
{
RepositoryFactory.Instance.Delete<TEntity>(entity);
} public void DeleteAll()
{
RepositoryFactory.Instance.DeleteAll<TEntity>();
} public void DeleteAll<TWhere>(TWhere[] where)
where TWhere : IWhere
{
RepositoryFactory.Instance.DeleteAll<TEntity>(where as IWhere[]); }
public void DeleteAll(string where)
{
RepositoryFactory.Instance.DeleteAll<TEntity>(where);
} public void DeleteAll(IEnumerable<string> pkValues)
{
RepositoryFactory.Instance.DeleteAll<TEntity>(pkValues);
} public void Update(TEntity entity)
{
RepositoryFactory.Instance.Update<TEntity>(entity);
} public bool Exists(string primaryKey)
{
return RepositoryFactory.Instance.Exists<TEntity>(primaryKey);
} public abstract string PrimaryKeyPropertyName
{
get;
}
#endregion
}
}
当到用特定的ORM扩展的时候
只需要实现
public abstract string PrimaryKeyPropertyName
{
get;
}
就可以了
用Castle实现如下
namespace Yom.NFramework2_0.CastleExtend
{
public class SinglePrimaryKeyServerBase<TEntity> : Yom.NFramework2_0.ISinglePrimaryKeyServer<TEntity>
where TEntity : EntityBase
{
string GetSinglePrimaryKeyName() {
System.Reflection.PropertyInfo[] pis = typeof(TEntity).GetProperties();
if (pis != null && pis.Length > ) {
object[] customAttributes;
foreach (System.Reflection.PropertyInfo pi in pis) {
customAttributes = pi.GetCustomAttributes(typeof(Castle.ActiveRecord.PrimaryKeyAttribute), true);
if (customAttributes != null && customAttributes.Length > ) {
string result = (customAttributes[] as Castle.ActiveRecord.PrimaryKeyAttribute).Column;
if (!string.IsNullOrEmpty(result)) {
return result;
}
return pi.Name;
}
}
}
return null;
} public override string PrimaryKeyPropertyName
{
get { return GetSinglePrimaryKeyName(); }
}
}
}
这样一个完整的基于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
namespace Yom.WbTest.UI.YOM_DEPARTMENT
{
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>
{
protected override Yom.NFramework2_0.IWhere[] Where
{
get {
return new NFramework2_0.IWhere[] {
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)}
};
}
}
}
}
Edit
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>
{
protected override bool IsValid(out string msg)
{
return base.IsValid(out msg);
}
}
其中IsValid负责服务器端的数据验证,如果返回False则不会执行保存操作。
在真正做项目的时候,按照约定排版如下:
Edit.ascx
<%@ Control Language="C#" AutoEventWireup="true" %>
<table>
<tr>
<td style=" width:10%; white-space:nowrap">部门名称:</td>
<td>
<asp:TextBox ID="DEPARTMENT_NAME" runat="server" CssClass="input"></asp:TextBox></td>
</tr>
<tr>
<td style=" width:10%; white-space:nowrap">部门编号:</td>
<td>
<asp:TextBox ID="DEPARTMENT_CODE" runat="server" CssClass="input"></asp:TextBox></td>
</tr>
<tr>
<td style=" width:10%; white-space:nowrap">排序:</td>
<td>
<asp:TextBox ID="ORDER_ID" runat="server" Text="" CssClass="input"></asp:TextBox></td>
</tr>
<tr>
<td style=" width:10%; white-space:nowrap">部门描述:</td>
<td>
<asp:TextBox ID="DEPARTMENT_DESC" runat="server" TextMode="MultiLine" CssClass="textarea"></asp:TextBox></td>
</tr>
<tr>
<td style=" width:10%; white-space:nowrap">是否禁用:</td>
<td>
<asp:RadioButtonList ID="IS_DISABLED" runat="server"
RepeatDirection="Horizontal" RepeatLayout="Flow">
<asp:ListItem Value="">是</asp:ListItem>
<asp:ListItem Selected="True" Value="">否</asp:ListItem>
</asp:RadioButtonList>
</td>
</tr>
</table>
<asp:Button ID="bSubmit" runat="server" Text="保存" /><input type="button" value="返回" onclick="window.location='List.aspx'" />
List.ascx(实现了点击创建时间,升降开关式排序功能)
<%@ Control Language="C#" AutoEventWireup="true" %>
<script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>
<asp:Panel ID="pSearcher" runat="server">
部门名称:
<asp:TextBox ID="DEPARTMENT_NAME" runat="server" PropertyName="DEPARTMENT_NAME"></asp:TextBox><asp:Button
ID="btnSearch" runat="server" Text="搜索" />
</asp:Panel>
<div style=" display:block;"><a href="Edit.aspx">新增</a></div>
<asp:Repeater ID="lvList" runat="server"> <HeaderTemplate>
<table class="gridview" border="" cellpadding="" cellspacing="" width="100%">
<tr>
<th><input type="checkbox" title="全选/全消" onclick="$(':checkbox.cbSelectItem').attr('checked',$(this).attr('checked'))" /></th>
<th>
部门名称</th><th>部门编号</th><th>部门描述</th><th><asp:LinkButton ID="LinkButton4" runat="server" CommandName="Order" CommandArgument="CREATE_DATE">创建时间</asp:LinkButton></th><th>操作</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<th><%# string.Format("<input type=\"checkbox\" name=\"cbSelectItem\" class=\"cbSelectItem\" value=\"{0}\" />", Eval("DEPARTMENT_ID"))%></th>
<td style=" text-align:center"><%# Eval("DEPARTMENT_NAME")%></td>
<td style=" text-align:center"><%# Eval("DEPARTMENT_CODE")%></td>
<td style=" text-align:center"><%# Eval("DEPARTMENT_DESC")%></td> <td style=" text-align:center"><%# string.Format("{0:yyyy-MM-dd}", Convert.ToDateTime(Convert.ToString(Eval("CREATE_DATE"))))%></td>
<td style=" text-align:center">
<a href="Edit.aspx?id=<%# Eval("DEPARTMENT_ID")%>">编辑</a>|<a href="Edit.aspx?action=delete&id=<%# Eval("DEPARTMENT_ID")%>" onclick="return confirm('确定要删除吗?')">删除</a></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</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
<%@ 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.List ID="list" runat="server">
</yom:YOM_DEPARTMENT.List> </div>
</form>
</body>
</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、易用性方面解决了旧框架的弊端,有没有新的弊端还需要后续的使用测试。
【Yom框架】漫谈个人框架的设计之三:业务接口+UI层的设计(基于Castle实现的Repository)的更多相关文章
- 业务接口+UI层的设计(基于Castle实现的Repository)
业务接口+UI层的设计(基于Castle实现的Repository) Repository层设计的文章见:[http://www.cnblogs.com/yomho/p/3297042.html] ...
- 【Yom框架】漫谈个人框架的设计之二:新的IRepository接口+搜索和排序解耦(+基于Castle实现)
经过了上篇IRepository和IRepository<T>的讨论[文章地址为:http://www.cnblogs.com/yomho/p/3296759.html] 我选择了IRep ...
- 设计原则:接口隔离原则(ISP)
接口隔离原则的英文是Interface Segregation Principle,缩写就是ISP.与里氏替换原则一样其定义同样有两种 定义1: Clients should not be force ...
- 【Yom框架】漫谈个人框架的设计之一:是IRepository还是IRepository<T>?
前言 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 9.插件引擎设计
目 录 第九章 插件引擎设计... 2 9.1 框架的契约-接口... 2 9.2 插件的雏形-抽象类... 3 9.3 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 7.外部接口的设计
目 录 第七章 外部接口的设计... 2 7.1 插件接口... 2 7.2 图形显示接口... 3 7.3 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计
目 录 第六章 通讯控制器的设计... 2 6.1 控制器接口... 2 6.2 串口控制器... 3 6.3 ...
- 【开源】OSharp框架解说系列(5.1):EntityFramework数据层设计
OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...
- .NET框架设计(常被忽视的C#设计技巧)
阅读目录: 1.开篇介绍 2.尽量使用Lambda匿名函数调用代替反射调用(走进声明式设计) 3.被忽视的特性(Attribute)设计方式 4.扩展方法让你的对象如虎添翼(要学会使用扩展方法的设计思 ...
随机推荐
- Android环境结构--安装Eclipse错
在学习安卓第一步.成立了一个开发环境. 经验,知道,所以这一步是不容易,因为你觉得,我可能是太幸运了. 我见到 题. 首先,安装Eclipse的时候. [Problem 1] [问题原因]: (1) ...
- DP 水的问题
假设的自然数N的K随机二进制表示是不相邻的两个相邻的数字.那么我们说这个数字是K好一些. 乞讨L地点K十六进制数K的相当数量的数. 例如K = 4.L = 2什么时候.整个K好一些11.13.20.2 ...
- android 数据共享
android数据共享的各种部件中的应用是最重要的3途径: 第一.使用Application子类来实现数据共享. 例如,请看下面的例子: /** * @author YangQuanqing 特征: ...
- Android 平台 HTTP网速測试 案例 API 分析
作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/25996817 工信部规定的网速測试标准 : 除普通网页測速 ...
- Windows系统下Redis的安装
Redis是一个用的比较广泛的Key/Value的内存数据库,新浪微博.Github.StackOverflow 等大型应用中都用其作为缓存,Redis的官网为http://redis.io/. 最近 ...
- 表现层及ASP.NET MVC介绍(二)
表现层及ASP.NET MVC介绍(二) 最近的更新速度越来越慢,主要是项目上比较忙,封装EasyUi也要花很多时间.不过大家请放心,本系列不会半途夭折,并且代码干货也会持续更新.本文继续介绍表现层和 ...
- 随记一个C的毫秒级群PING
正好公司为了检测前台网络,力图收集有力证据与某CDN PK,所以随手写了一个群PING的程序. 写的内容比较简单,没有去特别追求线程效率,也没有去用LINUX 2.6+的殿堂级神器,以追求实现效率为主 ...
- MVC验证10-到底用哪种方式实现客户端服务端双重异步验证
原文:MVC验证10-到底用哪种方式实现客户端服务端双重异步验证 本篇将通过一个案例来体验使用MVC的Ajax.BeginForm或jQuery来实现异步提交,并在客户端和服务端双双获得验证.希望能梳 ...
- <<Python基础课程>>学习笔记 | 文章13章 | 数据库支持
备注:本章介绍了比较简单,只是比较使用样品,主要假设是把握连接,利用数据库.和SQLite做演示样本 ------ Python数据库API 为了解决Python中各种数据库模块间的兼容问题,如今已经 ...
- 发现新大陆:一个最简单的破解SSL加密网络数据包的方法
1. 简介 相信能访问到这篇文章的同行基本上都会用过流行的网络抓包工具WireShark,用它来抓取相应的网络数据包来进行问题分析或者其他你懂的之类的事情. 一般来说,我们用WireShark来抓取包 ...