微型项目实践(9):页面的数据访问策略
2008-05-17 19:59
337 查看
上一篇中,我们完成了数据库的访问,今天我们来看看系统设计的最后一部分——UI层。加入了UI层之后,系统设计会变成这个样子:
这也就是系统最终的结构图。这个图上新添加的两个项目,一个是UI,一个是WebSite。其中前者依赖于业务逻辑和数据访问,提供统一的界面处理,而WebSite仅包含Aspx页面。需要注意的是,上图中箭头表示依赖或调用,而这个关系是具有传递性的,比如UI依赖于Business,而Business依赖于Common,则UI自然就依赖于Common。
将UI和WebSite分写在两个项目中,是我个人的一个习惯。其中UI主要包含页面基类和Helper类,还有数据库的获取方法,对于多种界面(比如既提供Web也提供WinForm)的系统,这一层次的抽象是必要的,而对于只提供Web访问的系统,把UI层放在WebSite中的App_Code文件夹下也无不可。
UI层中,目前就有两个核心类,一个是所有页面的基类,另一个是数据库生成类,其类结构图如下:
其中DatabaseGateWay的作用就是读取Web.Config中数据库链接节点的连接字符串,并调用DataAccess层的方法,构造新的数据库:
该类的GetNewDatabase()静态方法,是类型安全的,也是唯一使用到了DataAccess层具体实现的地方,系统的其它部分都只了解IDatabase而不了解其实现,更换不同的DataAccess只需要修改此方法。
PageBase类是所有Aspx页的基类,该类在该系统中只提供了数据库访问功能,即一个页面对应一个数据库访问,代码如下:
一个页面对应一个IDatabase,对于Linq来说,就是一个页面对应一个DataContext,这保证页面生存周期中的所有业务实体都来源或依附于同一个DataContext,避免了跨DataContext传递实体的问题。在Linq中,一个DataContext产生的Entity交由另一个DataContext中使用是一件非常麻烦的事情,必须保证实体必须使用Attach方法附加到新的DataContext上,如果不附加,则新的DataContext会认为该Entity是new出来的,这会导致再数据库中插入一条新的记录,而不是与现存记录建立关联,这个Bug很难调试,因为不会显示任何错误。我们举个例子说明这个问题。
我们经常会在用户登录的时候,在Session中储存登陆的用户:
这样以后我们就可以用Session["CurrentUser"]取得当前用户了。当用户新建一篇日志时,我们会使用类似下面的代码:
问题来了:上面两个代码通常来说不是发生在一个页面中,也就是说,从数据库中读取该user的DataContext早已不存在了,而新的、用于存储Blog的DataContext会认为这个blog相关的Creator是一个“新”的User,所以会在数据库中插入一条新的User记录,而不是和原来的User记录建立关联!为了避免这个情况,我们必须用Attach方法将Session["CurrentUser"]附加到新的DataContext中,上面的代码要这么改:
但是假设User还有关联的实体,比如Role、Power之类的东西,就需要全部附加上!否则就会插入新的关联记录!这几乎是不可能的,所以我们要保证每一个页面周期中所有的实体来源于同一个DataContext,对于上面这个例子,解决办法是不保存User,而保存UserID:
这样我们就可以在添加日志时这样写:
进一步,我们可以在PageBase中加载User,考虑在PageBase中存在以下代码:
这样我们就可以使用以下的代码建立日志了:
关于PageBase的用处还有很多,包括但不限于以下这些:
构造Database。
构造当前User。
判断当前用户是否具有该页面的访问权限。
根据当前用户定制页面:加入不同的Css等。
另外,基于PageBase的扩展方法,将对所有页面有效,这点常常用于执行JavaScript。
下一篇中,我们开始编写我们第一个页面。
代码下载
这也就是系统最终的结构图。这个图上新添加的两个项目,一个是UI,一个是WebSite。其中前者依赖于业务逻辑和数据访问,提供统一的界面处理,而WebSite仅包含Aspx页面。需要注意的是,上图中箭头表示依赖或调用,而这个关系是具有传递性的,比如UI依赖于Business,而Business依赖于Common,则UI自然就依赖于Common。
将UI和WebSite分写在两个项目中,是我个人的一个习惯。其中UI主要包含页面基类和Helper类,还有数据库的获取方法,对于多种界面(比如既提供Web也提供WinForm)的系统,这一层次的抽象是必要的,而对于只提供Web访问的系统,把UI层放在WebSite中的App_Code文件夹下也无不可。
UI层中,目前就有两个核心类,一个是所有页面的基类,另一个是数据库生成类,其类结构图如下:
其中DatabaseGateWay的作用就是读取Web.Config中数据库链接节点的连接字符串,并调用DataAccess层的方法,构造新的数据库:
namespaceDongBlog.UI
{
///<summary>
///数据库
///</summary>
publicclassDatabaseGateWay
{
privateconststringDatabaseConnectionConfigurationName= "DongBlogDatabaseConnectionString";
///<summary>
///取得新的数据库
///</summary>
///<returns>新数据库</returns>
publicstaticIDatabaseGetNewDatabase()
{
returnDatabase.New(ConfigurationManager
.ConnectionStrings[DatabaseGateWay.DatabaseConnectionConfigurationName]
.ConnectionString);
}
}
}
该类的GetNewDatabase()静态方法,是类型安全的,也是唯一使用到了DataAccess层具体实现的地方,系统的其它部分都只了解IDatabase而不了解其实现,更换不同的DataAccess只需要修改此方法。
PageBase类是所有Aspx页的基类,该类在该系统中只提供了数据库访问功能,即一个页面对应一个数据库访问,代码如下:
namespaceDongBlog.UI
{
///<summary>
///所有页面的基类
///</summary>
publicclassPageBase:Page
{
privateIDatabase_Database=DatabaseGateWay.GetNewDatabase();
///<summary>
///取得数据库访问
///</summary>
protectedIDatabaseDatabase
{
get{return_Database;}
}
}
}
一个页面对应一个IDatabase,对于Linq来说,就是一个页面对应一个DataContext,这保证页面生存周期中的所有业务实体都来源或依附于同一个DataContext,避免了跨DataContext传递实体的问题。在Linq中,一个DataContext产生的Entity交由另一个DataContext中使用是一件非常麻烦的事情,必须保证实体必须使用Attach方法附加到新的DataContext上,如果不附加,则新的DataContext会认为该Entity是new出来的,这会导致再数据库中插入一条新的记录,而不是与现存记录建立关联,这个Bug很难调试,因为不会显示任何错误。我们举个例子说明这个问题。
我们经常会在用户登录的时候,在Session中储存登陆的用户:
Useruser=GetLoginUser(username,password);
if(user!=null)
Session["CurrentUser"]=user;
else
Response.Redirect("Login.aspx");
这样以后我们就可以用Session["CurrentUser"]取得当前用户了。当用户新建一篇日志时,我们会使用类似下面的代码:
Blogblog=newBlog();
blog.Creator=Session["CurrentUser"]asUser;
blog.Save(Database);
问题来了:上面两个代码通常来说不是发生在一个页面中,也就是说,从数据库中读取该user的DataContext早已不存在了,而新的、用于存储Blog的DataContext会认为这个blog相关的Creator是一个“新”的User,所以会在数据库中插入一条新的User记录,而不是和原来的User记录建立关联!为了避免这个情况,我们必须用Attach方法将Session["CurrentUser"]附加到新的DataContext中,上面的代码要这么改:
Database.Users.Attach(Session["CurrentUser"]asUser);
Blogblog=newBlog();
blog.Creator=Session["CurrentUser"];
blog.Save(Database);
但是假设User还有关联的实体,比如Role、Power之类的东西,就需要全部附加上!否则就会插入新的关联记录!这几乎是不可能的,所以我们要保证每一个页面周期中所有的实体来源于同一个DataContext,对于上面这个例子,解决办法是不保存User,而保存UserID:
Useruser=GetLoginUser(username,password);
if(user!=null)
Session["CurrentUserID"]=user.ID;
else
Response.Redirect("Login.aspx");
这样我们就可以在添加日志时这样写:
Blogblog=newBlog();
blog.Creator=Database.Users.GetByID(Convert.ToInt32(Session["CurrentUserID"]));
blog.Save(Database);
进一步,我们可以在PageBase中加载User,考虑在PageBase中存在以下代码:
privateUser_CurrentUser;
publicUserCurrentUser
{
get
{
if(Session["CurrentUserID"]==null)
returnnull;
if(_CurrentUser==null)
_CurrentUser=Database.Users.GetByID(Convert.ToInt32(Session["CurrentUserID"]));
return_CurrentUser;
}
}
这样我们就可以使用以下的代码建立日志了:
Blogblog=newBlog();
blog.Creator=CurrentUser;
blog.Save(Database);
关于PageBase的用处还有很多,包括但不限于以下这些:
构造Database。
构造当前User。
判断当前用户是否具有该页面的访问权限。
根据当前用户定制页面:加入不同的Css等。
另外,基于PageBase的扩展方法,将对所有页面有效,这点常常用于执行JavaScript。
下一篇中,我们开始编写我们第一个页面。
相关文章推荐
- 微型项目实践(7):数据访问的定义
- 微型项目实践(7):数据访问的定义
- 微型项目实践(8):数据访问的实现
- 微型项目实践(6):Business层代码分析——实体类的生成策略
- 实践项目十:爬取百度百科Python词条相关1000个页面数据(慕课简单爬虫实战)
- 关于jsp web项目,jsp页面与servlet数据不同步的解决办法(报错404、405等)即访问.jsp和访问web.xml中注册的/servlet/的区别
- 微型项目实践(6):Business层代码分析——实体类的生成策略
- Python 项目实践二(生成数据)第一篇
- 关于JAVA EE项目在WEB-INF目录下的jsp页面如何访问WebRoot中的CSS和JS文件
- IIS配置与错误提示 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效 解决方法
- Android项目实践之菜鸟养成 (三)Android简单页面跳转
- 封装项目整体架构,访问网络数据,展示列表数据,根据图片数量的不同来多条目加载,使用Universal-Image-Loader进行图片加载
- android下PDF格式的地图数据的显示------超大PDF页面显示策略(一)
- 移动项目开发笔记(asp.net防止页面刷新引起重复提交数据)
- Vue项目数据动态过滤实践及实现思路
- Python 项目实践二(生成数据)第一篇
- javaWeb项目用过滤器filter实现登陆成功后才能访问主页面,否则直接输入主页面的地址自动跳转到登陆界面
- 项目中使用的自定义数据访问类(复杂查询,复杂操作)
- 第五周上机实践项目2——对象作为数据成员
- HTTP 错误 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效。