前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Entity Framework 基础知识走马观花

Entity Framework 基础知识走马观花

作者头像
Edison Zhou
发布于 2018-08-20 07:47:17
发布于 2018-08-20 07:47:17
1.4K00
代码可运行
举报
文章被收录于专栏:EdisonTalkEdisonTalk
运行总次数:0
代码可运行

一、EF中的edmx文件

1.1 emdx文件本质:一个XML文件

  (1)通过选择以XML方式打开edmx文件,我们可以可以清楚地看到,edmx模型文件本质就是一个XML文件;

  (2)可以清楚地看到,edmx模型文件是一个XML文件,其中定义了三大组成部分,这三大组成部分构成了所谓的ORM(对象关系映射);

  (3)再通过解决方案管理器分析edmx模型文件,其包含了三个子文件:

  ①第一个是xxx.Context.tt,这个首先是一个T4的模板文件,它生成了我们这个模型的上下文类;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public partial class LearnEntities : DbContext
    {
        public LearnEntities()
            : base("name=LearnEntities")
        {
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }
    
        public DbSet<T_Class> T_Class { get; set; }
        public DbSet<T_Message> T_Message { get; set; }
        public DbSet<T_Person> T_Person { get; set; }
    }

  ②第二个是设计器部分,它定义了模型关系图的元数据,比如每个类图的宽度多少,在图中的坐标(X、Y轴)等;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<edmx:Diagrams>
      <Diagram DiagramId="edad56670ede49c5ae2e343c9da730f1" Name="关系图1">
        <EntityTypeShape EntityType="LearnModel.T_Class" Width="1.5" PointX="0.75" PointY="1.75" />
        <EntityTypeShape EntityType="LearnModel.T_Message" Width="1.5" PointX="5.25" PointY="1" />
        <EntityTypeShape EntityType="LearnModel.T_Person" Width="1.5" PointX="3" PointY="1.25" />
        <AssociationConnector Association="LearnModel.FK_T_Person_T_Class" />
        <AssociationConnector Association="LearnModel.FK_T_Message_T_Person1" />
        <AssociationConnector Association="LearnModel.FK_T_Message_T_Person2" />
      </Diagram>
</edmx:Diagrams>

  ③第三个就是数据库表中所对应的实体类对象,它也是一个T4模板文件,对应了所有选择的数据库表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public partial class T_Class
    {
        public T_Class()
        {
            this.T_Person = new HashSet<T_Person>();
        }
    
        public int Id { get; set; }
        public string Name { get; set; }
    
        public virtual ICollection<T_Person> T_Person { get; set; }
    }

