热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

EF初识

什么是ORM起源随着编程的发展,程序里都是面向对象啥的,但是数据库发展呢网状数据库-》层次数据库-》关系数据库(当然还有nosql数据库我们只是做热数据缓存后面将会讲到)。关系型数
什么是ORM

起源随着编程的发展,程序里都是面向对象啥的,但是数据库发展呢  网状数据库 -》层次数据库 -》关系数据库(当然还有nosql数据库  我们只是做热数据缓存  后面将会讲到) 。关系型数据库一直流行到当今。

就出现了一个问题,程序里的发展和数据库的发展不匹配,一个面向对象 类什么,一个是行列结构    而且数据库的多样性,这种不协调就做阻抗失衡,所以就出现了ORM(Object Relational Mapping)。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据(.net中这种类叫做POCO类,没有任何业务逻辑,数据库类最好也用CLR中的数据类型  例如  system.int 这种的),将程序中的对象自动持久化(持久化就是保存)到关系数据库中。

EF三种模式

其实这个是随着 EF版本的发展以及ObjectContext到DBContex 的发展而发展的。

1、DBFirst

就是先把数据库设计好,然后用在VS项目中添加新建项,添加一个来自数据库的EF设计器  edmx。(edmx是一个组件,最后会生成一个dll,里面有很多类,类的生成是根据T4模板生成的。T4模板的底层使用的是codedom)

技术分享技术分享

上图中 选择模型内容,以前的版本中只有两个,后来变成四个。以前还有一个ef power tools,现在都用用不到了。本人认为上图中  来自数据库的Code First生成的代码使用的技术是codedom实现的。我是自己写的工具底层codedom,生成实体 以及一些其他数据操作类,业务操作类等。

EDMX其实就是一个xml文件,运行的时候被分为三个子文件,

*.csdl(conceptyal  shema definition)概念模型(conceptual model),就是实体类。

*.ssdl(storage schema definition language) 存储模型(storage model),底层存储操作类。

*.msl(mapping specification language) 概念-存储模型映射,概念模型于存储模型的映射。  

程序中EF第一使用会生成这个映射到缓存里。并且执行 protected override void OnModelCreating(DbModelBuilder modelBuilder)。这个方法也只会执行一次,以后再也不执行了。所以关于实体的配置一般都写在这个里面。能不写在构造函数里面的就不写在里面。但是禁用状态跟踪写在这个里没有效果(this.Configuration.AutoDetectChangesEnabled = false;)

所以大家说的第一次加载慢  就是因为这个操作,当然还有其他的原因。

技术分享技术分享
//EF Pre-Generated Mapping Views(预生成映射视图)
using (var dbcOntext= new CustomContext())
{
    var objectCOntext= ((IObjectContextAdapter)dbcontext).ObjectContext;
    var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
    //DataSpace.CSSpace   C 表示概念模型        S表示存储模型
    mappingCollection.GenerateViews(new List());
    //对程序中定义的所有DbContext逐一进行这个操作
}
这段代码一般放在项目入口问题,只执行一次。ASP.NET中放在Application_Start里面

 2、ModelFirst

就是先把模型edmx设计好,然后在通过edmx把模型跟新到数据库里面。

3、CodeFirst

没有了edmx,没有了乱七八糟的东西,都可以靠自己定制。更确切的说叫做code only

 总结的来说就是一个映射ORM过程。

ObjectContext-->DBContext

 老版本中的是ObjectContext,后来4.0后升级为DBContext

public class DbContext : IDisposable, IObjectContextAdapter

public interface IObjectContextAdapter
{
//
// 摘要:
// 获取对象上下文。
//
// 返回结果:
// 对象上下文。
ObjectContext ObjectContext { get; }
}

可以看出ObjectContext和DBContext中间多了一个适配器。DBContext中有加入了一些操作。

EF增删改

增加

先把实体类填充好,然后在context.DbSet.Add(.);

最后SaveChange(),一次性提交当前上下文中所有变更的实体。

