序言

很多朋友都向我提过,希望我写一下关于Linq to SQL 或者 VS 插件方面的文章。尽管市面上有很多 Linq to SQL 的书籍,但是都是介绍怎么用,缺乏深度。关于 VS 插件方面的书籍也是很显浅,按书籍做出来的东西,只能是学生级别的东西,根本拿不出手。他们觉得我有这个能力写好。

从技术能力的角度来说,的确是不存在什么问题,但是,要把一门技术讲精讲透,是花很时间的事情。自己付出了很多,如果不能得到读者的认同,那这个专题写下去也没什么意义了。这个专题不是教你怎么使用Linq to SQL,而是让你明白Linq to SQL的原理,对于想写ORM的朋友,绝对不可错过。写完《深入了解 Linq to SQL》这个系列后,下一个系列就是《VS 插件开发了》,所以,大家如果希望我继续写下去,请记得点推荐。你们的推荐,就是我写下去的动力。

概述

关于对象的标识,简单点说,就是主键相同的对象,在数据上下文的缓存中,只有一个。数据上下文,在加载数据,创建对象之后,接着对所创建的每个实体类的对象,都会克隆一份对象副本(浅复制)记住这点,我们在后面要用到,用来保存对象的初始值,当对象的属性值修改后,副本的属性值是不改的。注意,只有实体类对象才会创建对象副本,而匿名类对象是不会生成副本,也只实体。我们看下面一段代码:

例一

var c1 = db.Categories.Single(o => o.CategoryId == 1);
var c2 = db.Categories.Single(o => o.CategoryId == 1);

在例一这个例子中,c1、c2 是相等的,我们再来看下面一个例子:

例二

var c1 = db.Categories.Select(o => new { o.CategoryId, o.CategoryName }).Single(o => o.CategoryId == 1);
var c2 = db.Categories.Select(o => new { o.CategoryId, o.CategoryName }).Single(o => o.CategoryId == 1);

这个例二这个例子中,c1、c2 是不相等的。

为什么匿名对象不支持对象标识呢?因为匿名对象,不一定有主键。而 Linq to SQL ,是通过实体的主键来标识一个实体对象的,当从数据库加载完数据,会先根据主键检索缓存中是否有已经存在的对象,没有才会创建对象。这样会引起一个什么样的问题呢?执行下面的代码。

var c1 = db.Categories.Single(o => o.CategoryId == 1);

假设 CategoryId 为 1 的 CategoryName 为 “Beverages”,打开数据库,把该值改为“New Name”后,如下图:

图一

然后再执行下面的代码:

var c2 = db.Categories.Single(o => o.CategoryId == 1);

这时候 c2 修改后的 c2.CategoryName 的值是什么呢?仍然为“Beverages”!因为第二次加载完数据的时候,由于已经存在了主键(CategoryID)为“1”的Category,所以在第二次的加载中,不再创建新的对象,以是使用之前所创建的Category对象。那怎么样才能使用 c2 的 CategoryName 的值为最新值“New Name”呢?调用数据上下文的 Refresh 方法即可。

 db.Refresh(RefreshMode.OverwriteCurrentValues, c2);

跟踪属性的更改

我们先来看一个更新的例子:

var c1 = db.Categories.Single(o => o.CategoryId == 1);
c1.CategoryName = "xxx";
db.SubmitChanges(); c1.CategoryName = "xxx";
db.SubmitChanges();

通过查看生成的SQL,我们可以发现,尽管 SubmitChagens 方法执行了两次,但是实际上SQL只执行了一次,因为第二次的 CategoryName 并没有修改,那么Linq to SQL如何得知某一个属性是修改了或者没有呢?我们看Category实体类的定义,为了节省篇幅,只选取一部份。