1.2 emdx组成部分:SSDL、CSDL、C-S Mapping

  (1)SSDL

  它定义了数据库中所对应的表的定义,也可以称为存储模型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<edmx:StorageModels>
    <Schema Namespace="LearnModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
        <EntityContainer Name="LearnModelStoreContainer">
          <EntitySet Name="T_Class" EntityType="LearnModel.Store.T_Class" store:Type="Tables" Schema="dbo" />
          <EntitySet Name="T_Message" EntityType="LearnModel.Store.T_Message" store:Type="Tables" Schema="dbo" />
          <EntitySet Name="T_Person" EntityType="LearnModel.Store.T_Person" store:Type="Tables" Schema="dbo" />
          <AssociationSet Name="FK_T_Message_T_Person1" Association="LearnModel.Store.FK_T_Message_T_Person1">
            <End Role="T_Person" EntitySet="T_Person" />
            <End Role="T_Message" EntitySet="T_Message" />
          </AssociationSet>
          <AssociationSet Name="FK_T_Message_T_Person2" Association="LearnModel.Store.FK_T_Message_T_Person2">
            <End Role="T_Person" EntitySet="T_Person" />
            <End Role="T_Message" EntitySet="T_Message" />
          </AssociationSet>
          <AssociationSet Name="FK_T_Person_T_Class" Association="LearnModel.Store.FK_T_Person_T_Class">
            <End Role="T_Class" EntitySet="T_Class" />
            <End Role="T_Person" EntitySet="T_Person" />
          </AssociationSet>
        </EntityContainer>
        <EntityType Name="T_Class">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
          <Property Name="Name" Type="nvarchar" MaxLength="100" />
        </EntityType>
        <EntityType Name="T_Message">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
          <Property Name="Title" Type="nvarchar" Nullable="false" MaxLength="250" />
          <Property Name="Message" Type="nvarchar(max)" Nullable="false" />
          <Property Name="NickName" Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="IsAnonymous" Type="bit" Nullable="false" />
          <Property Name="IPAddress" Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="PostDate" Type="datetime" Nullable="false" />
          <Property Name="PostManId" Type="int" Nullable="false" />
          <Property Name="ReceiveManId" Type="int" Nullable="false" />
        </EntityType>
        <EntityType Name="T_Person">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
          <Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="Age" Type="int" Nullable="false" />
          <Property Name="Email" Type="nvarchar" Nullable="false" MaxLength="100" />
          <Property Name="ClassId" Type="int" Nullable="false" />
        </EntityType>
        <Association Name="FK_T_Message_T_Person1">
          <End Role="T_Person" Type="LearnModel.Store.T_Person" Multiplicity="1" />
          <End Role="T_Message" Type="LearnModel.Store.T_Message" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="T_Person">
              <PropertyRef Name="Id" />
            </Principal>
            <Dependent Role="T_Message">
              <PropertyRef Name="PostManId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
        <Association Name="FK_T_Message_T_Person2">
          <End Role="T_Person" Type="LearnModel.Store.T_Person" Multiplicity="1" />
          <End Role="T_Message" Type="LearnModel.Store.T_Message" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="T_Person">
              <PropertyRef Name="Id" />
            </Principal>
            <Dependent Role="T_Message">
              <PropertyRef Name="ReceiveManId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
        <Association Name="FK_T_Person_T_Class">
          <End Role="T_Class" Type="LearnModel.Store.T_Class" Multiplicity="1" />
          <End Role="T_Person" Type="LearnModel.Store.T_Person" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="T_Class">
              <PropertyRef Name="Id" />
            </Principal>
            <Dependent Role="T_Person">
              <PropertyRef Name="ClassId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
    </Schema>
</edmx:StorageModels>

  例如,我们可以通过下面的一个代码片段来看看它在说明什么?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<EntityType Name="T_Person">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
          <Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="Age" Type="int" Nullable="false" />
          <Property Name="Email" Type="nvarchar" Nullable="false" MaxLength="100" />
          <Property Name="ClassId" Type="int" Nullable="false" />
