在前面几篇关于Entity Framework 实体框架的介绍里面,已经逐步对整个框架进行了一步步的演化,以期达到统一、高效、可重用性等目的,本文继续探讨基于泛型的仓储模式实体框架方面的改进优化,使我们大家能够很好理解其中的奥秘,并能够达到通用的项目应用目的。本篇主要介绍实体数据模型 (EDM)的处理方面的内容。

1、实体数据模型 (EDM)的回顾

前面第一篇随笔,我在介绍EDMX文件的时候,已经介绍过实体数据模型 (EDM),由三个概念组成:概念模型由概念架构定义语言文件 (.csdl)来定义;映射由映射规范语言文件 (.msl);存储模型(又称逻辑模型)由存储架构定义语言文件 (.ssdl)来定义。

这三者合在一起就是EDM模式。EDM模式在项目中的表现形式就是扩展名为.edmx的文件。这个文件本质是一个xml文件,可以手工编辑此文件来自定义CSDL、MSL与SSDL这三部分。

CSDL定义了EDM或者说是整个程序的灵魂部分 – 概念模型。这个文件完全以程序语言的角度来定义模型的概念。即其中定义的实体、主键、属性、关联等都是对应于.NET Framework中的类型。

SSDL这个文件中描述了表、列、关系、主键及索引等数据库中存在的概念。

MSL这个文件即上面所述的CSDL与SSDL的对应,主要包括CSDL中属性与SSDL中列的对应。

2、EDMX文件的处理

我们在编译程序的时候,发现EDMX文件并没有生成在Debug目录里面,而EF框架本身是需要这些对象的映射关系的,那肯定就是这些XML文件已经通过嵌入文件的方式加入到程序集里面了,我们从数据库连接的字符串里面也可以看到端倪。

    <add name="sqlserver" connectionString="metadata=res://*/Model.sqlserver.csdl|res://*/Model.sqlserver.ssdl|res://*/Model.sqlserver.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=WinFramework;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />

我们看到,这里面提到了csdl、ssdl、msl的文件,而且这些是在资源文件的路径,我们通过反编译程序集可以看到,其实是确实存在这三个文件的。

但是我们并没有把edmx文件进行拆分啊,而且也没有把它进行文件的嵌入处理的啊?有点奇怪!

我们知道,一般这种操作可能是有针对性的自定义工具进行处理的,我们看看这个文件的属性进行了解下。

这个edmx文件的属性,已经包含了【自定义工具】,这个工具应该是生成对应的数据访问上下文类代码和实体类代码的了,那么生成操作不是编译或者内容,而是EntityDeploy是什么处理呢,我们通过搜索了解下。

EntityDeploy操作:一个用于部署 Entity Framework 项目的生成任务,这些项目是依据 .edmx 文件生成的。 可将这些项目作为资源嵌入,或将这些项目写入文件。

根据这句话,我们就不难解释,为什么编译后的程序集自动嵌入了三个csdl、ssdl、msl的xml文件了。

如果我们想自己构建相关的数据访问上下文类,以及实体类的代码生成(呵呵,我想用自己的代码生成工具统一生成,可以方便调整注释、命名、位置等内容),虽然可以调整T4、T5模板来做这些操作,不过我觉得那个模板语言还是太啰嗦和复杂了。

这样我把这个自定义工具【EntityModelCodeGenerator】置为空,也就是我想用自己的类定义格式,自己的生成方式去处理。当置为空的时候,我们可以看到它自动生成的类代码删除了,呵呵,这样就挺好。

3、EF框架的多数据库支持

在前面的例子里面,我们都是以默认SqlServer数据库为例进行介绍EDMX文件,这个文件是映射的XML文件,因此对于不同的数据库,他们之间的映射内容是有所不同的,我们可以看看SqlServer的edmx文件内容(以TB_City表为例)。

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx"><!-- EF Runtime content --><edmx:Runtime><!-- SSDL content --><edmx:StorageModels><Schema Namespace="WinFrameworkModel.Store" Provider="System.Data.SqlClient" ProviderManifestToken="2005" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl"><EntityType Name="TB_City"><Key><PropertyRef Name="ID" /></Key><Property Name="ID" Type="bigint" StoreGeneratedPattern="Identity" Nullable="false" /><Property Name="CityName" Type="nvarchar" MaxLength="50" /><Property Name="ZipCode" Type="nvarchar" MaxLength="50" /><Property Name="ProvinceID" Type="bigint" /></EntityType><EntityContainer Name="WinFrameworkModelStoreContainer"><EntitySet Name="TB_City" EntityType="Self.TB_City" Schema="dbo" store:Type="Tables" /></EntityContainer></Schema></edmx:StorageModels>

