Entity Framework 查漏补缺 (一)

明确EF建立的数据库和对象之间的关系

EF也是一种ORM技术框架, 将对象模型和关系型数据库的数据结构对应起来,开发人员不在利用sql去操作数据相关结构和数据。
以下是EF建立的数据库和对象之间关系


关系数据库

对象

数据库

DbContext类


DbContext中的DbSet<实体类名>

表间的关联

实体类之间的关联

字段

实体类的公有属性

单条数据

单个实体类的对象

约束(主键、外键默认值)

实体类中的特性

了解EDM( 实体数据模型)

EF使用概念模型、 映射和存储模型。三个模型来描述映射关系

  • 概念模型 (.csdl) ︰ 即为直接使用的对象类,并包含它们之间的关系。
  • 存储模型 (.ssdl) ︰ 数据库设计结构,包括表、 视图、 存储的过程和他们的关系和键。
  • 映射 (.msl) ︰ 包含将概念模型(对象类)映射到存储模型(关系数据库)的信息。

CodeFirst :实体结构发生变化,如何更新数据库结构?

  • 数据准备
public class Place
{
    [Key]
    public int PlaceID { get; set;}

    public string Provice { get; set; }

    public string City { get; set; }
    //导航属性
    public List<People> Population { get; set; }
}
public class People
{
    [Key]
    public int PeopleID{ get; set; }

    public string Name{ get; set; }

    public int Age{ get; set;}

    //外键,对应导航属性对象中的标识
    [ForeignKey("Place")]
    public int PlaceID{ get; set;}

    //导航属性
    public Place Place { get; set; }
}
  • 创建DbContext派生类
public class TestDB:DbContext
{
    public TestDB():base("name=Test") { }

    public DbSet<Place> Place { get; set; }

    public DbSet<People> People { get; set; }
}
  • 执行一次代码
class Program
{
    static void Main(string[] args)
    {
        using (var context = new TestDB())
        {
            context.Place.Add(new Place {
                PlaceID = 2,
                Provice="jiangsu",
                City="wuxi"
            });
            context.SaveChanges();

        }
    }
}

发现在数据库已经建好了相应的表,这时在people类增加Sex属性,随便增加一条数据执行代码,报了如下错,很显然EF并不会帮我们更新数据库结构:

有两种情况处理这种情况,手动和自动迁移方式去更新数据库

自动迁移

1、选择所在的项目,在VS的程序包管理控制台输入命令(Tools菜单中打开Package Manager Console):

enable-migrations –EnableAutomaticMigrations

2、执行完毕,项目目录中多出一个名为Migrations的文件夹,里面有个Configuration.cs文件,打开它看到如下代码:

internal sealed class Configuration : DbMigrationsConfiguration<EF1.Model.TestDB>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        ContextKey = "EF1.Model.TestDB";
    }

    protected override void Seed(EF1.Model.TestDB context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method
        //  to avoid creating duplicate seed data.
    }
}

ContextKey属性指定了要执行迁移的DbContext,以便多个DbContext共存迁移不会产生冲突。

在迁移过程成功后会执行Seed方法,可以利用这个方法添加一些初始化数据

在删除属性的时候,会迁移失败,提示操作会造成数据丢失,可以在构造函数中添加如下代码,忽略数据丢失。

public Configuration()
{
    AutomaticMigrationsEnabled = true;
    AutomaticMigrationDataLossAllowed = true;
    ContextKey = "EF1.Model.TestDB";
}

3.以上步骤已开启支持迁移,每次更新实体结构时,在项目执行前,需要在管理控制台输入以下命令先更新数据库

update-database

或是在DbContext构造函数中添加以下代码,每次将自动迁移至最新的数据库Schema

public class TestDB:DbContext
{
    public TestDB():base("name=Test") {
        //TestDB:dbContext;        //Test:连接字符串名称
        //迁移至最新版本
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<TestDB, Configuration>("Test"));
    }

    public DbSet<Place> Place { get; set; }

    public DbSet<People> People { get; set; }
}

手动迁移