</EntityType>

  是不是跟我们在MSSQL中所进行的数据表设计差不多?指定主键、指定字段的类型、是否为NULL,最大长度等等;

  (2)CSDL

  它定义了EF模型中与SSDL对应的实体类对象的定义,这里C代表Concept,即概念模型;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<edmx:ConceptualModels>
      <Schema Namespace="LearnModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
        <EntityContainer Name="LearnEntities" annotation:LazyLoadingEnabled="true">
          <EntitySet Name="T_Class" EntityType="LearnModel.T_Class" />
          <EntitySet Name="T_Message" EntityType="LearnModel.T_Message" />
          <EntitySet Name="T_Person" EntityType="LearnModel.T_Person" />
          <AssociationSet Name="FK_T_Person_T_Class" Association="LearnModel.FK_T_Person_T_Class">
            <End Role="T_Class" EntitySet="T_Class" />
            <End Role="T_Person" EntitySet="T_Person" />
          </AssociationSet>
          <AssociationSet Name="FK_T_Message_T_Person1" Association="LearnModel.FK_T_Message_T_Person1">
            <End Role="T_Person" EntitySet="T_Person" />
            <End Role="T_Message" EntitySet="T_Message" />
          </AssociationSet>
          <AssociationSet Name="FK_T_Message_T_Person2" Association="LearnModel.FK_T_Message_T_Person2">
            <End Role="T_Person" EntitySet="T_Person" />
            <End Role="T_Message" EntitySet="T_Message" />
          </AssociationSet>
        </EntityContainer>
        <EntityType Name="T_Class">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
          <Property Type="String" Name="Name" MaxLength="100" FixedLength="false" Unicode="true" />
          <NavigationProperty Name="T_Person" Relationship="LearnModel.FK_T_Person_T_Class" FromRole="T_Class" ToRole="T_Person" />
        </EntityType>
        <EntityType Name="T_Message">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
          <Property Type="String" Name="Title" Nullable="false" MaxLength="250" FixedLength="false" Unicode="true" />
          <Property Type="String" Name="Message" Nullable="false" MaxLength="Max" FixedLength="false" Unicode="true" />
          <Property Type="String" Name="NickName" Nullable="false" MaxLength="50" FixedLength="false" Unicode="true" />
          <Property Type="Boolean" Name="IsAnonymous" Nullable="false" />
          <Property Type="String" Name="IPAddress" Nullable="false" MaxLength="50" FixedLength="false" Unicode="true" />
          <Property Type="DateTime" Name="PostDate" Nullable="false" Precision="3" />
          <Property Type="Int32" Name="PostManId" Nullable="false" />
          <Property Type="Int32" Name="ReceiveManId" Nullable="false" />
          <NavigationProperty Name="T_Person" Relationship="LearnModel.FK_T_Message_T_Person1" FromRole="T_Message" ToRole="T_Person" />
          <NavigationProperty Name="T_Person1" Relationship="LearnModel.FK_T_Message_T_Person2" FromRole="T_Message" ToRole="T_Person" />
        </EntityType>
        <EntityType Name="T_Person">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
          <Property Type="String" Name="Name" Nullable="false" MaxLength="50" FixedLength="false" Unicode="true" />
          <Property Type="Int32" Name="Age" Nullable="false" />
          <Property Type="String" Name="Email" Nullable="false" MaxLength="100" FixedLength="false" Unicode="true" />
          <Property Type="Int32" Name="ClassId" Nullable="false" />
          <NavigationProperty Name="T_Class" Relationship="LearnModel.FK_T_Person_T_Class" FromRole="T_Person" ToRole="T_Class" />
          <NavigationProperty Name="T_Message" Relationship="LearnModel.FK_T_Message_T_Person1" FromRole="T_Person" ToRole="T_Message" />
          <NavigationProperty Name="T_Message1" Relationship="LearnModel.FK_T_Message_T_Person2" FromRole="T_Person" ToRole="T_Message" />
        </EntityType>
        <Association Name="FK_T_Person_T_Class">
          <End Type="LearnModel.T_Class" Role="T_Class" Multiplicity="1" />
          <End Type="LearnModel.T_Person" Role="T_Person" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="T_Class">
              <PropertyRef Name="Id" />
            </Principal>
            <Dependent Role="T_Person">
              <PropertyRef Name="ClassId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
        <Association Name="FK_T_Message_T_Person1">
          <End Type="LearnModel.T_Person" Role="T_Person" Multiplicity="1" />
          <End Type="LearnModel.T_Message" Role="T_Message" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="T_Person">
              <PropertyRef Name="Id" />
            </Principal>
            <Dependent Role="T_Message">
              <PropertyRef Name="PostManId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
        <Association Name="FK_T_Message_T_Person2">
          <End Type="LearnModel.T_Person" Role="T_Person" Multiplicity="1" />
          <End Type="LearnModel.T_Message" Role="T_Message" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="T_Person">
              <PropertyRef Name="Id" />
            </Principal>
            <Dependent Role="T_Message">
              <PropertyRef Name="ReceiveManId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
    </Schema>