<!-- CSDL content --><edmx:ConceptualModels><Schema Namespace="EntityModel" Alias="Self" annotation:UseStrongSpatialTypes="false" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm"><EntityType Name="City"><Key><PropertyRef Name="ID" /></Key><Property Name="ID" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" /><Property Name="CityName" Type="String" MaxLength="50" FixedLength="false" Unicode="true" /><Property Name="ZipCode" Type="String" MaxLength="50" FixedLength="false" Unicode="true" /><Property Name="ProvinceID" Type="Int32" /></EntityType><EntityContainer Name="SqlEntity" annotation:LazyLoadingEnabled="true"><EntitySet Name="City" EntityType="EntityModel.City" /></EntityContainer></Schema></edmx:ConceptualModels>

<!-- C-S mapping content --><edmx:Mappings><Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2009/11/mapping/cs"><EntityContainerMapping StorageEntityContainer="WinFrameworkModelStoreContainer" CdmEntityContainer="SqlEntity"><EntitySetMapping Name="City"><EntityTypeMapping TypeName="EntityModel.City"><MappingFragment StoreEntitySet="TB_City"><ScalarProperty Name="ID" ColumnName="ID" /><ScalarProperty Name="CityName" ColumnName="CityName" /><ScalarProperty Name="ZipCode" ColumnName="ZipCode" /><ScalarProperty Name="ProvinceID" ColumnName="ProvinceID" /></MappingFragment></EntityTypeMapping></EntitySetMapping></EntityContainerMapping></Mapping></edmx:Mappings></edmx:Runtime>.........其他内容</Designer>
</edmx:Edmx>

而对MySql而言,它的映射关系也和这个类似,主要是SSDL部分的不同,因为具体是和数据库相关的内容。下面是Mysql的SSDL部分的内容,从下面XML内容可以看到,里面的数据库字段类型有所不同。

<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx"><!-- EF Runtime content --><edmx:Runtime><!-- SSDL content --><edmx:StorageModels><Schema Namespace="testModel.Store" Provider="MySql.Data.MySqlClient" ProviderManifestToken="5.5" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl"><EntityType Name="tb_city"><Key><PropertyRef Name="ID" /></Key><Property Name="ID" Type="int" Nullable="false" /><Property Name="CityName" Type="varchar" MaxLength="50" /><Property Name="ZipCode" Type="varchar" MaxLength="50" /><Property Name="ProvinceID" Type="int" /></EntityType><EntityContainer Name="testModelStoreContainer"><EntitySet Name="tb_city" EntityType="Self.tb_city" Schema="test" store:Type="Tables" /></EntityContainer></Schema></edmx:StorageModels>

从以上的对比,我们可以考虑,以一个文件为蓝本,然后在代码生成工具里面,根据不同的数据类型,映射成不同的XML文件,从而生成不同的EDMX文件即可,实体类和数据访问上下文的类,可以是通用的,这个一点也不影响概念模型的XML内容了,所有部分变化的就是SSDL数据存储部分的映射XML内容。

为了测试验证,我增加了Mysql、Oracle共三个的EDMX文件,并且通过不同的配置来实现不同数据库的访问调用。

我们知道,数据上下文的类构建的时候,好像默认是指向具体的配置连接的,如下代码所示(注意红色部分)。

    /// <summary>/// 数据操作上下文/// </summary>public partial class DbEntities : DbContext{//默认的构造函数public DbEntities()  : base("name=DbEntities"){           }protected override void OnModelCreating(DbModelBuilder modelBuilder){throw new UnintentionalCodeFirstException();}public virtual DbSet<City> City { get; set; }public virtual DbSet<Province> Province { get; set; }public virtual DbSet<DictType> DictType { get; set; }}

如果我们需要配置而不是通过代码硬编码方式,那么是否可以呢?否则硬编码的方式,一次只能是指定一个特定的数据库,也就是没有多数据库的配置的灵活性了。

