走进Linq-Linq to SQL How do I(3)

NHibernate 是使用 XML 作为映射的配置文件, Caslte 中的 ActiveRecord( 底层还是使用 NHibernate) 是使用 Attribute 的方式做映射配置。一个是非侵入的,一个是侵入的。有人喜欢用 XML 做配置,说这样灵活,修改配置无需重新编译,有人喜欢使用 Attribute 的配置方式,说这样可维护性好,可以得到编译期的检查。

不过在Linq里,这一切都不是问题。Linq to SQL这几种方式都支持,而且走的更远。

Attribute方式配置

XMl文件的配置方式

使用SqlMetal命令行工具生成配置代码

使用Visual Studio的设计器生成映射配置代码

 

Attribute方式配置

前面几篇我们都是使用这种Attribute的方式的,在Linq to SQL的第一篇我就介绍了,这些映射的Attribute都存在于System.Data.Linq.Mapping命名空间下。在前面我们已经出现了TableAttribute,ColumnAttribute, AssociationAttribute。实际上还有DataBaseAttributeProviderAttributeFunctionAttribute。本篇后面的内容将在不同的地方对他们做全面的介绍。

Table

Table特性是加在类上面的,不能重复的加,也不能继承。Table,顾名思义,就是用来定义类和数据库表之间的映射的,Table是映射定义的关键点,如果没有给一个类加上Table特性,那么即使这个类里面的属性或字段加上了Column也是无效的,实际上,前面几篇中的例子,如果没有给映射类加Table特性在运行时会抛出一个异常:System.InvalidOperationException,说你的类没有映射为一个TableTable特性只有一个属性Name,用于在你的类名和数据库表名不同的时候来定义。

Column

Column特性就复杂得多了。不过可喜的是,这些属性的命名都很好,只要见到名字了,基本上就能把意思给猜出来了。

AutoSync

AutoSync:自动同步。这个属性是一个枚举类型:

public enum AutoSync

{

    Default,

    Always,

    Never,

    OnInsert,

    OnUpdate

}

这个属性的意思是,执行insertupdate操作后,这个类的属性如何和数据库表的那个被修改列的字段进行同步。

AutoSync. Default,自动选择,默认就是这个,一般是如果该列在数据库里有默认值,ColumnIsDbGenerated属性标记为true的时候则同步。

AutoSync. Always,总是进行同步

AutoSync.Never,从来不同步

AutoSync.OnInsert,在执行插入操作后同步,像我们博客园的那个例子,BlogId是主键,自增的,插入数据库的时候我们并不提供,而是数据库自动生成的,那这个时候我们的类插入以后这个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代码:
走进Linq-Linq to SQL How do I(3)

insert的代码下面,我们还会看到一个select的代码,将postidcreatedate给查询回来,但我们并没有执行查询操作啊,原来这就是AutoSync在使坏,在默认的时候IsDbGeneratedtrue的列是被会查出来返回的。为了检查一下说的是不是对的,我将PostTitle属性修改一下:

        [Column(AutoSync=AutoSync.Always)]

        public string Title { get; set; }

然后再执行上面 插入代码,看看生成的SQL又将如何呢:
走进Linq-Linq to SQL How do I(3)

呵呵,生成的select语句也将Title给查回来了。

现在应该明白这个AutoSync的意义了吧,你可以自己一一试验。

CanBeNull

这个属性可以指定对应的数据库表的列是否允许是null的,如果这个列不能为null,那你给这个属性赋个null的时候,是要触发异常的。不过要记住,null并不代表是一个空字符串或者零,关于null的更多内容,你可以参见园子Anytao的佳作。

DbType

如果你想使用DataContextCreateDataBase方法从映射类创建数据库表,那么最好指定这个,这样你就可以明确的指定出你的这个列在数据库表中的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

是主键吗?如果是就要指定这个属性了,如果你的主键是多个列组成的,那么就在多个属性上加吧。关于主键,在后面还会进一步介绍。

IsVersionUpdateCheck

这两个在这里就不说了,后面会有大篇幅的介绍的。

这个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有两个最重要的属性就是ThisKeyOtherKey

ThisKey用来标识和别的对象关联的键,如果没有指明就用本类属性上标识有IsPrimaryKey的了。OtherKey用来定义关联的类的键,如果没有指定就用关联的那个类的标识列了。

Association特性也是从Data特性继承来的,也有NameStorage属性。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命名空间下,你会发现这样的个关系:
走进Linq-Linq to SQL How do I(3)

看到它的两个子类的名称你也许就会猜出十之八九了吧,我们前面所使用的就是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元素有一个或多个ColumnAssociation元素。从上面的Xml中可以看出,它的用法和Attribute的一样,只是多了一个Member,这个是用来指定实体类的属性的,其余的意义和Attribute的是一样的。 

基本上每个ORM工具都提供了代码生成工具,我想如果没有代码生成使用ORM的工作量也是不小的,靠手动的去写实体类和映射文件纯粹是个体力活。Linq to SQL也不例外,为你提供了强大的工具。

使用SqlMetal命令行工具生成配置代码

SqlMetal是个命令行工具,这样你就可以很好的将其与MSBuild等进行集成,以达到自动化的目的。只要写个批处理文件,映射的东东都自动生成了。

关于SqlMetal的用法你只要在命令行里输入SqlMetal /?就可以了,这里就不做过多的介绍。

值得注意的是:我们写的实体类往往希望他们能通过WebServiceWCF传递,那么就要求它们是可系列化的,为此微软为SqlMetal工具准备了这个开关:

/serialization:<option>option有两个值:NoneUnidirectional,默认是None

使用Visual Studio的设计器生成映射配置代码

毕竟整天伴随着我们的是VS,所以还是来个可视化的工具比较过瘾。

你只要在你的项目当中新建一个LINQ to SQL Classes类型的新项,然后从服务器浏览器里把表拖到设计界面上,然后用工具栏里面的工具,连接好各个表之间的关系就OK了。

VS会为我们生成三个文件:一个以dbml为后缀的文件(其实这个文件就是个xml格式的文件,它和刚才只用xml作映射配置的文件格式是类似的)一个C#代码文件,注意到这个代码文件是个局部类,该类和dbml文件共同编译成一个类,这个和asp.net里面的aspx和它的后台代码使用的方式类似。还有一个是以layout为后缀名的,这个文件是设计器使用的,用来存储设计器中各表在界面上的位置,和我们的程序无关。

关于可视化的设计器的使用这里也不做介绍了,自己动动手,然后看看生成的几个文件里到底是什么东西,加上前面对手动操作的详细讲解,我想理解这个将是很容易的事情。

 

后记

本来想用三篇的长度将如何使用部分,看来还是不行。这篇文章比较长,但东西不是很多。

下一篇将是How do I的最后一篇,将对DataContext,做UpdateDeleteRowVersionLinq to SQL中的事务和延迟计算、延迟加载等内容做详细介绍。

 

你可能感兴趣的:(LINQ)