开发者社区> walb呀> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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

简介:
+关注继续查看

在前面几篇关于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)

本文转自博客园伍华聪的博客,原文链接:Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4),如需转载请自行联系原博主。



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Entity Framework 并发处理
原文 http://www.cnblogs.com/Gyoung/archive/2013/01/18/2866649.html 什么是并发? 并发分悲观并发和乐观并发。 悲观并发:比如有两个用户A,B,同时登录系统修改一个文档,如果A先进入修改,则系统会把该文档锁住,B就没办法打开了,只有等A修改完,完全退出的时候B才能进入修改。
899 0
Entity Framework快速入门--一对零到一关系处理
很久不更新blog了,正好趁着端午节的空,把之前一段时间使用关于EF以及工作上经验总结一下。 此文将跟朋友们分享一下关于1对0..1实体模型一些使用包括基本的添加和查询的注意事项。 首先我们EDMX实体模型: 模型很简单,一个用户的实体,另外关联了一个用户信息的实体。
760 0
Entity Framework 4.0并发处理的步骤
新用到了实体框架,用到了并发,参考以下的图书资料 Entity Framework 4.0 Recipes A Problem-Solution Approach ISBN-13 (pbk): 978-1-4302-2703-8 ISBN-13 (electronic): 978-1-4302-2704-5 http://apress.
864 0
一起谈.NET技术,在Entity Framework 4中映射现有实体类(POCO)
  Entity Framework的全称是ADO.NET Entity Framework,是微软开发的基于ADO.NET的ORM(Object/Relational Mapping)框架。   Entity Framework的主要特点:   1. 支持多种数据库(Microsoft SQL Server, Oracle, and DB2);   2. 强劲的映射引擎,能很好地支持存储过程;   3. 提供Visual Studio集成工具,进行可视化操作;   4. 能够与ASP.NET, WPF, WCF, WCF Data Services进行很好的集成。
910 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
FLowFuzz A Framework
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载