上一篇对Linq to SQL做了一个大致的介绍,从这一篇起,将对要完成一项Linq to SQL的Case要做的一些事情,主要从细节上做一些讲解。不会很深入,但是却又是必须的。为了使讲解不落于泛泛而谈,我们首先来构建实例:
还是以博客园的系统为例子,既然称Linq to SQL为一个ORM框架,ORM,对象-关系 映射,既然O在前关系在后,说明O为重,关系是根据O得来的,那么我们就先新建一些Entity Object吧。在一个博客系统里最常见的就是User,Blog,Post。
一个用户有且仅有一个博客,而一个博客可以有零篇或者多篇博客文章。根据这个描述我们来建立Entity:
Step 1 建立实体对象
对象建立好了,我们来建设数据库表吧:users表,blogs表,posts表(为什么表都用复数?从Rails里学过来的,ActiveRecord模式里,表里每条记录都对应着一个对象,所以表用复数表示,这是一种约定)
Step 2:创建数据库表
Users表:
Blogs 表:
Posts表:
我们注意到,用户类里的LeaveTime离开时间在数据库里并没有对应的字段,因为这个是用来临时记路用户离开离开博客园的时间的,这样可以用来做一些在线统计,所以无需持久化。
Step 3:建立映射
对象也有了,关系也有了,那剩下的是什么?对,就是映射,我们怎样将对象映射到数据库表上?
我们从最简单的Post开始吧:
/// 博客文章类
/// 这个类和数据库里的posts表对应,由于
/// 类名和表名不一样,所以需要显式的指明Table特性的Name属性
/// 如果是一致的则无需指明了
/// </summary>
[Table(Name = " posts " )]
public class Post
{
/// <summary>
/// 文章Id
/// 这个对应posts表里的postid
/// 这个字段还是一个主键,所以Column特性的IsPrimaryKey属性为true
/// </summary>
[Column(Name = " postid " ,IsPrimaryKey = true )]
public int Id { get ; set ; }
/// <summary>
/// 文章从属的博客
/// 这个对应着表里的blogid,名字一样所以只需要加个Column特性就可以了
/// 下面几个也是一样的意思
/// </summary>
[Column]
public int BlogId { get ; set ; }
/// <summary>
/// 标题
/// </summary>
[Column]
public string Title { get ; set ; }
/// <summary>
/// 内容
/// </summary>
[Column]
public string Body { get ; set ; }
/// <summary>
/// 发表时间
/// </summary>
[Column]
public DateTime CreateDate { get ; set ; }
}
映射建完了,我们可以施加一些操作了。
在上一篇文章里介绍了,Linq to SQL的入口点是DataContext类,这个类主要做这么几件事情:
将我们用C#写的这个查询翻译成SQL语句,当然也并不是他全权负责翻译工作。
执行查询
连接的管理,这样我们就不用写啥Connection了啊,也不用担心数据库的连接和关闭的问题。
Step 4:执行查询
那我们就首先实例化一个DataContext类吧(为了好测试,使用一个控制台程序):
Console.WriteLine(post.Title);
那你将会从控制台里得到下面的东东:
这就是Linq为你生成的SQL语句,是不在是比你写的还标准啊,呵呵。
当然,我们常常做的肯定不是像上面这样的全查询,我们还要附加一些条件的。
上一篇还提到过:由于Table<TEntity>实现了IEnumerable<TEntity>接口,那么所有的查询表达式都可以在这里使用了,这样你先前在Linq to Objects里学到东西现在又有了新的用途了:
比如我们可以对BlogId做筛选:
where post.BlogId == 2
select post;
看看这次生成的SQL语句:
可以看到生成的SQL语句对blogid做了筛选,而且值得庆祝的是,Linq to SQL为我们生成的SQL语句还使用的是参数,她并不是仅仅将数值和语句组合在一起。关于使用参数而不是拼凑起来的SQL语句的好处在这里啰嗦两句,主要有两点:
这样可以有效的SQL注入攻击,这个hack手段曾经让很多网站吃尽了苦头。
使用参数可以利用Sql Server对SQL语句的缓存和预编译作用,因为使用参数的SQL语句可能多次用到,而拼凑起来的SQL语句却和特定的查询有关。
这样的筛选还不能体现出Linq的智能了,请再看这个:
where post.Title.StartsWith( " y " )
select post;
面对这样的一个 Linq 查询, Linq to SQL 居然知道将它翻译成 LIKE 子句,从上图倒数第二行最后中括号里面的东西可以看出, Linq to SQL 还知道将传入的参数设置为 y% ,我不得不惊叹:太强大了。
但是不是所有的C#的方法都可以使用呢?答案是否定的。比如:
where post.Title.StartsWith( " y " )
select post.CreateDate.ToString( " yyyy-MM-dd " );
上面只是做了筛选的实例,你还可以去尝试一下排序和分组,这个和Linq to Objects里的用法是一样的,你可以到前面的文章里复习一下。
Step 5:插入对象
本系列的文章到现在为止都是在介绍查询,难道Linq to SQL只能做查询么?不能向数据库添加数据?,肯定不是的:
// 先实例化一个新的要插入的对象
Post post = new Post();
post.BlogId = 2 ;
post.Title = " test " ;
post.Body = " test,test,test,test " ;
post.CreateDate = DateTime.Now;
// 调用Table<TEntity>的InsertOnSubmit方法
posts.InsertOnSubmit(post);
// 把改变提交到数据库,这个时候才真正执行了
dbContext.SubmitChanges();
// 提交修改后,你就可以查询新插入的post的Id了
Console.WriteLine(post.Id);
一个简单的不能再简单的insert into语句,加上一条返回新插入记录的标识值的语句。
如果你按照本文所说的一步步往下来,在执行上面的插入的时候肯定会碰到异常,这是因为,Id对应的数据表字段postid是该表的主键,你不应该在插入的时候赋值,有人说我实例化对象的时候确实没有给post的Id属性赋值啊,但是.net会在后台为我们将Id赋值为0,所以你要对映射对象做一下修改:
public int Id { get ; set ; }
Step 6:更新数据
可以插入肯定就可以更新了,下面就来看看如何更新呢:
做更新的时候,你首先得从数据库查询出该对象,然后对该对象的属性进行修改,最后更新到数据库:
where post.BlogId == 2
select post;
foreach (var post in posts)
post.BlogId = 5 ;
dbContext.SubmitChanges();
有了插入,更新,查询就差一个Delete CURD就全了,对于delete更简单了,你只要调用Table<TEntity>的DeleteOnSubmit方法就行了,这里就不再详述。
后记
这篇文章主要关注How Do I上面,对一个简单的单表CURD做一个比较全面的介绍,下一篇会更深入一些,将会涉及到多表的连接,两个表之间的关系怎样反应到映射上来,还将探讨一下Linq to SQL中的延迟计算的问题。本篇定位为入门级,只期望给一些初学者或者未接触过Linq的朋友一些提示。