EF 知识点
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 知识点的更多相关文章
- %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 ...
- .net学习之Attribute特性和EF关键知识点
一.Attribute特性/标签1.Attribute用来对类.属性.方法等标注额外的信息,贴一个标签简单的说,定制特性Attribute,本质上就是一个类,它为目标元素提供关联附加信息,并在运行时以 ...
- Linq to sql与EF零碎知识点总结
------------------------------第一天(2013-3-25) 1.ado.net实体模型,(Ef) 2.创建上下文对象: 调用相应方法,最后调用.savechanges() ...
- asp.net、mvc、ajax、js、jquery、sql、EF、linq、netadvantage第三方控件知识点笔记
很简单,如下: 父页面:(弹出提示框) function newwindow(obj) { var rtn = window.showModalDialog('NewPage.htm','','sta ...
- 和ef一起使用的一些知识点。
ObjectContext.ExecuteFunction 方法 (String, ObjectParameter[]) .NET Framework 4.6 and 4.5 执行在数据源中定义 ...
- ASP.NET MVC开发:Web项目开发必备知识点
最近加班加点完成一个Web项目,使用Asp.net MVC开发.很久以前接触的Asp.net开发还是Aspx形式,什么Razor引擎,什么MVC还是这次开发才明白,可以算是新手. 对新手而言,那进行A ...
- C#高级知识点&(ABP框架理论学习高级篇)——白金版
前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(62)-EF链接串加密
系列目录 前言: 这一节提供一个简单的功能,这个功能看似简单,找了一下没找到EF链接数据库串的加密帮助文档,只能自己写了,这样也更加符合自己的加密要求 有时候我们发布程序为了避免程序外的SQL链接串明 ...
- Java进击C#——应用开发之Linq和EF
本章简言 上一章笔者对于WinForm开发过程用到的几个知识点做了讲解.笔者们可以以此为开端进行学习.而本章我们来讲一个跟ORM思想有关的知识点.在讲之前让我们想一下关于JAVA的hibernate知 ...
随机推荐
- Java7并发编程实战(一) 线程的中断
控制线程中断的方法一般常规是定义一个布尔值,然后while(布尔值) 去执行,当想停止该线程时候,把布尔值设为false. 这里我们来看第二种,Interrupt 该例子模拟一个线程从1打印到10,然 ...
- 异常和IO
异常 异常是指java程序运行时(非编译)所发生的非正常情况或错误. Java对异常进行了分类,不同类型的异常分别用不同的 Java 类表示,所有异常的根类为 java.lang.Throwable, ...
- HDU2389-Rain on your Parade-二分图匹配-ISAP
裸二分图匹配 /*--------------------------------------------------------------------------------------*/ #i ...
- 大新闻!HoloLens即将入华商用
昨天微软搞了大新闻,Terry和Alexi到了深圳,在WinHEC大会上宣布了2017上半年HoloLens正式入华商用. 关于HoloLens的技术原理和细节官方文档和报道已经披露很多了,他是一款真 ...
- javascript 中加’var‘和不加'var'的区别,你真的懂吗?
没看之前千万别说我是标题党,这个问题真的有好多淫都不懂!!! 大家都看了很多文章,都说避免隐式声明全局变量,就是说声明变量前必须加'var',那加了'var'和不加'var'到底有啥区别呢? 先来看一 ...
- keepalived+LVS 实现双机热备、负载均衡、失效转移 高性能 高可用 高伸缩性 服务器集群
本章笔者亲自动手,使用LVS技术实现实现一个可以支持庞大访问量.高可用性.高伸缩性的服务器集群 在读本章之前,可能有不少读者尚未使用该技术,或者部分读者使用Nginx实现应用层的负载均衡.这里大家都可 ...
- 天龙客户端的ResourceManager
今天培训的时候,Leader针对项目结构讲了很多分层架构的思想,思路,对我而言有很大的助益,学会了将需求分层,或者说先设计出各个层次,然后有需求后落实到对应的层次上,尤其对于刚开始的架构设计阶段,能把 ...
- hdu5481 Desiderium
链接 Desiderium 题意 给定n条线段,从中选取若干条,共有2n种选法(因为每一条线段有两种方法:选或者不选). 每一种选法都对应一个长度,也就是所选线段的并集长度. 求这2n种选法长度之和. ...
- RHCE实验环境|rhel7-lab
教学环境说明: 1.yum源地址是:http://content.example.com 2.网卡都用同一个,且自定义网卡! 3.网络配置参考 classroom IP 172.25.254.254/ ...
- 通过Keepalived实现Redis Failover自动故障切换功能
通过Keepalived实现Redis Failover自动故障切换功能[实践分享] 参考资料: http://patrick-tang.blogspot.com/2012/06/redis-keep ...