</edmx:ConceptualModels>

  当然,我们再通过一个代码片段来看看在概念模型中是如何定义的?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<EntityType Name="T_Person">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
          <Property Type="String" Name="Name" Nullable="false" MaxLength="50" FixedLength="false" Unicode="true" />
          <Property Type="Int32" Name="Age" Nullable="false" />
          <Property Type="String" Name="Email" Nullable="false" MaxLength="100" FixedLength="false" Unicode="true" />
          <Property Type="Int32" Name="ClassId" Nullable="false" />
          <NavigationProperty Name="T_Class" Relationship="LearnModel.FK_T_Person_T_Class" FromRole="T_Person" ToRole="T_Class" />
          <NavigationProperty Name="T_Message" Relationship="LearnModel.FK_T_Message_T_Person1" FromRole="T_Person" ToRole="T_Message" />
          <NavigationProperty Name="T_Message1" Relationship="LearnModel.FK_T_Message_T_Person2" FromRole="T_Person" ToRole="T_Message" />
</EntityType>

  在CSDL中,大部分都与SSDL中所对应的一致,但是我们发现多了一些属性。例如:NavigationProperty 导航属性,因为T_Person表与T_Class、T_Message表都存在一对一或一对多的关系(即存在外键),因此在EF模型所生成的对象实体中,加入了外键所在实体的导航属性。

  (3)C-S Mapping

  它是一个映射关系,它将SSDL与CSDL对应了起来,因此我们在用EF操作实体类时才可以正确地生成对相应数据表的SQL语句。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    <EntitySetMapping Name="T_Person">
            <EntityTypeMapping TypeName="LearnModel.T_Person">
              <MappingFragment StoreEntitySet="T_Person">
                <ScalarProperty Name="ClassId" ColumnName="ClassId" />
                <ScalarProperty Name="Email" ColumnName="Email" />
                <ScalarProperty Name="Age" ColumnName="Age" />
                <ScalarProperty Name="Name" ColumnName="Name" />
                <ScalarProperty Name="Id" ColumnName="Id" />
              </MappingFragment>
            </EntityTypeMapping>
          </EntitySetMapping>
     </EntityContainerMapping>

  可以看出,这里将SSDL中的T_Person与CSDL中的LearnModel.T_Person对应了起来,当然还将他们各自的属性进行了一一对应。

二、EF中的代理类对象

2.1 代理模式初探

  通过上面的图片,我们可以看到,通过增加代理类B来解耦A与C之间的调用,这样可以封装原来C调用A的一些相关细节,转换成C直接调用B中封装后的代理方法,则等同于访问A。在实际应用中,例如对于WebService的远程调用时,如果我们使用添加Web引用的方式,那么WebService会为我们自动生成代理类,我们所有的交互都只是和代理类进行的,而没有直接和服务提供者进行。

代理模式:可以看看园友程兴亮的一篇文章《极速理解设计模式之代理模式

2.2 EF中的代理应用

  (1)我们首先有下面这样一段代码,它要进行的是一个简单的修改操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        static void Edit()
        {
            // 下面返回的是一个Person类的代理类对象
            T_Person person = db.T_Person.FirstOrDefault(p => p.Id == 11);
            Console.WriteLine("Before update:{0}", person.ToString());
            // 此时操作的也只是Person类的代理类对象,同时标记此属性为已修改
            person.Name = "周旭龙";
            person.Email = "edisonchou7@cuit.edu.cn";
            person.Age = 25;
            // 此时EF上下文会检查容器内部所有的对象,
            // 找到标记为修改的对象属性生成对应的修改SQL语句
            db.SaveChanges();
            Console.WriteLine("Update Successfully~~");
        }

  (2)通过分析这段代码,我们知道要进行一个修改操作,需要经过三个步凑:第一是查询到要修改的那一行,然后进行修改操作赋值,最后提交修改操作。(当然,这是官方推荐的修改操作步凑,你也可以不经过查询而直接修改,这需要利用到DbEntityEntry)那么,为什么要经过这几个步凑呢?

  ①我们首先来看看第一步:查询 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  db.T_Person.FirstOrDefault(p => p.Id == 11);

  在实际开发中,我们的应用程序不会直接和数据库打交道,而是和EF数据上下文中的代理类打交道。首先,通过查询操作数据库返回了一行数据,EF上下文将其接收并将其“包装”起来,于是就有了代理类。在代理类中,真实的实体类对象被封装了起来,并且在代理类中为每个属性都设置了一个标志,用来标识其状态(是否被修改)。而我们在程序中所获得的数据,都是从代理类中返回的。

  ②再来看看第二步:修改

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  person.Name = "周旭龙";

  当执行完修改操作之后,代理类中对应字段的标志会被修改,例如我们这里修改了Name属性,那么其对应的标志就会由false变为true。虽然只是变了一个标志位,但是却对EF生成SQL语句产生了重大影响。如果我们只修改了一个属性,那么其生成的SQL语句只会有一个Update ** Set Name='***'。

  ③最后来看看第三步:提交  

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  db.SaveChanges();

  当SaveChanges方法触发时,EF上下文会遍历代理类对象中的状态标志,如果发现有修改的(即为True)则将其加入生成的SQL语句中。这里,因为Name被修改了,所以在生成的SQL语句中会将Name加入,而其他未修改的则不会加入。

  我们也可以通过SQLServer Profiler来查看EF所生成的SQL语句:

