EntityFramework知识点记录

ORM

记录EF之前,首先讲讲什么是ORM(对象关系映射)

ORM:面向对象的对象模型和关系型数据库之间的相互转换。基于数据库的数据存储,实现的一个面向对象接口。

O(对象模型):根据数据库表建立的一个个实体

R(数据接口):我们建立的数据库表

M(映射):从数据库到对象模型的映射。

优点:不需要考虑SQL,大大提高工作效率。减少维护数据访问层的成本

发展史

EF1.0时,只支持Database First,数据库优先。必须将设计器指向一个现有的数据库。

EF4时,支持Model First,模型优先。可以使用设计面板来创建实体类。最终都会生成EDMX文件。

EF4.1时,支持Code First,代码优先。不需要依赖设计类,只需要编写类就行,会自动映射成表。

三种风格

EF有三种开发风格。

Database First:最适合于使用已经存在的数据库的应用。

如果数据库已经存在,只要花一点时间就可以编写数据访问层。实体数据模型(EDM)可以从数据库生成。

Model First:使用EDMX设计器来设计我们的模型。数据库和类,会从这个设计器来生成。

使用该EDM可以创建概念模型和数据库,使用这种方法原因是我们需要使用设计器来展示图形关系。

Code First:所有的领域模型都是以类编写。这些类会建立我们的EDM,数据库模式会从中创建。

对于所有业务逻辑,以类来进行实现。使用原因如下

1. 数据库只作为模型的持久化操作,没有数据逻辑。

2. 完全控制代码,没有自动生成的模型和上下文。

3. 数据库不会手动更改,模型类总是更改,数据库基于模型变更。

选择条件:如果想要类图使用Model First。已有数据库 使用Database First。已有模型类 使用Code First。

架构

EF构建在provider架构上,当创建LINQ查询是,EF引擎会链接provider并生成SQL。发送数据库。

概念数据模型

EDM全称Entity Data Model(实体数据模型)。使用EF,必须创建EDM,也就是.edmx文件。它定义了我们的概念模型类,类之间的关系与映射。

一旦创建的EDM就可以对概念模型执行所有的CRUD操作。EF会将操作翻译成SQL,并发送数据库。

ObjectContext:用来在创建的EDM上进行操作。它是EF的主要对象,负责:管理数据库连接、执行操作、追踪模型更改。

当我们想保存一个新的或者更改对象到数据库时,我们必须调用SaveChanges方法。

DbContext:ObjectContext的封装类,是一个更好的API。一般均使用此类,来进行操作。

DbContext:Code First的核心类,数据库抽象。包含表内容。

DbSet<T>:DbContext有泛型的集合属性,每个属性的类型是DbSet<T>对应于每个表。表中的数据定义在T的属性里面。

Migration迁移:如果需要添加或修改表,EF通过Migration迁移,功能来解决。允许你通过C#代码更改数据库结构,可以添加、删除、修改、添加索引。

数据库上下文

数据库上下文是数据库的抽象,它集成DbContext。需要传递链接字符串。需要使用DbSet来表示一个表。如不指定名称,则默认与上下文同名。

  public class EFContext : DbContext
{
public EFContext() : base("EFConnection")
{ } public DbSet<Donator> Donators { get; set; }
}

四种加载方式

EF加载数据的方式一共有四种:预加载、延迟加载、显示加载、按需加载。不同的加载方式适用于不同的情况。

延迟加载:

又叫惰性加载、懒加载。在需要或使用的时候,加载数据。默认情况下,EF会使用延迟加载方式。

在数据库上下文中表示为:Configuration.LazyLoadingEnabled = true; 如果false,则不会启用延迟加载。

属性或集合上添加virtual,会让此属性懒加载。就是访问时不会加载,只有请求他时,才会加载数据。

同时懒加载需要在数据库上下文中添加标记。默认是启用延迟加载状态