[Table(Name="Categories")]
public partial class Category : INotifyPropertyChanging, INotifyPropertyChanged
{
[Column(Storage="_CategoryName", DbType="VarChar(15)", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
public string CategoryName
{
get
{
return this._CategoryName;
}
set
{
if ((this._CategoryName != value))
{
this.OnCategoryNameChanging(value);
this.SendPropertyChanging();
this._CategoryName = value;
this.SendPropertyChanged("CategoryName");
this.OnCategoryNameChanged();
}
}
} protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

我们可以看得到,实体类Category实现了 INotifyPropertyChanged 的接口,通过这个接口,就可以得知某个属性是否已经更改了,但是,事实上Category实体类不实现上面的接口,也是没有问题的,如下所示:

[Table(Name = "Categories")]
public partial class Category
{
[Column(Storage = "_CategoryName", DbType = "VarChar(15)", CanBeNull = false, UpdateCheck = UpdateCheck.Never)]
public string CategoryName
{
get { return this._CategoryName; }
set { this._CategoryName = value; }
}
}

为什么这样也可以呢?记得我们刚才说到对象副本吗?当 Category 没有实现 INotifyPropertyChanged 这个接口时,Linq to SQL会将Category实体对象和原始的对象副本作比较来得知Category的属性值是否更改了。

但是下面的定义,会导致 CategoryName 的值即使用修改了,也不会更新到数据库。因为,当 CategoryName 的值发生变化的,并没有通知到接口 INotifyPropertyChanged,而当实体类继承于INotifyPropertyChanged时,Linq to SQL是依赖INotifyPropertyChanged来获取值被更改的属性。

[Table(Name = "Categories")]
public partial class Category: INotifyPropertyChanged
{
[Column(Storage = "_CategoryName", DbType = "VarChar(15)", CanBeNull = false, UpdateCheck = UpdateCheck.Never)]
public string CategoryName
{
get { return this._CategoryName; }
set { this._CategoryName = value; }
}
}

由上面我们可以知道,通过对象标识,可以使得Model的定义最为简洁,在Linq to SQL的设计里,你会常常可以看到,很多地方都遵循简洁模型这个原则,这个原则,在后面的内容里会给大家介绍。同时你会发现,如果对于没有定义好主键的实体,是不能进行添加、更新、删除的操作,因为这些操作都依赖于对象标识,而对象标识又需要实体的主键。又因为主键是用来标识对象的,所以主键是不能何改的。

备注

ALinq 提供了 Insert、Update、Delete 三个方法,这三个方法,只是简单地将 Linq 的 Lamdba 表达式转成 SQL 语句然后再执行。因此,它不依赖于对象标识。所以它可以:

1、没有主键也能更新

2、可以更新主键的值

总之,SQL 能操作的,它都可以。关于这几个方法的使用,ALinq 的文档中都有介绍,看文档即可。

它不可以:

1、该方法不能被扩展方法置换。

例如:如果你使用的是 InsertOnSubmit(customer), 你可以通过重写 InsertCustomer 方法,替换原有的方法。当然你需要将一个表拆分成两个时,非常有用。如果你用的是 Insert 方法,则无效。

2、ALinq Inject 注入框架无法对其注入。

《深入了解 Linq to SQL》之对象的标识 —— 麦叔叔呕心呖血之作的更多相关文章

  1. 《深入了解 Linq to SQL》之对象的增删改 —— 麦叔叔呕心呖血之作

    你的程序里,是否到处充斥着这种代码: db.Customers.InsertOnSubmit(customer); db.SubmitChange(); 如果某一天,因为 Customers 表的数据 ...

  2. LINQ To SQL在N层应用程序中的CUD操作、批量删除、批量更新

    原文:LINQ To SQL在N层应用程序中的CUD操作.批量删除.批量更新 0. 说明 Linq to Sql,以下简称L2S.    以下文中所指的两层和三层结构,分别如下图所示: 准确的说,这里 ...

  3. LINQ to SQL语句(17)之对象加载

    对象加载 延迟加载 在查询某对象时,实际上你只查询该对象.不会同时自动获取这个对象.这就是延迟加载. 例如,您可能需要查看客户数据和订单数据.你最初不一定需要检索与每个客户有关的所有订单数据.其优点是 ...

  4. LINQ to SQL语句(16)之对象标识

    对象标识 运行库中的对象具有唯一标识.引用同一对象的两个变量实际上是引用此对象的同一实例.你更改一个变量后,可以通过另一个变量看到这些更改. 关系数据库表中的行不具有唯一标识.由于每一行都具有唯一的主 ...

  5. linq to sql中的自动缓存(对象跟踪)

    linq to sql中,对于同一个DataContext上下文环境,根据表主键选择记录时(当然这里所指的“记录”会自动转成“对象”),如果该记录已经被select过,默认情况下会被自动缓存下来,下次 ...

  6. LINQ TO SQL:操作有层次关系的对象

    对于关系型数据与对象数据之间最大的隔阂就是由标识列连接起来的行(关系型数据)与由集合保存的对象(对象数据)之间的冲突. 例如某个Subject对象(也就是数据库中的Subject表),从Subject ...

  7. 年终巨献 史上最全 ——LINQ to SQL语句

    LINQ to SQL语句(1)之Where 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句.Where操 ...

  8. LINQ to SQL语句(19)之ADO.NET与LINQ to SQL

    它基于由 ADO.NET 提供程序模型提供的服务.因此,我们可以将 LINQ to SQL 代码与现有的 ADO.Net 应用程序混合在一起,将当前 ADO.NET 解决方案迁移到 LINQ to S ...

  9. LINQ to SQL语句(10)之Insert

    1.简单形式 说明:new一个对象,使用InsertOnSubmit方法将其加入到对应的集合中,使用SubmitChanges()提交到数据库. var newCustomer = new Custo ...

随机推荐

  1. CF 545E Paths and Trees

    题目大意:给出n个点,m条无向边,每条边有长度.求一棵树,要求树上的每个点到源点距离最小的前提下,使得树上的边的长度和最小.输出树上边的总长度,以及树上的边的序号(按输入顺序 1...m). 思路 : ...

  2. html 表单初步学习

    <html> <head> <title> 静态页面</title> </head> <body> 这是一个静态页面<br ...

  3. Static用法

    一.Static全局变量和全局变量的区别 1)全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量.全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式. 这两者在存储 ...

  4. 最详细的 HTTPS 科普扫盲帖

    为什么需要https HTTP是明文传输的,也就意味着,介于发送端.接收端中间的任意节点都可以知道你们传输的内容是什么.这些节点可能是路由器.代理等. 举个最常见的例子,用户登陆.用户输入账号,密码, ...

  5. 构建高性能web站点笔记一

    构建高性能web站点笔记 第三章 服务器并发处理能力 3.1吞吐率 描述服务器在实际运行期间单位时间内处理的请求数.也就是一定并发用户的情况下,服务器处理请求能力的量化体现. 吞吐率的前提包括: 并发 ...

  6. JUnit单元测试框架的使用

    http://blog.csdn.net/mao520741111/article/details/51462215 原文地址 http://www.open-open.com/lib/view/op ...

  7. Android学习笔记__1__Android体系架构

    Android 体系结构图 Android作为一个移动设备的平台,其软件层次结构包括了一个操作系统(OS),中间件(MiddleWare)和应用程序(Application).根据Android的软件 ...

  8. 《招聘一个靠谱的iOS》面试题参考答案(下)

    相关文章: <招聘一个靠谱的iOS>面试题参考答案(上) 说明:面试题来源是微博@我就叫Sunny怎么了的这篇博文:<招聘一个靠谱的 iOS>,其中共55题,除第一题为纠错题外 ...

  9. proxy set 拦截

    set方法用来拦截某个属性的赋值操作. 假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy保证age的属性值符合要求. let validator = { ...

  10. Java之Static静态修饰符详解

    Java之Static静态修饰符详解 Java之Static静态修饰符详解 一.特点 1.随着类的加载而加载,随着类的消失而消失,生命周期最长 2.优先于对象存在 3.被所有类的对象共享 4.可以直接 ...