三、EF中的延迟加载与即时加载

3.1 浅谈延迟加载

所谓延迟加载,就是只有在我们需要数据的时候才去数据库读取加载它。

  在Queryable类中的扩展方法中,Where方法就是一个典型的延迟加载案例。在实际的开发中,我们往往会使用一些ORM框架例如EF去操作数据库,Where方法的使用则是每次调用都只是在后续生成SQL语句时增加一个查询条件,EF无法确定本次查询是否已经添加结束,所以没有办法木有办法在每个Where方法执行的时候确定最终的SQL语句,只能返回一个DbQuery对象,当使用到这个DbQuery对象的时候,才会根据所有条件生成最终的SQL语句去查询数据库。

  (1)针对条件的延迟加载

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    DbQuery<T_Person> query = db.T_Person.Where(p => p.Id == 11).OrderBy(p => p.Age) as DbQuery<T_Person>;
    // 用的时候才去加载:根据之前的条件生成SQL语句访问数据库
    T_Person person = query.FirstOrDefault();

  通过SQLServer Profiler调试跟踪,当执行完第一行代码时,是没有进行对数据库的查询操作的。而当执行到第二行的FirstOrDefault()方法时,EF才根据前面的条件生成了查询SQL语句去加载数据。

  (2)针对外键的延迟加载

  首先,我们有这样两张表,他们是1:N的关系;其中ClassId是T_Person的外键;

  其次,在EF所生成的实体对象中,在T_Person的代码中会有一个T_Class的对象属性;因为一个T_Person对应一个T_Class;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public partial class T_Person
    {
        public T_Person()
        {
            this.T_Message = new HashSet<T_Message>();
            this.T_Message1 = new HashSet<T_Message>();
        }
    
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Email { get; set; }
        public int ClassId { get; set; }
    
        public virtual T_Class T_Class { get; set; }
    }

  最后,在实际开发中,我们有如下一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    IQueryable<T_Person> dbQuery = db.T_Person.Where(p => p.ClassId == 1);
    T_Person person = dbQuery.FirstOrDefault();
    Console.WriteLine(person.T_Class.Name);

  通过调试我们可以发现,在执行最后一段代码输出Person对象所属班级的信息之前,都没有对Class进行查询。而在执行到最后一句时,才去数据库查询所对应的Class信息;

3.2 浅谈即时加载