public UserDbContext()
: base("name=UserDbContext")
{
this.Configuration.LazyLoadingEnabled = true;
}

下面展示一个延迟加载的例子

using (var dbcontext=  new ModelFirstDemoEntities())
{
#region 延迟加载:用的时候加载
var customers = from c in dbcontext.Customer
select c; foreach (var cus in customers)
{
Console.WriteLine(cus.Id);
foreach (var order in cus.Order) //根据导航属性,自动查询客户的订单
{
Console.WriteLine(order.OrderContent);
}
}
#endregion
}

上文中的例子中,对表Customer进行了一次查询。然后每循环访问一次Order就会进行一次查询。

预加载:

又叫贪婪加载,如果想让数据一次性全部加载到内存中,那么需要使用.Include(T)方法。

using (var dbcontext=  new ModelFirstDemoEntities())
{
#region 贪婪加载: 一次查询加载全部数据
var q = from t in dbcontext.Customer.Include("Order")
select t; foreach (var cus in q)
{
Console.WriteLine("Teacher : {0}", cus.Id);
Console.WriteLine("Respective Courses...");
foreach (var order in cus.Order)
{
Console.WriteLine("Course name : {0}", order.OrderContent);
}
Console.WriteLine();
}
#endregion }

Include方法,是针对IQueryble的扩展方法。只有一次数据交互过程。可以减少与数据库交互次数。

但如果数据量很大,一次性将所有数据载入内存是不明智的。

显示加载:

显示加载和延迟加载类似,不过需要手动关闭EF的延迟加载属性。通过Configuration.LazyLoadingEnabled = false

以下代码中,首先关闭延迟加载,然后使用Load(),启用延迟加载。那么会形成4次查询。

可以通过判断条件,对加载方式进行控制,过滤掉无用的数据,从而减少数据的交互次数。

using (var dbcontext= new ModelFirstDemoEntities())
{
dbcontext.Configuration.LazyLoadingEnabled = false;
#region 显式加载:查询部分列数据,前提关闭 懒加载
//查询表中部分列的数据
var items = from c in dbcontext.Customer
select c;
foreach (var item in items)
{
//条件判断,只加载满足条件的数据,减少访问数据库的次数
if (item.Id < )
{
dbcontext.Entry(item).Collection(c => c.Order).Load();
Console.WriteLine(item.CusName);
} foreach (var order in item.Order)
{
Console.WriteLine("Course name : {0}", order.OrderContent);
}
}
#endregion
}

按需加载:

按需加载,就只是单纯的加载需要的列

   #region 按需加载:查询部分列数据
//查询表中部分列的数据
var items = from c in dbcontext.Customer
where c.Id <
select new { Id = c.Id, CName = c.CusName, OrderCount = c.Order.Count() };
foreach (var item in items)
{
Console.WriteLine(item.CName);
}
#endregion

封装方法

先看一个标准的查询封装。

#region 按条件查询:LoadItems(Func<T, bool> whereLambda)
public IQueryable<T> LoadItems(Func<T, bool> whereLambda)
{
return MyBaseDbContext.Set<T>().Where(whereLambda).AsQueryable();
}
#endregion

这样写的Func委托,会先查出所有的数据,然后根据条件where多次。

对于这种情况,我们需要改成 Expression<Func<TObject, bool>>

使用表达式树,会带上查询生成一条SQL。

AutoMapper

使用按需加载会投影出对应的类,如果表字段非常多,导航属性也非常多,那么手动映射就不方便了。

接下来,我们使用AutoMapper来完成映射。

创建数据库

使用两种方法可以创建数据库。1. 通过数据迁移。2. 通过EF数据库的API

通过API创建

 using (var context = new EFContext())
{
context.Database.CreateIfNotExists();

通过数据迁移

开启数据迁移:Enable-Migrations。开启后,会生成Configuration文件。可以在此文件中,进行种子填充。

显示迁移:Add-Migration。当开启数据迁移后,在需要修改数据库,则可以选择显示迁移,会生成多个迁移文件。

退回指定迁移:Update-Database  -TargetMigration 迁移名称。

更新数据库:update-database

注意:如需进行种子填充,需要开始Configuration文件中的,AutomaticMigrationDataLossAllowed 和 AutomaticMigrationsEnabled

CURD

写一个增删改查的小例子

 public async Task<string> Index()
{
StringBuilder strText = new StringBuilder();
using (var context = new EFContext())
{
var donators = new List<Donator>{
new Donator { Name="A", Amount=1.00m, DonatorDate=DateTime.Now },
new Donator { Name="B", Amount=2.00m, DonatorDate=DateTime.Now },
new Donator { Name="C", Amount=3.00m, DonatorDate=DateTime.Now }
};
context.Donators.AddRange(donators);
await context.SaveChangesAsync();
strText.Append("添加完成 \n"); if (context.Donators.Any())
{
var updateDonators = context.Donators.First(p => p.Name == "A");
updateDonators.Name = "AA";
await context.SaveChangesAsync();
} context.Donators.Remove(context.Donators.First(p => p.Name == "B"));
await context.SaveChangesAsync(); foreach (var donator in context.Donators)
{
strText.Append(donator.Name + "\n");
} }
return strText.ToString();
}

初始化器

初始化器会在首次实例化过程期间或者EF首次访问数据库时运行。初始化器有三种:

CreateDatabaseIfNotExists: 指如果数据库不存在则创建。

DropCreateDatabaseIfModelChanges:指如果模型改变了就销毁之前的数据库在创建。

DropCreateDatabaseAlways:首次在应用程序域中使用上下文时,重新创建数据库。

MigrateDatabaseToLatestVersion:在应用程序启动时自动升级

在方法中重写Seed,可以设置数据库种子,并且进行初始化数据。

 //模型改变就销毁数据库
public class InitializerDrop : DropCreateDatabaseIfModelChanges<EFContext>
{
protected override void Seed(EFContext context)
{
base.Seed(context);
} }
//如果数据库不存在创建
public class InitializerCreate : CreateDatabaseIfNotExists<EFContext>
{
protected override void Seed(EFContext context)
{
base.Seed(context);
}
}
//总是销毁再创建
public class InitializerDropAlways : DropCreateDatabaseAlways<EFContext>
{
protected override void Seed(EFContext context)
{
context.PayWays.AddRange(new List<PayWay> {
new PayWay { Name="支付宝" },
new PayWay { Name="微信" },
new PayWay { Name="QQ红包" }
});
}
}

可以设置默认初始化器

在数据库上下文中,进行定义即可。

MigrateDatabaseToLatestVersion:当你发布部署应用程序的时候,可能希望当程序启动的时候它自动更新数据库(更新应用任何未更新的迁移),
你可以通过注册 MigrateDatabaseToLatestVersion 数据库初始化器来实现这一点,数据库初始化器只包含一些逻辑检查用于确保数据库被正确设置,
这个逻辑检查将会在AppDomain 的 context 第一次被使用的时候执行。
 public EFContext() : base("name=EFConnection")
{
Database.SetInitializer(new InitializerDropAlways());
       Database.SetInitializer(new MigrateDatabaseToLatestVersion<EFContext, Configuration>(""));
}

属性配置

类的特性配置,主要是两个命名空间:System.ComponentModel.DataAnnotations.Schema  / System.ComponentModel.DataAnnotations

代码中设置了,表明、主键、列名、长度等

 [Table("CHENXY_PAYWAY")]
public class PayWay
{
[Key]
public int Id { get; set; }
[MaxLength(,ErrorMessage = "支付方式的名称不能大于8")]
public string Name { get; set; }
} [Table("CHENXY_DONATOR")]
public class Donator
{
[Key]
[Column]
public int DonatorId { get; set; }
[StringLength(, MinimumLength = )]
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime DonatorDate { get; set; }
}

数据库映射

DbContext类有一个OnModelCreating方法,用于配置数据库模式的映射。

  public class EFContext : DbContext
{
public EFContext() : base("name=EFConnection")
{ } public DbSet<Donator> Donators { get; set; }
public DbSet<PayWay> PayWays { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//设置表名和主键
modelBuilder.Entity<Donator>().ToTable("CHENXY_DONATOR").HasKey(m => m.DonatorId);
//列名映射
modelBuilder.Entity<Donator>().Property(m => m.DonatorId).HasColumnName("Id");
modelBuilder.Entity<Donator>().Property(m => m.Name).IsRequired().IsUnicode().HasMaxLength();
base.OnModelCreating(modelBuilder);
}
}

伙伴类

数据库映射的方法,只适合于少量的表。如果表数据比较多,可以使用伙伴类。

为每个实体类单独创建一个伙伴类,然后在OnModelCreating方法中调用这些伙伴类。

public class DonatorMap:EntityTypeConfiguration<Donator>
{
public DonatorMap() {
ToTable("DonatorFromConfig");
Property(m => m.Name).IsRequired().HasColumnName("DonatorName");
}
} public class EFContext : DbContext
{
public EFContext() : base("name=EFConnection")
{ } public DbSet<Donator> Donators { get; set; }
public DbSet<PayWay> PayWays { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DonatorMap());
base.OnModelCreating(modelBuilder);
}
}

可空类型

EF会自动推断类型,如果希望创建的是可空类型。一、使用DateTime?。二、使用 IsRequire() 必填,使用 IsOptional() 选填。

一对多

public class Donator
{
public Donator() {
PayWays = new HashSet<PayWay>();
} public int DonatorId { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime DonatorDate { get; set; }
public virtual ICollection<PayWay> PayWays { get; set; }
}

此时会在PayWay表中,添加DonatorId字段。也可以指定外键字段,使用HasForeignKey

 public DonatorMap()
{
ToTable("DonatorFromConfig");
Property(m => m.Name).IsRequired();
HasMany(d => d.PayWays)
.WithRequired()
.HasForeignKey(p => p.DonatorId);
}

或者使用 [ForeignKey("DonatorId2")]

一对一

 public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public virtual Student Student { get; set; }
} public class Student
{
public int PersonId { get; set; }
public virtual Person Person { get; set; }
public string CollegeName { get; set; }
public DateTime EnrollmentDate { get; set; }
}

可以使用WithOption来制定一对一的关系

public class StudentMap : EntityTypeConfiguration<Student>
{
public StudentMap()
{
HasRequired(p => p.Person).WithOptional(s => s.Student);
HasKey(s => s.PersonId);
}
}

多对多

HasMany(p => p.Companies)
.WithMany(c => c.Persons)
.Map(m =>
{
m.MapLeftKey("PersonId");
m.MapRightKey("CompanyId");
});

zhanweifu

EF 知识点的更多相关文章

  1. %E3%80%90%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E3%80%91

    "%3Cdiv%20class%3D%22htmledit_views%22%20id%3D%22content_views%22%3E%0A%20%20%20%20%20%20%20%20 ...

  2. .net学习之Attribute特性和EF关键知识点

    一.Attribute特性/标签1.Attribute用来对类.属性.方法等标注额外的信息,贴一个标签简单的说,定制特性Attribute,本质上就是一个类,它为目标元素提供关联附加信息,并在运行时以 ...

  3. Linq to sql与EF零碎知识点总结

    ------------------------------第一天(2013-3-25) 1.ado.net实体模型,(Ef) 2.创建上下文对象: 调用相应方法,最后调用.savechanges() ...

  4. asp.net、mvc、ajax、js、jquery、sql、EF、linq、netadvantage第三方控件知识点笔记

    很简单,如下: 父页面:(弹出提示框) function newwindow(obj) { var rtn = window.showModalDialog('NewPage.htm','','sta ...

  5. 和ef一起使用的一些知识点。

    ObjectContext.ExecuteFunction 方法 (String, ObjectParameter[]) .NET Framework 4.6 and 4.5    执行在数据源中定义 ...

  6. ASP.NET MVC开发:Web项目开发必备知识点

    最近加班加点完成一个Web项目,使用Asp.net MVC开发.很久以前接触的Asp.net开发还是Aspx形式,什么Razor引擎,什么MVC还是这次开发才明白,可以算是新手. 对新手而言,那进行A ...

  7. C#高级知识点&(ABP框架理论学习高级篇)——白金版

    前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...

  8. ASP.NET MVC5+EF6+EasyUI 后台管理系统(62)-EF链接串加密

    系列目录 前言: 这一节提供一个简单的功能,这个功能看似简单,找了一下没找到EF链接数据库串的加密帮助文档,只能自己写了,这样也更加符合自己的加密要求 有时候我们发布程序为了避免程序外的SQL链接串明 ...

  9. Java进击C#——应用开发之Linq和EF

    本章简言 上一章笔者对于WinForm开发过程用到的几个知识点做了讲解.笔者们可以以此为开端进行学习.而本章我们来讲一个跟ORM思想有关的知识点.在讲之前让我们想一下关于JAVA的hibernate知 ...

随机推荐

  1. js 数组去重

    这是一道常见的面试题,最近在做[搜索历史记录]功能也用到,开始用了 indexOf 方法,该方法在 ECMA5才有支持,对于 IE8- 就不支持了. 我们可以自己写一个函数(Array对象的方法都是定 ...

  2. lecture14-RBM的堆叠、修改以及DBN的决策学习和微调

    这是Hinton的第14课,主要介绍了RBM和DBN的东西,这一课的课外读物有三篇论文<Self-taught learning- transfer learning from unlabele ...

  3. Python面试题 —— 计算列表中出现最多次的字符

    给你一个其中包含不同的英文字母和标点符号的文本,你要找到其中出现最多的字母,返回的字母必须是小写形式, 当检查最想要的字母时,不区分大小写,所以在你的搜索中 "A" == &quo ...

  4. 前端Mvvm QC 上传了测试版

    QC是一个前端MVVM框架,适合用来构建复杂的业务逻辑 项目地址:https://github.com/time-go/qc 技术支持QQ群:330603020 QC特点: 1.良好的浏览器兼容性(兼 ...

  5. Scala入门之控制结构

    package com.dtspark.scala.basics /** * Scala中的基本控制结构有顺序.条件和循环三种方式,这个其它的JVM语言是一致的,但是Scala也有一些高级的流程控制结 ...

  6. Sublime轻量级编辑器

    对于从事计算机的小伙伴,好用的编辑器等效于手里的利器!可说为,砍柴不误,磨刀工! 手有神器,游走四方! sublime,记得好像是支持跨平台的 家乡的情绪 http://pan.baidu.com/s ...

  7. 61-umask 简明笔记

    设定在创建文件时的权限掩码 umask [mask] 参数 mask可以是3位八进制数或者是如同在chmod中使用的符号值,mask指定不允许的权限(文件的实际权限是777减去umask值) 如果没有 ...

  8. difference between append and appendTo

    if you need append some string to element and need set some attribute on these string at the same ti ...

  9. linux系统数据落盘之细节

      本文节选自这里,原文以mysql innodb系统为例,介绍了数据经过的各层级的buffer和cache,其它系统也有相似的原理,摘录于此. 3.  VFS层 该层的缓冲都放在主机内存中,它的目的 ...

  10. stringBuffer拼接有规律字符串

    1. 拼接结果如下的字符串 1,2,3,4,5,6,7,8,9,10,11,12,12,12,12,34,234,2134,1234,1324,1234,123 2. 以前是这样想的,但是从效率,速度 ...