两种迁移方式基本相同,只是相比自动迁移,手动迁移会记录每次更新的情况,允许回滚数据库到某个指定版本,适合于团队开发。

1.一样要先开启支持迁移,并吧生成的Configuration构造函数中AutomaticMigrationsEnabled置为false,表示不使用自动迁移。

2.每次更改实体结构或映射配置时,在程序包管理控制台运行以下代码

Add-Migration ChangeSet1

会自动在前面生成的Migrations文件下生成一个ChangeSet1迁移文件

类的内容就是对于我们所更改的实体结构或映射配置,EF迁移要执行的内容,以下就是我增加了一个Phone属性,运行Add-Migration ChangeSet1所生成的。此迁移文件后面可以用来回滚到某个版本

public partial class ChangeSet1 : DbMigration
{
    public override void Up()
    {
        AddColumn("dbo.People", "Phone", c => c.String());
    }

    public override void Down()
    {
        DropColumn("dbo.People", "Phone");
    }
}

3.成功迁移文件后,运行下面代码更新至最新的迁移文件对应的版本。

Update-Database

或是回滚到指定版本

Update-Database -TargetMigration ChangeSet1

多种方式的增删改查

基础知识

上下文是根据检测实体的EntityState枚举状态,来执行相应的增/删/改操作。

EntityState枚举状态如下:

  • Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态;
  • Unchanged:对象添加到上下文中后,还未被修改过;
  • Added:对象已添加到对象上下文,还没有调用SaveChanges() 方法;
  • Deleted:将对象从上下文中删除;
  • Modified:对象已更改,还没有调用SaveChanges() 方法;

DBcontext类中的方法:

  • Entry:将对象加入EF容器,并获取当前实体对象的状态管理对象。
  • Set<T>:获取实体相应的DbSet类对象,如context.Set<Student>().Attach(student);

DbSet类

  • Attach:是把一个已存在于数据库中,但没有被 dbContext 跟踪的对象,以EntityState.Unchanged 状态附加到 dbCotext 中,对于已有相同key的对象存在于EF Context的情况,如果这个已存在对象状态为Unchanged则不进行任何操作,否则将其状态更改为Unchanged。
  • Add:将一个已存在于数据库中的对象,以Added实体状态添加到EF Context中。
  • Remove:将一个已存在于EF Context中的对象标记为Deleted,当SaveChanges(已经存在于EF Context)时,这个对象对应的数据库条目被删除。
  • Find:按主键去获取一个实体,首先在EF Context中查找是否有被缓存过的实体,如果查找不到再去数据库查找,如果数据库中存在则缓存到EF Context并返回,否则返回null。
  • AsNoTracking:无跟踪查询,不受EFcontext管理,所以查询出来的数据不能做修改,对于只查询显示的功能,加上AsNoTracking可以提升效率。
  • 。。。。。

增加

基本方式

static void Main(string[] args)
{
    using (var context = new TestDB())
    {
            context.Place.Add(new Place
            {
                PlaceID = 8,
                Provice = "jiangsu",
                City = "wuxi",
            });
            context.SaveChanges();
    }
}

Entry方式

附:用attch附加到上下文保存无效,因为附加后的状态是unchange,需要使用ChangeObjectState方法更改实体状态,或是再次使用Entry获取状态管理对象更新

static void Main(string[] args)
{
    using (var context = new TestDB())
    {
        var obj = new Place()
        {
            PlaceID =9,
            Provice = "jiangsu",
            City = "wuxi",
        };
        //加入EF容器,并获取当前实体对象的状态管理对象
        var entity = context.Entry<Place>(obj);
        entity.State = System.Data.Entity.EntityState.Added;
        context.SaveChanges();
    }
}

更新

先查询后更新:

static void Main(string[] args)
{
    using (var context = new TestDB())
    {
        var query = context.Place.Where(p => p.PlaceID == 9).FirstOrDefault();
        query.City = "suzhou";
        context.SaveChanges();
    }
}

Entry