技术分享技术分享
Class c1 = new Class();
c1.ClassAddress = "北京壹号四合院1";
c1.ClassName = "四合院名字1";
c1.ClassNum = 1;
Student st1 = new Student();
st1.StudentAddress = "学生地址1";
st1.StudentAge = 1;
st1.StudentName = "学生名字1";
st1.StudentSex = 1;
c1.Student.Add(st1);
Student st2 = new Student();
st2.StudentAddress = "学生地址2";
st2.StudentAge = 2;
st2.StudentName = "学生名字2";
st2.StudentSex = 2;
c1.Student.Add(st2);
context.Class.Add(c1);
var f = context.SaveChanges();
View Code

删除

删除操作也是先加载一个实体,然后删除实体。最后savechange。

(实体可以查询数据库得到,也可以通过指定实体主键,然后加载上下文中。DbSet.Attach)

技术分享技术分享
var stu = context.Student.FirstOrDefault();
if (stu != null)
{
    var remStu = context.Student.Remove(stu);
    var remFlag = context.SaveChanges();
    Console.WriteLine("删除成功studentID{0},删除数量{1}", stu.StudentID, remFlag);
}
View Code

修改

直接修改加载到上下文中的实体,然后修改,最后SaveChange.

技术分享技术分享
var stu = context.Student.FirstOrDefault();
if (stu != null)
{
    stu.StudentAddress = "我更新了一下学生信息,更新时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    stu.StudentAge = 9;
    var remFlag = context.SaveChanges();
    Console.WriteLine("更新成功studentID{0},更新数量{1}", stu.StudentID, remFlag);
}
View Code

事物

SaveChange会把上下文中所有的修改的实体生成SQL提交给数据库。

一个context中一个SaveChange就是一个事物。也可以在context中开启事物。

技术分享技术分享
using (var tr = context.Database.BeginTransaction().UnderlyingTransaction)
{
    try
    {
        string strCom = "INSERT INTO Book(BookName,BookPage,BookUser)VALUES(@BookName,@BookPage,@BookUser)";
        SqlParameter sp1 = new SqlParameter("@BookName", "我是通过实物提交上来的书名 保存失败测试");
        SqlParameter sp2 = new SqlParameter("@BookPage", 1);
        SqlParameter sp3 = new SqlParameter("@BookUser", "BookUser实物 保存失败测试");
        var fsql = context.Database.ExecuteSqlCommand(strCom, sp1, sp2, sp3);
        Class c1 = new Class();
        c1.ClassAddress = "北京壹号四合院实物1 保存失败测试";
        c1.ClassName = "四合院名字实物1";
        c1.ClassNum = 1;
        Student st1 = new Student();
        st1.StudentAddress = "学生地址实物1";
        st1.StudentAge = 1;
        st1.StudentName = "学生名字实物1";
        st1.StudentSex = 1;
        c1.Student.Add(st1);
        Student st2 = new Student();
        st2.StudentAddress = "学生地址实物2";
        st2.StudentAge = 2;
        st2.StudentName = "学生名字实物2";
        st2.StudentSex = 2;
        c1.Student.Add(st2);
        var eflag = context.Class.Add(c1);
        context.SaveChanges();

        //设置失败保存
        //context.Database.ExecuteSqlCommand("失败保存");
        tr.Commit();
        Console.WriteLine("保存成功{0},{1}", fsql, eflag);
    }
    catch (Exception e)
    {
        tr.Rollback();
        Console.WriteLine("保存失败:{0}", e.Message);
    }

}
View Code
EF查询

ObjectContext对象(可以不用学习这一部分)

技术分享

1、Entity SQL是一种类似SQL的查询语言(ESQL),NH中的是HSQL。Entity SQL查询对象是EDMX而不是数据库中的表。

select value c from 上下文名字.实体名 as c

value关键字希望你可以返回一个强制类型集合,如果没有这个关键字,那么返回的就是一个数据库中的二维表 table。

Entity SQL 调用 Object Services

2、LINQ TO Entities

通过linq查询ADO.NET实体数据模型。底层使用对象服务(object services)。他可以将查询结果转过为强类型的CLR对象。