所谓即时加载,就是在加载数据时就把该对象相关联的其它表的数据一起加载到内存对象中去。

  在Queryable类中的扩展方法中,ToList()方法就是一个典型的即时加载案例。与延迟加载相对应,在开发中如果使用ToList()方法,EF会根据方法中的条件自动生成SQL语句,然后立即与数据库进行交互获取查询结果,并加载到内存中去。

  (1)例如,我们有以下一段代码,在执行到第一句的ToList()方法时,EF就立即对数据库发起访问,并将结果记载到了内存中,最后将personList指向了这块记录在堆中的地址;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    List<T_Person> personList = db.T_Person.Where(p => p.ClassId == 1).ToList();
    personList.ForEach(p => Console.WriteLine(p.ToString()));

  (2)这时候,如果我们再想对查询到的结果进行排序,我们该怎么写?是不是写出了以下的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    List<T_Person> personList = db.T_Person.Where(p => p.ClassId == 1).ToList().OrderBy(p => p.Name).ToList();
    personList.ForEach(p => Console.WriteLine(p.ToString()));

  这时我们发现,当ToList()之后,OrderBy()方法就没有对SQL语句进行调整,也没有再对数据库发起请求了。因为,这里的OrderBy()方法是对内存中的数据进行的排序,而不是和前面的Where()方法一起拼接成SQL语句。

3.3 使用Include提高查询效率

  前面我们看到了延迟加载在EF中被广泛应用,但是延迟加载对于外键的加载也存在不足:那就是每次调用外键实体都会去查数据库。

  (1)我们来看看下面一段代码,它的作用是通过每个班级查询所对应的所有学生信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    IQueryable<T_Class> classQuery = db.T_Class;
    foreach (T_Class c in classQuery)
    {
          Console.WriteLine(c.Id + "-" + c.Name + ":");
          ICollection<T_Person> pList = c.T_Person;
          foreach (T_Person p in pList)
          {
               Console.WriteLine(p.Id + "-" + p.Name);
          }
     }

  其显示结果如下图所示:

  (2)通过SQLServer Profiler跟踪,可以发现,每次调用外键实体属性时都会对数据库发出一起查询请求,从下图也可以看出,总共发出了接近10个请求;

  (3)但是,EF也做了一个小优化:对于相同外键的加载请求,只会执行一次;例如,这里存在多个ClassId=1的Person记录,因此它们都只会执行一次即可;

  (4)虽然EF做了一些优化,但是有木有一种方法能够让我们只通过一次请求就获取所有的信息呢?在SQL语句中,我们可以通过一个超级简单的连接查询就可以实现,那么在EF中呢如何实现呢?还好,微软早就想到了这一点,为我们提供了一个Include方法。我们可以对上面的代码段进行修改,得到下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            IQueryable<T_Class> classQuery = db.T_Class.Include("T_Person");
            foreach (T_Class c in classQuery)
            {
                Console.WriteLine("班级:" + c.Id + "  " + c.Name + ":");
                ICollection<T_Person> pList = c.T_Person;
                foreach (T_Person p in pList)
                {
                    Console.WriteLine(p.Id + "-" + p.Name);
                }
            }

  这下程序在执行到Include方法时,便会与T_Person表进行一个连接查询,将连接查询到的T_Person部分数据存入T_Class的T_Person属性中,也就是都存入了内存中,后面再次访问外键实体只需要从内存中读取而不用再发出多个数据库查询请求了。

Include方法跟ToList方法一样,也是即时加载类型的一种具体方法,其本质是生成连接查询的SQL语句。从整体来看,通过Include将以空间换取效率,在某些具体的应用场合可以适当使用。

参考资料

(1)陈少鑫,《EF贪婪加载与延迟加载的选择和使用》:http://www.cnblogs.com/chenshao/p/4169210.html

(2)强子,《解析ASP.NET MVC开发方式之EF延迟加载》:http://www.cnblogs.com/qq731109249/p/3502874.html

(3)Liam Wang,《ASP.NET MVC小牛之路:使用EF》:http://www.cnblogs.com/willick/p/3304534.html

作者:周旭龙

