Entity Framework的存储模型切换实践

  最近做一个项目,里面需要同时支持Sql Server和SQLite。考虑到Entity Framework虽然目前还有诸多不足,但是基本上满足于我的项目的要求,加上,也希望做一个尝试,毕竟,虽然使用过去的技术和手段可能更可靠、更熟练、风险更低,但是,在技术行业,不能止步不前,如果某种新技术有你需要的某些特性,并且你认为值得尝试,那就应该出手试试,虽然尝试可能会失败(以及不得不吞下失败产生的苦果),但是如果成功,其带来的好处也是很大的,也代表着你又往前迈进了一步。对于Entity Framework我也是新手,如果各位朋友发现文中有什么可以改进的地方或者不正确的地方,欢迎指点。

 

  废话不多说了。切入正题。Entity Framework需要三种文件支持:CSDL(概念架构定义语言)、SSDL(存储架构定义语言)、MSL(映射规范语言),以及,一个据此生成的类文件(这里我们不研究这个类文件)。从这种结构上看,EF是完全支持上层稳定的概念架构与底层具体可变动的存储架构之间的独立和分离的。但通常我们利用VS的设计器只能生成一个单独的.edmx文件,其中包含了CSDL、SSDL和MSL的内容,而本质上,它们3个是完全可以独立成3个文件的。对于我们当前的目标,我们需要把SSDL从.edmx文件中剥离出来,并创立分别针对于Sql Server和SQLite的SSDL。

 

下面详述一下我的做法。

1、根据Sql Server中的表、视图结构,创建SQLite数据库

  由于之前已经Sql Server上已经有了相关的表和视图,因此以Sql Server为模板,创建SQLite数据库,并在SQLite数据库中创建与Sql Server中的表和视图几乎完全一样的表和视图。之所以说“几乎完全一样”,是因为SQLite中的类型系统与Sql Server中的差别很大。但是因为我没有用到Sql Server中太特别的类型,因此这个问题的解决相对容易一些。我从Sql Server中生成表和视图的脚本,然后修改一下让SQLite可以识别和执行。主要的修改包括,去掉“dbo”架构的指定,将自增标识列主键的声明“int identity(1,1) primary key”改为“integer primary key autoincrement”,后者是SQLite中自增列主键的声明语句,对于其他简单类型,比如varchar/nvarchar, bit, money等等类型则保留原有的声明不变,这些类型虽然SQLite并不“支持”,但是执行的时候并不会出错,而且利用“.schema”命令查询表架构时,这些类型的声明是被保留的(后面在生成针对SQLite的EDM的时候,似乎也说明这些类型声明产生了正面作用),另外,有一点,这次我没有用到DateTime类型,因此无法断定2种数据库对它的支持的差别。SQLite的类型系统的特点是,动态性,虽然你创建表的时候可以指定某种“类型”,但是你完全可以向一个“int”列插入一个字符串。最后,我没有在SQLite中创建任何外键约束,虽然SQLite支持外键,但是为了简化复杂性(毕竟这是我的初次尝试),就牺牲一些“完整性”吧。

   由于保证了SQLite中的表和视图与Sql Server中的完全一致(除了类型上的固有差别),因此,可以保证两者在创建EDM的时候,其CSDL和MSL是完全相同的。

2、依据Sql Server创建EDM

  具体方法就和我们平常做的一样。利用VS比较强大的设计器,从Sql Server中生成模型,并且创建好各个实体之间的Association。经过这一步,我们会得到一个.EDMX文件以及一个对应的类文件。

 

3、创建针对Sql Server的SSDL文件

  用XML编辑器打开上面生成的.EDMX文件,可以很清楚的分辨出SSDL、CSDL、MSL三部分。我们把“<edmx:StorageModels>”节点下的<Schema>节点的所有内容Copy出来,就是下图中黄色标记出来的那部分:

 

然后新建一个后缀名为.SSDL的XML文件,把Copy出来的Schema元素粘贴进去。保存。就可以了。

 

3、创建针对SQLite的SSDL文件

  这一步有2种办法:

  • 将针对Sql Server的SSDL文件复制一份过来,然后将其修改成符合SQLite的要求。主要是去掉一些SQLite不支持的东西,比如,对于Schema="dbo"这样的声明。以及,修改Schema元素的Provider属性和ProviderManifestToken属性。前者需要修改为"System.Data.SQLite",后者修改为“ISO8601”(为何改成这2个值?看到后面就会知道了)
  • 利用VS的设计器重新针对SQLite数据库生成一份EDMX文件,然后抽取其中的SSDL定义。
  我采用的是第二种方法。 这里需要用到针对SQLite的ADO.NET提供程序,即System.Data.SQLite。这个东西可以从以下链接找到下载地址:http://sqlite.phxsoftware.com/,真正的下载地址在http://sourceforge.net/projects/sqlite-dotnet2/files/

下载完成之后,可以安装,然后就可以在项目中引用System.Data.SQLite并利用设计器创建针对SQLite数据库的EDM。创建和提取SSDL的内容与针对SQL Server的完全一样,不再赘述,由此,我们可以得到一个针对SQLite的SSDL文件,其中Schema元素的Provider属性值是System.Data.SQLite,而ProviderManifestToken属性是ISO8601。

  这里还需要指出的一点是,后来在测试的时候,发现运行失败,原因是,在SSDL中存在不同的表/实体类型中引用和被引用的字段(外键字段)Type属性不一致的情况,比如假设Company类型的CompanyID字段在Company类型中的type是integer,但是在引用它的Employee类型中的CompanyID字段的type是int,在这里,比如把它们2个统一为int或integer才可以。

4、通过修改连接字符串来切换不同的SSDL

  在我的项目中,我将这2个SSDL分别作为2个不同的程序集的资源内嵌到程序集中。然后通过修改连接字符串来切换到某个SSDL。连接字符串看起来是这样的:

metadata=res://GE/GTDP.GTDEDM.csdl|res://LiteTDP/LiteTDP.GTDEDM.ssdl|res://GE/GTDP.GTDEDM.msl;provider=System.Data.SQLite;provider connection string=&quot;Data Source=D:\Practice\SQLite\Ricky.db;&quot; 

注意连接字符串中对于CSDL、SSDL、MSL的指定,都指定到具体程序集的相关资源,其中黄色底色部分指定了使用SQLite的SSDL,而CSDL和MSL则直接使用了第二步中生成的EDMX中的内容。

 

补充:

  在第二步中生成的EDMX被完整的保留下来,这样,仍然可以随时利用设计器打开和操作。而针对SQLite生成的EDMX在提取需要的SSDL之后被我删掉了。当然了,是留是删,完全看需要,和,嗯,心情了。呵呵。

  很久没有写东西,都不会说话了,如果读起来别扭,请多包涵了。 

posted on 2010-02-22 10:20  零度的火  阅读(1029)  评论(2编辑  收藏  举报

导航