不过在Linq里,这一切都不是问题。Linq to SQL这几种方式都支持,而且走的更远。
l 纯Attribute方式配置
l XMl文件的配置方式
l 使用SqlMetal命令行工具生成配置代码
l 使用Visual Studio的设计器生成映射配置代码
纯Attribute方式配置
前面几篇我们都是使用这种Attribute的方式的,在Linq to SQL的第一篇我就介绍了,这些映射的Attribute都存在于System.Data.Linq.Mapping命名空间下。在前面我们已经出现了TableAttribute,ColumnAttribute, AssociationAttribute。实际上还有DataBaseAttribute,ProviderAttribute,FunctionAttribute。本篇后面的内容将在不同的地方对他们做全面的介绍。
Table
Table特性是加在类上面的,不能重复的加,也不能继承。Table,顾名思义,就是用来定义类和数据库表之间的映射的,Table是映射定义的关键点,如果没有给一个类加上Table特性,那么即使这个类里面的属性或字段加上了Column也是无效的,实际上,前面几篇中的例子,如果没有给映射类加Table特性在运行时会抛出一个异常:System.InvalidOperationException,说你的类没有映射为一个Table。Table特性只有一个属性Name,用于在你的类名和数据库表名不同的时候来定义。
Column
Column特性就复杂得多了。不过可喜的是,这些属性的命名都很好,只要见到名字了,基本上就能把意思给猜出来了。
AutoSync
AutoSync:自动同步。这个属性是一个枚举类型:
public enum AutoSync
{
Default,
Always,
Never,
OnInsert,
OnUpdate
}
这个属性的意思是,执行insert和update操作后,这个类的属性如何和数据库表的那个被修改列的字段进行同步。
AutoSync. Default,自动选择,默认就是这个,一般是如果该列在数据库里有默认值,Column的IsDbGenerated属性标记为true的时候则同步。
AutoSync. Always,总是进行同步
AutoSync.Never,从来不同步
AutoSync.OnInsert,在执行插入操作后同步,像我们博客园的那个例子,Blog的Id是主键,自增的,插入数据库的时候我们并不提供,而是数据库自动生成的,那这个时候我们的类插入以后这个Id如何同步呢?
AutoSync. OnUpdate,在更新的时候同步
实际上我们可以看看,这个AutoSync是如何影响Linq to SQL的行为的(打开前面我们曾经建的那个博客园的例子,如果你没有创建一个强烈建议你回到前几篇按照那里的步骤新建)
我们执行一下对Post的插入:
DataContext dbContext = new DataContext(ConfigurationManager.ConnectionStrings["CnBlogs"].ConnectionString);
dbContext.Log = Console.Out;
var post = new Post {
BlogId = 1,
Title = "Linq to SQL How do I(3)",
Body = "废话一堆",
};
dbContext.GetTable<Post>().InsertOnSubmit(post);
dbContext.SubmitChanges();
看看生成的SQL代码:
在insert的代码下面,我们还会看到一个select的代码,将postid和createdate给查询回来,但我们并没有执行查询操作啊,原来这就是AutoSync在使坏,在默认的时候IsDbGenerated为true的列是被会查出来返回的。为了检查一下说的是不是对的,我将Post的Title属性修改一下:
[Column(AutoSync=AutoSync.Always)]
public string Title { get; set; }
然后再执行上面 插入代码,看看生成的SQL又将如何呢:
呵呵,生成的select语句也将Title给查回来了。
现在应该明白这个AutoSync的意义了吧,你可以自己一一试验。
CanBeNull
这个属性可以指定对应的数据库表的列是否允许是null的,如果这个列不能为null,那你给这个属性赋个null的时候,是要触发异常的。不过要记住,null并不代表是一个空字符串或者零,关于null的更多内容,你可以参见园子Anytao的佳作。
DbType
如果你想使用DataContext的CreateDataBase方法从映射类创建数据库表,那么最好指定这个,这样你就可以明确的指定出你的这个列在数据库表中的DbType是啥,不然Linq to SQL会从属性的类型推断DbType,这有可能不怎么适合,比如这个string类型:
[Column(DbType="NVarchar(50) not null")]
public string Title { get; set; }
在这里我们是不是发现了,我们还可以从映射类创建数据库,都说ORM是以O为主,可我们实践的时候总是先建立数据库,然后根据数据库创建Object,那这不是ROM了么,从这里的信息表示,你可以先设计好Domain Object,并建立好Object之间的关系,最后使用CreateDataBase方法来创建数据库表。关于CreateDataBase更多的信息在后面我会更进一步的说明的。
Expression
我只能说Linq to SQL做的太周到了,连这个都考虑了。Expression用来表示一个计算列,什么意思?意思是这个属性是数据库表的列通过计算获取的,比如,假如我们数据库里有一列price存储的是美元,可是取出来的时候我们要求用人民币表示:
[Column(Expression="price * 6")]
public float Price
{ get; set; }
上面的例子只是用来说明Expression这个属性的用途,而这个例子的做法实在是不可取,对于美元转人民币这种汇率问题,汇率是时刻波动的,所以这样硬编码,实在是糟糕的很,各位读了以后BS一下,一笑而过吧。
IsDbGenerated
从名字上就可以看出来了,这个表明这个属性的值是数据库产生的,不需要我们的程序赋值,比如这个自增的列,比如这个有默认值的列(时间,我们可以用SQL getdate()函数设置值,而无需我们在程序里指定)
IsPrimaryKey
是主键吗?如果是就要指定这个属性了,如果你的主键是多个列组成的,那么就在多个属性上加吧。关于主键,在后面还会进一步介绍。
IsVersion和UpdateCheck
这两个在这里就不说了,后面会有大篇幅的介绍的。
这个Column特性是从抽象特性Data继承来的,从这个特性继承了两个属性:
Name
这个是当你的属性名字和这个列名不一致的时候指定列名用的。
Storage
有的时候啊,你的属性里的set里面应用了复杂的业务逻辑,而Linq to SQL在将值从数据库取出,然后赋值给这个属性的时候,默认是要使用这个set的,这个时候在这种情况下(从数据库取值赋给类的属性),你并不想执行这个set里面的逻辑,想把这个值直接给属性背后的那个私有字段:
private float _price;
/// <summary>
/// 由于某些原因,当从数据库取值给Price属性的时候
/// 你不想给这个值乘以20
/// </summary>
[Column(Storage="_price")]
public float Price
{
get { return _price; }
set{_price = _price * 20;}
Column的就到此为止了,我们现在来介绍Association特性。
Association
这个特性是用来建立实体之间关系的。在前面的例子里我们看到了:
/// <summary>
/// 一个博客有零篇或多篇文章,
/// </summary>
[Association(ThisKey="Id",OtherKey = "BlogId")]
public EntitySet<Post> Posts { get; set; }
Association有两个最重要的属性就是ThisKey和OtherKey
ThisKey用来标识和别的对象关联的键,如果没有指明就用本类属性上标识有IsPrimaryKey的了。OtherKey用来定义关联的类的键,如果没有指定就用关联的那个类的标识列了。
Association特性也是从Data特性继承来的,也有Name和Storage属性。Storage属性和Column是一样的,这里的Name属性是CreateDataBase利用映射类动态创建数据库的时候建立关系用的,这个Name就是关系名。关于Association的更多信息你还是参看上一篇文章。
我们用的最多的就是上面三个Attribute了,不过Linq to SQL还提供好几个Attribute,我想这个放在后面相应的地方再做介绍。
XML文件的配置方式
有心的你也许发现了,DataContext类有好几个重载的构造函数,我们常用的是:
DataContext(string fileOrConnectionString)
还有一个:
DataContext(string fileOrConnectionString,MappingSource mapping)
这个MappingSource是干吗的呢?
在System.Data.Linq.Mapping命名空间下,你会发现这样的个关系:
看到它的两个子类的名称你也许就会猜出十之八九了吧,我们前面所使用的就是AttributeMappingSouce映射,除此之外还可以使用XML作为映射的配置文件哦,从上面的图看,XmlMappingSource还有几个静态的方法,他们可以以各种形式的Xml数据源来构建XmlMappingSource实例:
XmlMappingSource mapping = XmlMappingSource.FromXml(File.ReadAllText(@“e:"cnblogs"map.xml”));
DataContext dbContext = new DataContext(connectionString,mapping);
用这种方式构建DataContext对象我们就可以使用Xml作映射了。
我们就来看看这个XML映射文件的格式:
<?xml version="1.0" encoding="utf-16"?>
<Database Name="Cnblogs" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="posts">
<Type Name="Yuyijq.Linq.Cnblogs.Domain.Post">
<Column Name="postid" Member="Id" IsPrimaryKey="true" IsDbGenerated="true" />
<Column Name="blogid" Member="BlogId" />
<Column Name="title" Member="Title" />
<Column Name="body" Member="Body" />
<Column Name="createdate" Member="CreateDate" IsDbGenerated="true"/>
</Type>
</Table>
</Database>
大家可以看到,这个文件有一个根元素DataBase,这个对应着Attribute里的DataBaseAttribute,每个DataBase元素可以包含有一个或多个Table,每个Table元素有一个Type元素,这个是用来指定这个Table和哪个实体类对应的。一个Type元素有一个或多个Column和Association元素。从上面的Xml中可以看出,它的用法和Attribute的一样,只是多了一个Member,这个是用来指定实体类的属性的,其余的意义和Attribute的是一样的。
基本上每个ORM工具都提供了代码生成工具,我想如果没有代码生成使用ORM的工作量也是不小的,靠手动的去写实体类和映射文件纯粹是个体力活。Linq to SQL也不例外,为你提供了强大的工具。
使用SqlMetal命令行工具生成配置代码
SqlMetal是个命令行工具,这样你就可以很好的将其与MSBuild等进行集成,以达到自动化的目的。只要写个批处理文件,映射的东东都自动生成了。
关于SqlMetal的用法你只要在命令行里输入SqlMetal /?就可以了,这里就不做过多的介绍。
值得注意的是:我们写的实体类往往希望他们能通过WebService或WCF传递,那么就要求它们是可系列化的,为此微软为SqlMetal工具准备了这个开关:
/serialization:<option>,option有两个值:None和Unidirectional,默认是None。
使用Visual Studio的设计器生成映射配置代码
毕竟整天伴随着我们的是VS,所以还是来个可视化的工具比较过瘾。
你只要在你的项目当中新建一个LINQ to SQL Classes类型的新项,然后从服务器浏览器里把表拖到设计界面上,然后用工具栏里面的工具,连接好各个表之间的关系就OK了。
VS会为我们生成三个文件:一个以dbml为后缀的文件(其实这个文件就是个xml格式的文件,它和刚才只用xml作映射配置的文件格式是类似的)一个C#代码文件,注意到这个代码文件是个局部类,该类和dbml文件共同编译成一个类,这个和asp.net里面的aspx和它的后台代码使用的方式类似。还有一个是以layout为后缀名的,这个文件是设计器使用的,用来存储设计器中各表在界面上的位置,和我们的程序无关。
关于可视化的设计器的使用这里也不做介绍了,自己动动手,然后看看生成的几个文件里到底是什么东西,加上前面对手动操作的详细讲解,我想理解这个将是很容易的事情。
后记
本来想用三篇的长度将如何使用部分,看来还是不行。这篇文章比较长,但东西不是很多。
下一篇将是How do I的最后一篇,将对DataContext,做Update和Delete时RowVersion、Linq to SQL中的事务和延迟计算、延迟加载等内容做详细介绍。