3、EntityClient

不管使用EntitySQL还是Linq to entities最终都要依赖Entity Client完成工作。

我们可以在程序中使用entity client查询来存取数据,但是只能通过编写entity sql(entity client 类似ADO.NEt),性能可以改善,到那时不得不手动跟踪对象状态。所以这种方式建立使用批量读取且读完后不做数据修改的操作,

上面三种都适用于ObjectContex

4、直接执行SLQ

在ObjectContext中可以直接

objectContext.ExecuteStoreCommand
objectContext.ExecuteStoreQuery

在DBContext中

context.Database.ExecuteSqlCommand
context.Database.SqlQuery

5、使用linq和扩展方法+lambda

实际上linq to entities查询最终转化为扩张方法+lambda。然后在转化成数据库的SQL。个人觉得增删改用实体对象操作,查询直接执行SQL  存储过程操作,查询的也可以做读写分离。

DBContext对象

1、查询表达式和扩展方法+lambda(linq)

以前写的文章:C#-LINQ

本文对应的代码中也会有常用的查询例子(见最下方下载)。

这里没有涉及到表达式树的知识,讲在后面文章专门写到。东西实在太多了,都是基础。

特殊查询过滤函数:

find

include

2、直接执行SQL

context.Database.SqlQuery执行一个查询语句,返回的默认是一个列表,可以设置参数化查询。T 表示返回的值会自动转化为该类型的列表形式。

context.Database.ExecuteSqlCommand执行一个数据库语句,返回值是一个受影响的行数,可以设置为参数化查询。一般为增删改操作,但是也可以为select查询等其他操作。

技术分享技术分享
string strCom = "INSERT INTO Book(BookName,BookPage,BookUser)VALUES(@BookName,@BookPage,@BookUser)";
SqlParameter sp1 = new SqlParameter("@BookName", "我是通过实物提交上来的书名 保存失败测试");
SqlParameter sp2 = new SqlParameter("@BookPage", 1);
SqlParameter sp3 = new SqlParameter("@BookUser", "BookUser实物 保存失败测试");
var fsql = context.Database.ExecuteSqlCommand(strCom, sp1, sp2, sp3);
View Code

3、存储过程、视图

  • 视图也可以建一个实体类,创建方式和表的实体类创建一样,这里就不多说了(当然也可以直接写SQL查询SqlQuery,类型转换)。

    具体的看本文代码。

  • 存储过程其实就是执行SQL

    context.Database.SqlQuery("EXEC pClaStu");

    ("exec pro_XXX @i,@j,@he output", parameters)

4、关于一些缓存问题

  • 当前上下文对象缓存

DbSet.Local  定义:它表示此集中的所有“已添加”、“未更改”和“已修改”实体的本地视图。在上下文中添加或删除实体时,该本地视图将保持同步。同样,在本地视图中添加或删除实体也会自动在上下文中添加或删除实体。

意思就是说,我在当前上下文中做了数据操作,都会保存到本地。

其中缓存可以监控,是一个观察者模式。

技术分享技术分享
context.Student.Local.CollectionChanged += (sender, e) => {
    Console.WriteLine("-------start------");
    if (e.NewItems != null)
    {
        foreach (Student item in e.NewItems)
        {
            Console.WriteLine("ADD:"+item.StudentName);
        }
    }
    if (e.OldItems != null)
    {
        foreach (Student item in e.OldItems)
        {
            Console.WriteLine("Remove:" + item.StudentName);
        }
    }
    Console.WriteLine("-------end------");
};
View Code
  • 查询缓存(并没有执行SQL)

Find

5、关于延迟加载问题

  • IQueryable
  • 导航属性
  • Virtual关键字

上下文中加入的实体类前面加virtual关键字。

导航属性

导航属性包含virtual关键字,当时访问到这个导航属性的时候,才会加载这个导航属性。

技术分享技术分享
public Class()
{
    Student = new HashSet();
}
public virtual ICollection Student { get; set; }
View Code
技术分享技术分享
public long ClassID { get; set; }
[ForeignKey("ClassID")]
public virtual Class Class { get; set; }
View Code