using (var context = new TestDB())
{
    var obj = new Place
    {
        PlaceID=9,
        City="changzhou"
    };
    //将obj加入到上下文,并去获取实体对象的状态管理对象
    var entity = context.Entry<Place>(obj);
    //置为未被修改过
    entity.State = System.Data.Entity.EntityState.Unchanged;
    //设置该对象的City属性为修改状态,同时 entity.State由Unchanged-> Modified 状态
    entity.Property("City").IsModified = true;
    context.SaveChanges();
}

Attach

using (var context = new TestDB())
{
    var obj = new Place
    {
        PlaceID=9
    };
    //将obj附加到上下文,此时实体状态为Unchanged
    var newobj=context.Place.Attach(obj);
    //这时City属性为修改状态,同时entity.State自动由Unchanged-> Modified 状态
    newobj.City = "yangzhou";
    context.SaveChanges();
}

删除

常规操作:先查询后删除

using (var context = new TestDB())
{
    var queryObj = context.Place.Where(q=>q.PlaceID==8).FirstOrDefault();
    //当前的实体对象已置为Deleted状态
    context.Place.Remove(queryObj);
    context.SaveChanges();
}

Entry

using (var context = new TestDB())
{
    var obj = new Place
    {
        PlaceID = 6
    };
    //将obj附加到上下文,并获取实体对象的状态管理对象
    var entity =context.Entry<Place>(obj);
    //将状态置为Deleted
    entity.State = System.Data.Entity.EntityState.Deleted;
    context.SaveChanges();
}

Attach

using (var context = new TestDB())
{
    var obj = new Place
    {
        PlaceID = 7
    };
    //将obj附加到上下文,此时实体状态为Unchanged
    var newobj = context.Place.Attach(obj);
    context.Place.Remove(newobj);
    context.SaveChanges();
}

查询

对于简单查询,不需要进行修改操作,建议使用AsNoTracking,无跟踪查询来提升效率

using (var context = new TestDB())
{
    var obj = context.Place.Where(p => p.PlaceID == 9).AsNoTracking().ToList();
}

Find方法 :之前一直使用lambda查询(where,FirstOrDefault),有一个问题就是不管我们要查询的实体是否被dbconext缓存,都会去查询数据库,而使用Find通过主键来查询,会首先在EF Context中查找是否有被缓存过的实体,如果查找不到才去数据库查找

using (var context = new TestDB())
{
    var obj = context.Place.Find(9);
}

注:更详细的EF查询学习会在查漏补缺(二) 数据加载  中继续记录

原文地址:https://www.cnblogs.com/qiuguochao/p/10123743.html

时间: 2024-11-08 17:21:30

Entity Framework 查漏补缺 (一)的相关文章

Entity Framework 查漏补缺 (三)