出处:http://edisonchou.cnblogs.com/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-02-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
在Entity Framework中使用存储过程(一):实现存储过程的自动映射
之前给自己放了一个比较长的假期,在这期间基本上没怎么来园子逛。很多朋友的留言也没有一一回复,在这里先向大家道个歉。最近一段时间的工作任务是如何将ADO.NET Entity Framework 4.0(以下简称EF)引入到我们的开发框架,进行相应的封装、扩展,使之成为一个符合在特定场景下进行企业级快速开发的ORM。在此过程中遇到了一些挑战,也有一些心得。为了向大家分享这些心得,也为了借助大家的脑袋解决我们遇到的问题,接下来我会写一系列相关的文章。这些文章的读者适合那些对EF有基本了解的人。 第一个主题是关于
蒋金楠
2018/02/07
2.6K0
Entity Framework快速入门--实例篇 DatabaseFirst
在上一篇中我们简单了解的EF的定义和大体的情况,我们通过一步一步的做一个简单的实际例子来让大家对EF使用有个简单印象。好,废话少说,直入主题。
老马
2022/05/10
5010
Entity Framework快速入门--实例篇  DatabaseFirst
ADO.NET Entity Framework
作为下一代 ADO.NET 3.0 的开发框架,Entity Framework 让我们从复杂的关系数据模型中解脱出来,使用更加符合面向对象的实体数据模型(Entity Data Model)来完成业务设计和开发。 Entity Framework 很像 NHibernate,同样采取 XML 进行映射。不过借助于强大的可视化设计工具和向导,我们可以非常方便地完成这些头疼的工作。 Entity Framework  对 Linq 的直接支持 —— LINQ to Entities Entity Frame
张善友
2018/01/31
8770
ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多。主要集中增删该查每个模块都有,所以本次是为封装相同接口方法    如果你想了解怎么重构普通的接口DAL层请查看第二节点    如果你只想了解利用T4链接EF生成代码,可以忽略前两节,之后跳后最后T4模版的使用。   (代码在最后)    补充:   最后必须让初学者理解一个知识点:分部类 partial 关键字,因为我们的重构是围绕分部类而实现,包括接口 partial 关键字指示可在命名空间中定义该类、结构或接口的其他
用户1149182
2018/01/16
2K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
Hibernate【查询详解、连接池、逆向工程】
前言 在Hibernate的第二篇中只是简单地说了Hibernate的几种查询方式….到目前为止,我们都是使用一些简单的主键查询阿…使用HQL查询所有的数据….本博文主要讲解Hibernate的查询操
Java3y
2018/03/15
1.3K0
Hibernate【查询详解、连接池、逆向工程】
Entity Framework CodeFirst数据迁移
我们知道无论是“Database First”还是“Model First”当模型发生改变了都可以通过Visual Studio设计视图进行更新,那么对于Code First如何更新已有的模型呢?今天我们简单介绍一下Entity Framework的数据迁移功能。
aehyok
2018/09/11
8710
Entity Framework  CodeFirst数据迁移
ASP.NET MVC5+EF6+EasyUI 后台管理系统(59)-BLL层重构
前言:  这应该是本系统最后一次重构,将重构BLL层和Model层。来完全取代代码生成器生成的BLL层和DAL层。完全废掉了代码生成器的DAL,BLL,MODEL层。     全自动生成增,删,改,查的通用方法和模型转换与BLL层的模型事务脱离,后续文章,会以一些插件或功能为目的,继续完善,进行分享,最后60节的文章会对本系统做一个总结   (但是还没时间写,相信60节的文章能让你快速了解到本系统的优势和架构,就算你从未阅读之前的所有文章)    继上次的DAL层重构(上一节),本来只想重构DAL层算了,
用户1149182
2018/01/16
1.3K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(59)-BLL层重构
Entity Framework 约定
约定,类似于接口,是一个规范和规则,使用Code First 定义约定来配置模型和规则。在这里约定只是记本规则,我们可以通过Data Annotaion或者Fluent API来进一步配置模型。约定的形式有如下几种:
喵叔
2020/09/08
1.3K0
【SSH快速进阶】——Hibernate一对一映射(one-to-one)——主键关联映射
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/details/50219961
DannyHoo
2018/09/13
5950
【SSH快速进阶】——Hibernate一对一映射(one-to-one)——主键关联映射
【SSH快速进阶】——Hibernate一对一映射(one-to-one)——唯一外键关联
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/details/50253847
DannyHoo
2018/09/13
5390
【SSH快速进阶】——Hibernate一对一映射(one-to-one)——唯一外键关联
浅析Entity Framework Core中的并发处理
本文讲述如何使用Entity Framework Core来实现一个乐观并发控制,并介绍在.NET Core中使用DbContext进行数据库操作时的一些常见问题和解决方案。
GuZhenYin
2018/01/04
2.8K0
浅析Entity Framework Core中的并发处理
entity framework数据库映射(ORM)
Sql Server安装:EntityFramework Mysql安装:MySql.Data.Entity
sofu456
2022/05/06
9200
entity framework数据库映射(ORM)
走向面试之数据库基础:二、SQL进阶之case、子查询、分页、join与视图
  假设我们有一个论坛网站,其中有一张User表{ UId,Name,Level },Level是一个int类型,代表了用户等级类型,例如:1代表骨灰,2代表大虾等;我们就可以用CASE来对其进行等值判断了:
