zoukankan      html  css  js  c++  java
  • 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查询学习会在查漏补缺(二) 数据加载  中继续记录

  • 相关阅读:
    园 首页 新随笔 联系 管理 订阅 订阅 RTSP协议转换RTMP直播协议
    sequence diagram
    Model Binding
    asp.net mvc
    系统日志和异常的处理①
    随机森林之oob error 估计
    Extjs相关知识点梳理
    Extjs报错处理
    webbrowser在html中写入内容并添加js
    tcpdump一个命令的剖析
  • 原文地址:https://www.cnblogs.com/qiuguochao/p/10123743.html
Copyright © 2011-2022 走看看