Code First的数据库映射 有两种方式来实现数据库映射: 数据属性:Data Annotation 映射配置: Fluent API 有继承关系的实体如何映射? Code First在生成数据库表时,默认使用TPH方式 就是把父类和子类生成同一张表,额外增加了一列Discriminator字段,区分是父类或子类的数据类型 比如: 父类Book对象 public class Book { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Ide

Entity Framework 查漏补缺 (二)

数据加载 如下这样的一个lamda查询语句,不会立马去查询数据库,只有当需要用时去调用(如取某行,取某个字段.聚合),才会去操作数据库,EF中本身的查询方法返回的都是IQueryable接口. 链接:IEnumerable和IQueryable接口说明 其中聚合函数会影响数据加载,诸如:toList(),sum(),Count(),First()能使数据立即查询加载. IQueryable中的Load方法 一般情况,我们都是使用ToList或First来完成预先加载数据操作.但在EF中还可以使用

近来的java小总结(2.1):类的知识的查漏补缺

首先,我是一名新手,所以,要带着批判的眼光来看下面的文章   这篇文章说了些什么? 这文章是我近来8.6号来在编程思想上打的代码,从0~200页的源码接近到在这里,下文正是总结这0~200页的的知识,涉及到接口,内部类.初始化,数值计算的一些细节.此文章不会一下子写完,可能隔一天可能再补下来.因为代码确实有点多.. 注意 1 我的注释不一定正确(不过各小标题和代码一定是正确的,因为是书本上的原话,但是注释不一定正确),如果你确信我的内容的话,你可能会损失很大,因为我只是个菜鸟,我只是来补救一些知

查漏补缺——java多态

---恢复内容开始--- 刚学完java,开始了查漏补缺阶段阶段,为了巩固自己的知识和为别人提供一些微末的帮助决定开通博客,求各位大牛们指出我的不足,不要吝惜言语,也希望我的总结可以对别人有帮助,对自己对他人负责. 开始正文:术语多态:可以定义为“有多种形态”,多态引用是一个一个在不同时刻可以指向不同类型对象的引用变量.通过多态引用可以调用不同的具体的方法. 类方法的多态性的实现有两种方式: 1:方法重载:可以声明多个同名但是参数不同(个数.类型和顺序)的方法.注意呵呵重载方法只能声明在一个类里

近来的java小总结(2.2):类的知识的查漏补缺

1 首先,我是一名新手,所以,要带着批判的眼光来看下面的文章   这篇文章说了些什么? 这文章是我近来8.6号来在编程思想上打的代码,从0~200页的源码接近到在这里,下文正是总结这0~200页的的知识,涉及到接口,内部类.初始化,数值计算的一些细节.此文章不会一下子写完,可能隔一天可能再补下来.因为代码确实有点多.. 注意 1 我的注释不一定正确(不过各小标题和代码一定是正确的,因为是书本上的原话,但是注释不一定正确),如果你确信我的内容的话,你可能会损失很大,因为我只是个菜鸟,我只是来补救一

java知识查漏补缺

一.重写(override)和重载(overload)的区别 二者除了名字相似,其实没什么联系 范围不同:重写发生在同一个类的不同方法之间.重载发生在父类和子类自荐. 前提: 重写要求:方法名相同,参数列表不同,对于返回值类型不要求相同. 重载要求:方法名形同,参数列表也相同.重载是实现多态的关键,注意如果父类中的方法是private类型,那么子类中对应方法不算重载,而相当于是定义了一个新方法. 二.final的用法 修饰类:该类不能被继承 修饰方法:该方法不能被重写 修饰属性:该属性初始化后不

自家用的java小总结(2.4):类的知识的查漏补缺(内部类)

1 2      首先,这是一篇自己用的文章,不对读者承担任何责任,所以,要带着批判的眼光来看下面的文章   1 发现了,得加上输出结果,怕自己出错,~~   这篇文章说了些什么? 这文章是我近来8.6号来在编程思想上打的代码,从0~200页的源码接近到在这里,下文正是总结这0~200页的的知识,涉及到接口,内部类.初始化,数值计算的一些细节.此文章不会一下子写完,可能隔一天可能再补下来.因为代码确实有点多.. 注意 1 我的注释不一定正确(不过各小标题和代码一定是正确的,因为是书本上的原话,但

查漏补缺

查漏补缺[1].this语句:this语句用于构造函数之间进行相互调用.this语句只能定义在构造函数的第一行,因为初始化要先执行.[2].对象的初始化过程 Person p =new Person("hei",10); 1.因为new用到了Person.class,所以先找到Person.class文件加载到内存中 2.执行类中的静态代码块,如果有的话,给Person.class类进行初始化 3.在堆内存中开辟空间,分配内存地址 4.在堆内存中建立对象的特有属性,并进行默认初始化 5

《CSS权威指南》基础复习+查漏补缺

前几天被朋友问到几个CSS问题,讲道理么,接触CSS是从大一开始的,也算有3年半了,总是觉得自己对css算是熟悉的了.然而还是被几个问题弄的"一脸懵逼"... 然后又是刚入职新公司,事情不算多,于是拿起<CSS权威指南>进行"基础学习"+"查漏补缺",本篇文章主要是总结了些自己认为CSS中值的注意的几个知识点(本文知识点仅限本书范围内,若要讲CSS全部样式,那本兽还是选择慢慢懵逼去~). 选择器 这里要说明的是类选择器的嵌套选择与多类