Edison Zhou
2018/08/20
7640
走向面试之数据库基础:二、SQL进阶之case、子查询、分页、join与视图
jdbc
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/dangdang jdbc.username=root jdbc.password=root
Dean0731
2020/05/08
1K0
【愚公系列】2023年03月 Java教学课程 118-Mybatis(多表操作)
多表模型是一种关系型数据库设计模式,它使用多个表格来存储和管理数据。在多表模型中,每个表格都包含一组相关的数据,并使用外键等关系来与其他表格建立连接。这种模型通常用于处理复杂的数据结构,例如具有多个关系的实体或需要动态添加或删除属性的实体。
愚公搬代码
2023/04/04
6310
【愚公系列】2023年03月 Java教学课程 118-Mybatis(多表操作)
【愚公系列】2023年02月 .NET/C#知识点-使用控制台手搭webapi框架
WebAPI是一种协议,用于允许网络应用程序(如浏览器)与网络服务器(如Web服务器)之间进行通信。它可以用于处理数据,访问数据库,处理图像和视频,以及进行其他高级功能。
愚公搬代码
2023/03/16
1.5K0
【愚公系列】2023年02月 .NET/C#知识点-使用控制台手搭webapi框架
在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?
在《实现存储过程的自动映射》中,我通过基于T4的代码生成实现了CUD存储过程的自动映射。由于映射的都是基于数据表结构的标准的存储过程,所以它们适合概念模型和存储模型结构相同的场景。如果两种模型存在差异,在进行数据更新操作的时候就会出错。本篇文章主要介绍当概念模型中具有继承关系的两个实体映射到数据库关联的两个表,如何使用存储过程。 目录 一、创建具有继承关系的实体 二、基于继承关系实体的查询与更新 三、映射标准的CUD存储过程 四、修正存储过程 一、创建
蒋金楠
2018/02/07
1.5K0
在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?
Entity Framework 简单查询
第一步还是先建立一个控制台的应用程序,然后通过Nuget添加Entity Framework。那么同时会给packages.config和App.config添加相应的配置。
aehyok
2018/09/11
8430
Entity Framework 简单查询
Entity Framework 一对多关系映射
EF中关系映射也是一个很关键的内容,关系映射和属性映射一样,也是在 OnModelCreating 中配置映射。EF中的关系映射有如下三种:
喵叔
2020/09/08
1K0
Entity Framework 一对多关系映射
在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?
继续讨论EF中使用存储过程的问题,这回着重讨论的是为存储过程的参数进行赋值的问题。说得更加具体一点,是如何为实体映射的Delete存储过程参数进行赋值的问题。关于文中涉及的这个问题,我个人觉得是EF一个有待改进的地方,不知道各位看官是否同意? 目录 一、EF存储过程参数赋值的版本策略 二、Delete存储参数就一定是Original值吗? 三、如果直接修改.edmx模型的XML呢? 四、为Delete存储过程参数赋上Current值,如何做得到? 一、
蒋金楠
2018/02/07
1.8K0
在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?
推荐阅读
相关推荐
在Entity Framework中使用存储过程(一):实现存储过程的自动映射
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验