找了很久,发现真的还是有这样人提出这样的问题,根据他们的解决思路,修改代码如下所示,从而实现了配置的动态性。

    /// <summary>/// 数据操作上下文/// </summary>public partial class DbEntities : DbContext{//默认的构造函数//public DbEntities()  : base("name=DbEntities")//{           //}/// <summary>/// 动态的构造函数/// </summary>public DbEntities() : base(nameOrConnectionString: ConnectionString()){           }    /// <summary>/// 通过代码方式,获取连接字符串的名称返回。/// </summary>/// <returns></returns>private static string ConnectionString(){//根据不同的数据库类型,构造相应的连接字符串名称AppConfig config = new AppConfig();string dbType = config.AppConfigGet("ComponentDbType");if (string.IsNullOrEmpty(dbType)){dbType = "sqlserver";}return string.Format("name={0}", dbType.ToLower());}protected override void OnModelCreating(DbModelBuilder modelBuilder){throw new UnintentionalCodeFirstException();}public virtual DbSet<City> City { get; set; }public virtual DbSet<Province> Province { get; set; }public virtual DbSet<DictType> DictType { get; set; }}

我通过在配置文件里面,指定ComponentDbType配置项指向那个连接字符串就可以了。

<configuration><configSections><!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --><section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /><!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup><entityFramework><defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"><parameters><parameter value="mssqllocaldb" /></parameters></defaultConnectionFactory><providers><provider invariantName="Oracle.ManagedDataAccess.Client" type="Oracle.ManagedDataAccess.EntityFramework.EFOracleProviderServices, Oracle.ManagedDataAccess.EntityFramework" /><provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6"></provider><provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /></providers></entityFramework><connectionStrings><add name="oracle" connectionString="metadata=res://*/Model.oracle.csdl|res://*/Model.oracle.ssdl|res://*/Model.oracle.msl;provider=Oracle.ManagedDataAccess.Client;provider connection string=&quot;DATA SOURCE=ORCL;DBA PRIVILEGE=SYSDBA;PASSWORD=whc;PERSIST SECURITY INFO=True;USER ID=WHC&quot;" providerName="System.Data.EntityClient" /><add name="sqlserver" connectionString="metadata=res://*/Model.sqlserver.csdl|res://*/Model.sqlserver.ssdl|res://*/Model.sqlserver.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=WinFramework;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" /><add name="mysql" connectionString="metadata=res://*/Model.mysql.csdl|res://*/Model.mysql.ssdl|res://*/Model.mysql.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;user id=root;password=root;persistsecurityinfo=True;database=test&quot;" providerName="System.Data.EntityClient" /></connectionStrings><appSettings><add key="ComponentDbType" value="mysql" /></appSettings>

OK,这样就很好解决了,支持多数据库的问题了。

4、框架分层结构的提炼

我们在整个业务部分的项目里面,把一些通用的内容可以抽取到一个Common目录层(如BaseBLL/BaseDAL等类或接口),这样我们在BLL、DAL、IDAL、Entity目录层,就只剩下一些和具体表相关的对象或者接口了,这样的结构我们可能看起来会清晰一些,具体如下所示。

但是这样虽然比原先清晰了一些,不过我们如果对基类接口进行调整的话,每个项目都可能导致不一样了,我想把它们这些通用的基类内容抽取到一个独立的公用模块里面(暂定为WHC.Framework.EF项目),这样我在所有项目里面引用他就可以了,这个做法和我在Enterprise Library框架的做法一致,这样可以减少每个项目都维护公用的部分内容,提高代码的重用性。

基于这个原则,我们重新设计了项目的分层关系,如下所示。

 

这样我们既可以减少主体项目的类数量,也可以重用公用模块的基类内容,达到更好的维护、使用的统一化处理。

这个系列文章索引如下:

Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)

Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)

Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)

Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)

