怎样在ASP.NET项目里面正确使用Linq to Sql_.Net教程
推荐:解读链表的顺序表示和实现/*List.h*/ #ifndef _LIST_H #define _LIST_H #define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 template
Linq to Sql 用的人也应该有些吧,我在cnblogs上面看老赵写的那几篇文章(请看08年9月左右的文章),感觉也很有深度,有不少启发。因此我也打算写一点我自己的实践经验,希望也能同样给大家一些有用的启发吧。
我首先想要问一下大家,Linq to Sql有哪些很特别的地方?这个问题的答案肯定五花八门,我说一下我看到的一些问题吧。
首先,Linq to Sql的基础之一是DataContext,而另外一个基础,则是通过映射产生的实体类,以及这些实体类的Table<>对象。这个不是废话嘛!我想很多人都应该知道这个最基本的知识,不过却不见得有多少人真正注意到,或者认真思考一下这里面的“机关”。不知道“机关”在哪里,那么就不可能写出合适的代码。比如说,在某个页面里面(N层结构没有给弄好的情况下),或者在某个业务逻辑里面(有N层结构),你的Linq to Sql的代码是否是长这样的?
以下为引用的内容: using (MyDataContext db = new MyDataContext) |
“对啊,就是长这样的,有什么问题吗?”当然有问题啦,否则我也不写这个随笔了。不知道大家有没有想过这么一个问题,什么叫做Context?Context就是上下文,上下文的意思就是,依赖于这个上下文的对象,必须存活在这个上下文里面。脱离了这个上下文,那些对象就会出现错误。事实上也确实如此:在上面的例子里面,从ProductInfos中得到的q.ToList(),里面的每一个元素都依赖于MyDataContext。换句话说MyDataContext如果被注销了,q.ToList()生成的对象也就会“部分功能失效”。
“失效就失效好了,反正该做的工作已经做完了,q.ToList()也已经利用完了。”不错,在上面的例子里面,不会发生什么错误。不过这么写的话,会比较难使用的。为什么这么说?我举一个具体的例子:这个网站需要用户登录,而所有的业务逻辑几乎都依赖于当前用户。如果说,我们使用上面的using模式,那么我估计你的代码不外乎是如下两种情况:
1、每一次需要当前用户的地方,你都需要从数据库读取;或者
2、你把当前用户保存为全局变量了,但是你发现currentUser.CompanyInfo因为上下文已经抛弃了,因此是无法使用的,业务层不得不每一次都重新从数据库读取该用户所属公司的数据。
这两种形式如下所示:
以下为引用的内容: // 通过实体对象来存储 |
如果你是第一种情况,那么很明显,你会有大量重复的SQL调用。
如果是第二种情况,其实也不见得好到哪里去。因为:
1、currentUser可能不需要经常取,但相关的其它内容,由于上下文各自独立,你还是经常在重复的获取的;
2、有一个地方你无法绕过去——如果你要修改当前用户的属性,而这个全局的当前用户不是当前Context产生的,你还非得从当前Context取出来,然后再修改;
3、因为currentUser的上下文已经被抛弃了,因此程序会很容易设计成传入的不是一个UserInfo,而是一个int类型的Id值,否则底层很容易一不小心就用到这个实际上功能不全的对象,然后就抛出异常了。但这样做的后果是,获取同一个类型的实体对象,可能会有各种不同的重载形式,例如:
以下为引用的内容: IQueryable<TransactionInfo> GetTransactionsByUserId(int userId); |
因为这种设计实施之后,有时很可能就会出现只有userId的情况,那么这个时候即使UserInfo对象中其实也存在CompanyId的值,也还是要重新获取一遍UserInfo对象。为了简化这一过程,就可能会产生不同的获取形式。
这样设计完整个系统之后一跑,看着好像没什么,但真正上线却发现有点慢。当我们打开Sql server的Profiler一看,会发现很简单的一个页面的访问,其数据库访问会搞到几十次甚至上百次,其中有很多Sql语句是完全重复的。
这个问题怎么解决呢?有人会说,加个缓存机制吧。也许吧,但这种增加复杂度的设计,我觉得还是不得已而为之的一种做法。我认为更好的解决办法是,将上下文在当前页面中缓存起来。所谓的上下文,就是一种运行环境,而一次页面访问,其环境应该是相同的。首先,我们对MyDataContext做一个扩展:
以下为引用的内容: partial class MyDataContext |
然后我们再制作一个HttpModule(并且在web.config里面配置好):
以下为引用的内容: /// <summary> |
接下来,我们只要在逻辑层这么直接写即可:
以下为引用的内容: public static IQueryable<TransactionInfo> GetCompanyAccountDetails(UserInfo operatorUser, EAccountName account) |
这么改造完之后,你会发现几乎可以在所有地方直接返回IQueryable(除了有的时候Linq to Sql本身有Bug),整个逻辑层内的设计变得简单化:一开始检查各种参数(是否具备完整或者部分权限),然后根据检查结果做完全信赖的操作。由于返回的是实体对象,或者IQueryable,几乎所有重复性的Sql调用也随之自然消失了。如果有所怀疑的话,您可以用Sql Profiler自行做修改前后的对比,看看效果是否“惊人”?
也许有人会质疑,这样好吗?岂不是通过user.Company.Transactions就可以得到所有的Transaction了?没错,如果所有东西都是公开的话,就会有这个问题。如果要彻底解决这样的问题,需要将这些部分变成对逻辑层可见,而对其它层不可见的修饰方式——比如两层在一个dll里面,这些属性是internal的,或者放在两个dll里面并且打上InternalsVisibleTo标记。通过这种方式,就可以避免上层直接查找DAL中一些在BLL中需要经过权限检查才可以得到的内容。当然,如果项目比较小的情况下,你也可以选择不要这么麻烦,直接控制代码质量即可(要求有些东西必须通过BLL来获得)。
分享:浅谈ASP.NET中显示Linq To SQL输出的SQL语句最近在使用Linq To SQL的时候,为了了解不同Linq语句对性能造成的不同影响,需要获得Linq To SQL生成的SQL语句。 如果是在桌面程序中,只需要 _context.Log = Console.Out; 即可在控制台输出SQL语句。可是在ASP.NET中又该怎么办呢? 这时我想起了StringWriter
- asp.net如何得到GRIDVIEW中某行某列值的方法
- .net SMTP发送Email实例(可带附件)
- js实现广告漂浮效果的小例子
- asp.net Repeater 数据绑定的具体实现
- Asp.Net 无刷新文件上传并显示进度条的实现方法及思路
- Asp.net获取客户端IP常见代码存在的伪造IP问题探讨
- VS2010 水晶报表的使用方法
- ASP.NET中操作SQL数据库(连接字符串的配置及获取)
- asp.net页面传值测试实例代码
- DataGridView - DataGridViewCheckBoxCell的使用介绍
- asp.net中javascript的引用(直接引入和间接引入)
- 三层+存储过程实现分页示例代码
- 相关链接:
- 教程说明:
.Net教程-怎样在ASP.NET项目里面正确使用Linq to Sql。