如果导航属性不加这个关键字,则导航属性为空集合 ,

对象状态跟踪

状态跟踪原理

EF中对象跟踪使用的是一个DbChangeTracker对象来跟踪用户操作。当EF从查询结果总取到实体的时候,他会同步创建一个DbEntityEntity对象来记录实体的变化。一个实体实例就有一个对应的DbEntityEntity。只要对象活着他的DbEntityEntity也活着。DbContext. ChangeTracker对象在适当的时间自动检查对象属性值的更改或DbSet对象集合中对象个数的变化,负责同步更新对应的DbEntityEntry对象。我们可以使用DbContext.Entry(entity)方法获取entity所对应的状态对象,从而了解对象的相关信息。

适当的时间:

DbSet.Add
DbSet.Find
DbSet.Remove
DbSet.Local
DbContext.SaveChanges
Running any LINQ query against a DbSet
DbSet.Attach
DbContext.GetValidationErrors
DbContext.Entry
DbChangeTracker.Entries

如果禁用了状态跟踪Configuration.AutoDetectChangesEnabled = false;上面的动作都不会更新状态。如下图。

获取状态代码,主要是要得到实体对象对应的DbEntityEntity对象。context.Entry(book)  具体的看本文代码,下方下载

技术分享

BookName修改了,但是状态还是unchanged   ,bookname的修改状态也是false。(如果想改变状态就要手动调用ChangeTracker.DetectChanges()方法)

但是删除还是会修改状态的。

技术分享

但是最后提交的时候DbContext.SaveChanges(),此方法在内部会调用 ChangeTracker.DetectChanges()方法,根据它所管理的所有的DbEntityEntry对象的状态生成相应的Insert,Delete,Update命令。并负责将这些命令发送给数据库。(所以我们一般禁用状态跟踪,可以提高一点性能)

禁用状态跟踪

数据是只读的,那么,可以禁用状态跟踪以获取较优的性能

context.DbSet.AsNoTracking();   查询语句

this.Configuration.AutoDetectChangesEnabled = false;    这一句只能写在上下问的构造函数里面。

常用实体特性

 1、Key主键特性

EF默认ID id  表明ID是主键,需要是自增长整形的。

[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

2、外键

[ForeignKey("ClassID")]
public virtual Class Class { get; set; }

3、表

[Table("Student")]

[Table("Student",Schema ="dbo")]

4、长度

[MinLength(10),MaxLength(30)]
public string Name { get; set; }
5、非空

[Required(ErrorMessage="请输入描述")]
6、列
[Column(Order =1,TypeName = "Timest")]
7、忽略映射,可以应用于 自定义属性 非数据库字段。
[NotMapped]
8、时间戳长于[ConcurrencyCheck]一起用,判断版本号
[Timestamp,DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] Timest { get; set; }

9、[ConcurrencyCheck]  

生成的SQL中where中会添加这个值是不是和原来不一样了

实体之间关联

1、VS自己生成

最简单的方法就是先把数据库创建好,主外键关系设置好,然后VS建一个项目,添加组件  ADO.NET实体数据模型。选择来自数据库的CodeFirst。这样所有的实体都创建好了。(自己框架里面自己写的一个工具,还可以生成注释的。)

2、关系介绍

1......*

0,1........*

*........*

  多对多要有中间表的中间表也要有主键,住的注意的是,EF中所有的表都要有主键。

自关联

实体组合,拆分

实体集合属性

技术分享技术分享
public Class()
{
    Student = new HashSet();
}
public virtual ICollection Student { get; set; }
View Code

public virtual ICollection Student { get; set; }被初始化为一个HashSet。编译的时候EF会把他编译为一个代理对象,变成Student类的子类,这个子类在内部封装了EF早期版本所开发的相应类型。因此,对Student属性值的改变会影响到实体对象的状态。

本文代码下载

EF-初识


推荐阅读
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
author-avatar
steveukuk
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有