转载于:https://www.cnblogs.com/wuhuacong/p/4338982.html

Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)相关推荐

  1. Entity Framework 实体框架的形成之旅--实体框架的开发的几个经验总结

    在前阵子,我对实体框架进行了一定的研究,然后把整个学习的过程开了一个系列,以逐步深入的方式解读实体框架的相关技术,期间每每碰到一些新的问题需要潜入研究.本文继续前面的主题介绍,着重从整体性的来总结一下 ...

  2. Entity Framework 实体框架的形成之旅--为基础类库接口增加单元测试,对基类接口进行正确性校验(10)...

    本篇介绍Entity Framework 实体框架的文章已经到了第十篇了,对实体框架的各个分层以及基类的封装管理,已经臻于完善,为了方便对基类接口的正确性校验,以及方便对以后完善或扩展接口进行回归测试 ...

  3. Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)

    在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...

  4. Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)

    很久没有写博客了,一些读者也经常问问一些问题,不过最近我确实也很忙,除了处理日常工作外,平常主要的时间也花在了继续研究微软的实体框架(EntityFramework)方面了.这个实体框架加入了很多特性 ...

  5. ADO.NET Entity Framework建模和映射(实体框架)

    在实体框架中,可以采用最适合您应用程序的方式定义概念模型.存储模型以及这两种模型之间的映射.使用 Visual Studio 中的实体数据模型工具,可以从数据库或图形模型创建一个 . edmx 文件, ...

  6. ADO.NET Entity Framework如何:使用实体数据模型向导(实体框架)

    本主题演示如何使用实体数据模型向导来生成 AdventureWorks 销售 .edmx 文件以及将 Visual Studio 项目配置为使用实体框架. 此模型和配置将在任务相关的各个实体框架主题中 ...

  7. LINQ TO SQL和Entity Framework 的关系 你了解多少?

    1. LINQ  TO SQL 和EF 特点:  LINQ TO SQL和Entity Framework都是一种包含LINQ功能的ORM 也就是所谓的关系对象的映射.其中包括的有DBFrist  C ...

  8. Entity Framework 学习总结之三:架构组成介绍

    为什么需要建立另外一种数据模型? 那么为什么需要建立另外一种模型呢?随着公司数据处理量的增加,理顺数据关系并基于这些数据来开发应用程序变得非常困难.数据库架构的设计需要考虑存储问题(如数据完整性.性能 ...

  9. Entity Framework Profiler 6.0加快您的应用程序

    Entity Framework Profiler 加快您的应用程序! Entity Framework Profiler 是一个实时可视化调试器,允许开发团队获得有价值的洞察力和视角来了解他们对 E ...

最新文章

  1. 数据库事务转载基础二:MySQL事务隔离级别详解
  2. uwsgi指定python路径_uwsgi-安装实操
  3. mysql inodb主键bug_MySQL的这个bug,坑了多少人?
  4. Linux内存page,Linux虚拟内存管理 - Page Table的作用
  5. tomcat7 java_java开发环境配置(windows下JDK7+tomcat7)
  6. [转载] python flask实现分页
  7. 新建一个grub软盘镜像
  8. 5.2 imnoise函数
  9. electron 软件 出现进程 XXX 可能无法关闭 解决方法
  10. Selenium获取动态图片验证码
  11. SQL Server 索引 之 书签查找 第十一篇
  12. C语言基础犄角旮旯的知识之数据类型
  13. 区块链+边缘计算应用研究与探讨
  14. [angular1.6]Error: transition superseded ui-router 在angular1.6 报错误问题解决
  15. 根据ebp/rbp/fp获取backtrace
  16. 个人奋斗动力源的一些感想
  17. 用大数据思考用户体验 纪学锋谈《江湖》特色
  18. 别让假装努力毁了你,最强的68道软件测试基础问答题你能答的溜嘛?
  19. int、long、long long、unsigned int、_int64的取值范围(与不同位数的编译器有关)
  20. ESP8266 贝壳物联

热门文章

  1. mysql隐式转换造成索引失效的事故总结
  2. 防止IE6出现BUG的十种常见解决方法
  3. apachectl startssl启动apache自动运行输入密码
  4. 信息系统项目管理师复习第1小时
  5. paramiko基础
  6. Django_ORM数据表查询总结
  7. linux ---jenkins的安装与配置
  8. Atitit.论垃圾文件的识别与清理 文档类型垃圾文件 与api概要设计pa6.doc
  9. ***S 2012 交互式报表 -- 钻取式报表
  10. 【linux排错】error while loading shared libraries: xxx.so.x 错误的原因和解决办法