博客园在推广ORM方面的确做了很大的贡献,很多的程序员开始使用ORM,不用写SQL的喜悦让他们激动不已,可是好景不长,他们很快发现众多的烦恼一个接一个的出现了。

很遗憾,我并不打算在这篇文章中解决这些问题,因为的确存在这些问题,而且目前没有完美的解决方法。那么既然这样,我们为什么要使用ORM呢?难道真的是为了不使用SQL吗?

还是要看O - R ,我们为什么要将关系型的数据转化成Object的方式,DataSet的方式难道不好吗?和数据库的表现还是很一致,又简单又方便,为什么先辈们要兴师动众的转化为Object。
我们知道,Object是可以继承的,是可以使用接口的,而Relation没有这个概念。就是因为这一点,我们将实体设计成Object方法,从而获取了大量的优势。
例如:我可以在程序中检测实体是否支持IVersionObject接口,如果支持,我们将自动做版本控制,而如果你给我一个DataSet,那我将无法检测(不要告诉我检测是否存在Version字段)。通过这个特性我们将可以自动化处理很多的事情。

如,我设计了一个单据实体的基类,包含了SheetCode、SheetDate等等字段,然后我的OrderSheet继承自SheetBase,他们
将自动获取到这些标准的字段,而且我的基础类可以自动帮助我处理很多统一的规则,使程序更加稳健和统一。而这个Relation的东西是非常难做到的。

市面上有很多的ORM系统,鱼龙混杂,事实上,相当多的系统根本无法利用Object的继承特性,他们还是一堆的如何做一对多、多对多的概念。根本没有了解到ORM的精髓就做出来。

在这里我需要解释几个误解:
1、ORM使我们摆脱了SQL,但并不代表我们不再使用SQL,事实上,复杂的查询和报表我仍然推荐使用SQL,良好的系统应该可以兼容以前的方式;
2、
微软在表模型(Relation)上花费了无数的精力,所以目前Relation的一揽子解决方案是最完整,最好的。但我们看到,微软在.NET
2.0中对Object方式的绑定支持更近了一步,随着LinQ、XAML等很多后续技术的发展,相信领域模型(Object)的完整解决方案将更加完
整;
3、ORM更适合复杂的系统(这里使用复杂,而不是大型),而不是小的系统,因为这样的系统要求建造速度快,系统稳定,他们的业务规则异常的复杂,但他们对系统的性能要求并不是很高(相对电信这样的性能要求)。

Entity Framework将概念模型中定义的实体和关系映射到数据源,利用实体框架可以将数据源返回的数据具体化为对象;跟踪对象所做的更改;并发处理;将对象 更改传播到数据源等。今天我们就一起讨论如何利用Entity Framework进行查询、插入、更新和删除数据。

查询

我 们将使用AdventureWorks数据库来进行今天的所有演示,因此开始之前请准备好相应的数据库。在EF中进行查询应该说是相当简单,只需要定义一 个类继承于“DbContext”,然后定义对应的“DbSet”集合属性即可。例如下面的“AdventureWorksContext”类:

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using EFPowerTools.Models.Mapping; namespace EFPowerTools.Models
{
public partial class AdventureWorksContext : DbContext
{
static AdventureWorksContext()
{
Database.SetInitializer<AdventureWorksContext>(null);
} public AdventureWorksContext()
: base("Name=AdventureWorksContext")
{
} public DbSet<Employee> Employees { get; set; }
public DbSet<Person> People { get; set; }
}
}

一个数据库上下文的生命周期随着该对象的创建而开始,随着对象的释放(或GC回收)而结束,因此建议在开发过程中使用“Using”编码方式,这样就可以免去手动释放对象的操作。如下面的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var persons = db.People.Where(p => p.LastName == "Stevens").OrderBy(p=>p.FirstName);
foreach(var p in persons)
{
Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName);
}
}
}
}
}

另外对于数据库连接的管理在EF中是透明的,我们一般不需要手动进行处理,当查询一个对象时打开连接当处理完查询的结果集之后会自动关闭连接。

在EF的Code First模式中有三种常用的数据查询方式(Model First和Database First中还有其他方式如:使用Entity SQL,但这不是我们今天的重点):Linq To Entity表达式查询、基于方法的查询、原生SQL查询。

Linq To Entity表达式查询

查询表达式是C#3.0新增的功能,它是由一组类似于T-SQL或XQuery声明性语句组成,CLR并不能直接读取这种查询表达式而是在编译时转换为对应的方法调用。如下面的例子:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var persons = from p in db.People
where p.LastName == "Stevens"
orderby p.FirstName
select p; foreach (var p in persons)
{
Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName);
}
}
}
}
}

基于方法的查询

基于方法的查询事实上是一组对象的扩展方法,同Linq查询不同的是这些方法可以直接被CLR识别并运行。

例如上面的方法我们可以转换为如下代码,他们的效果是一样的,返回的都是“IQueryable”对象:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var persons = db.People.Where(p => p.LastName == "Stevens").OrderBy(p => p.FirstName);
foreach (var p in persons)
{
Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName);
}
}
}
}
}

原生SQL查询

EF还支持原生SQL查询(注意与Entity SQL区别),例如:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models;
using System.Data.Objects; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var persons = db.People.SqlQuery("SELECT * FROM Person.Person WHERE LastName='Stevens'");
foreach (var p in persons)
{
Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName);
}
}
}
}
}

不仅如此,EF还支持非实体类型的查询:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models;
using System.Data.Objects; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var persons = db.Database.SqlQuery<string>("SELECT FirstName FROM Person.Person WHERE LastName='Stevens'").ToList();
foreach (var p in persons)
{
Console.WriteLine("FirstName:{0}", p);
}
}
}
}
}

当然也支持无返回值的SQL命令:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models;
using System.Data.Objects; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
db.Database.ExecuteSqlCommand("UPDATE Person.Person SET NameStyle=1 WHERE BusinessEntityID='1813'");
}
}
}
}

增加

在EF中添加操作一般有两种方式:一是直接创建对象,然后调用“DbSet”的”Add()”方法进行添加;二是调用数据库上下文的”Entry()”方法并设置对应的状态。无论使用哪种方式最终一定要调用“SaveChange()”进行提交。如:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var stephen = new Person
{
BusinessEntityID=20778,
PersonType="EM",
NameStyle=false,
Title="Architec",
FirstName="Stephen",
LastName="Chow",
EmailPromotion=1,
rowguid = Guid.NewGuid(),
ModifiedDate = DateTime.Now
};
db.People.Add(stephen); var jeffrey = new Person
{
BusinessEntityID = 20779,
PersonType = "EM",
NameStyle = false,
Title = "Engineer",
FirstName = "Jeffrey",
LastName = "Lee",
EmailPromotion = 0,
rowguid=Guid.NewGuid(),
ModifiedDate=DateTime.Now
};
db.Entry(jeffrey).State = EntityState.Added; db.SaveChanges();
}
}
}
}

效果如图:

此外,在含有导航属性时,将一个对象赋值给另一个对象的导航属性也能达到添加的效果(当导航属性为”DbSet“集合时通过调用导航属性的 “Add()“方法也同样可以达到添加效果),例如在”Person.Person”中我们上面添加了两条记录,但对于“Person”类的导航属性 “EmailAddress”和“Password”在对应的“EmailAddress”表和“Password”表中并没有添加对应的记录,此时我们 就可以通过下面的方式来增加:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var password = new Password
{
BusinessEntityID=20778,
PasswordHash = "ZEgQH9qZIPiLgyBHYw/dD1FJQNpdQyIAa+BFfKX5/jg=",
PasswordSalt = "7iy/umc=",
rowguid=Guid.NewGuid(),
ModifiedDate=DateTime.Now
}; var email = new EmailAddress
{
BusinessEntityID = 20778,
EmailAddress1 = "StephenChow@outlook.com",
rowguid = Guid.NewGuid(),
ModifiedDate = DateTime.Now
}; var person = db.People.Find(20778);
person.Password = password;
person.EmailAddresses.Add(email); db.SaveChanges();
}
}
}
}

此时查看将可以看到“EmailAddress”表中确实增加了一条记录(“Password”表同样也是如此):

状态跟踪

在这里我们需要强调一点那就是状态跟踪,对于上面的操作如果我们调用“Attach()”方法对实体进行跟踪或者设置实体的状态那么数据将不会保存到数据库:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var stephen = new Person
{
BusinessEntityID = 20778,
PersonType = "EM",
NameStyle = false,
Title = "Architec",
FirstName = "Stephen",
LastName = "Chow",
EmailPromotion = 1,
rowguid = Guid.NewGuid(),
ModifiedDate = DateTime.Now
};
db.People.Add(stephen);
db.People.Attach(stephen);
//db.Entry(stephen).State = EntityState.Unchanged;//同上面db.People.Attach(stephen);作用一样
db.SaveChanges();
}
}
}
}

使用”Attach()”方法进行实体跟踪时会设置实体的状态为“Unchanged”此时实体处于未修改状态,当执行 “SaveChange()”方法时EF不会执行修改操作。相反如果此时设置实体状态为“Modified”则EF执行更新操作。那么既然EF的数据修改 操作(增加、更新、删除)是根据实体状态而进行的,那么为什么之前我们的增加操作能正常进行而不用手动修改其状态呢?原因是EF会自动发现状态改变,在调 用下面的方法时状态发现是自动的:

· DbSet.Find

· DbSet.Local

· DbSet.Remove

· DbSet.Add

· DbSet.Attach

· DbContext.SaveChanges

· DbContext.GetValidationErrors

· DbContext.Entry

· DbChangeTracker.Entries

当然,并不是所有的时候我们都需要EF自动发现状态改变,设置 “DbContext.Configuration.AutoDetectChangesEnabled”属性为“false”可以禁用自动发现功能。

注意:在EF对数据操作时有时会抛出:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

此时可以使用try{} catch(DbEntityValidationException ex){} 对异常进行捕获,将鼠标放到ex上并逐级查看ex的信息进行解决。

删除

下面看一下EF的删除操作:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models;
using System.Data.Objects; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var mail = db.EmailAddresses.Where(m => m.EmailAddressID == 19977).FirstOrDefault();
db.EmailAddresses.Remove(mail);
db.SaveChanges();
}
}
}
}

当然有了上面状态跟踪的讨论相信大家也可以想到如下删除方法:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models;
using System.Data.Objects; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var mail = db.EmailAddresses.Where(m => m.EmailAddressID == 19976).FirstOrDefault();
db.Entry(mail).State = EntityState.Deleted;
db.SaveChanges();
}
}
}
}

修改

修改数据很简单,直接修改对应的属性即可:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFPowerTools.Models;
using System.Data.Objects; namespace EFPowerTools
{
class Program
{
static void Main(string[] args) {
using (var db = new AdventureWorksContext())
{
var person = db.People.Where(p => p.LastName == "Stevens").OrderBy(p=>p.BusinessEntityID).FirstOrDefault();
person.NameStyle = false;
person.EmailAddresses.First().EmailAddress1 = "KenshinCui@outlook.com";
db.SaveChanges();
}
}
}
}

需要说明的是,EF在执行修改操作前会检查哪些属性发生了变化,并且只会修改发生变化的字段。

ORM框架Entity Framework的更多相关文章

  1. 实体框架(Entity Framework)快速入门

    实体 框架 (Entity Framework )简介 实体框架Entity Framework 是 ADO .NET 中的一组支持 开发 面向数据的软件应用程序的技术.是微软的一个ORM框架. OR ...

  2. C#代码生成工具:文本模板初体验 使用T4批量修改实体框架(Entity Framework)的类名

    转自:http://www.cnblogs.com/huangcong/archive/2011/07/20/1931107.html 在之前的文本模板(T4)初体验中我们已经知道了T4的用处,下面就 ...

  3. 实体框架(Entity Framework)快速入门--实例篇

    在上一篇 <实体框架(Entity Framework)快速入门> 中我们简单了解的EF的定义和大体的情况,我们通过一步一步的做一个简单的实际例子来让大家对EF使用有个简单印象,看操作步骤 ...

  4. ADO.NET实体框架Entity Framework模型-基于元数据解析

           上一篇简单介绍了EF的XML模型结构,在基于xml解析一文中,主要使用xml查询技术Xpath,XQuery来得到实体模型中相应信息的,由于这种方式在数据库庞大,表关系复杂的情况下,有诸 ...

  5. ORM之Entity Framework(EF)

    ORM之Entity Framework(EF) 一.下载安装: nuget 搜索Entity Framework安装 EntityFramework.Extension是个扩展库根据需要安装 二.使 ...

  6. 细说ORM之Entity FrameWork系列(被替换)

    一. 谈情怀 从第一次接触开发到现在(2018年),接近五年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQL ...

  7. ASP.NET Core 中的 ORM 之 Entity Framework

    目录 EF Core 简介 使用 EF Core(Code First) EF Core 中的一些常用知识点 实体建模 实体关系 种子数据 并发管理 执行 SQL 语句和存储过程 延迟加载和预先加载 ...

  8. 实体框架Entity Framework 4.1快速入门

    介 绍 在旧的Entity 框架中,开发者可以从已存在的数据库中产生业务实体的模型,这种开发方法被称为数据库驱动的开发方法.而在4.1的Entity Framework中,支开发者先创建实体业务类,然 ...

  9. ADO.NET实体框架Entity Framework模型-基于XML解析

            最近由于项目需求,需要对实体框架内表之间的关系进行处理,主要功能要求是通过一表名,返回其在实体框架内的所有关系表.主外键及每个字段的属性.先简单描述我解决这个问题从开始到最后的分析实现 ...

随机推荐

  1. PasswordHasher

    namespace Microsoft.AspNet.Identity { public class PasswordHasher : IPasswordHasher { public virtual ...

  2. Hibernate.lock()方法中各种锁的区别

    悲观锁 它指的是对数据被外界修改持保守态度.假定任何时刻存取数据时,都可能有另一个客户也正在存取同一笔数据,为了保持数据被操作的一致性,于是对数据采取了数据库层次的锁定状态,依靠数据库提供的锁机制来实 ...

  3. mysql 索引及其原理

    mysql 索引 KEY与INDEX的区别: KEY is something on the logical level, describes your table and database desi ...

  4. Python-面向对象编程(二)

    面向对象进阶篇: 初级篇中我们介绍了面向对象基本知识: 1.面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 2.介绍了类中的对象.方法和属性及类中内置的方法 3.类 是一个模板 ...

  5. C++基础入门

    #include "iostream" using namespace std; class A{ public:      A(int x1){       x = x1;  } ...

  6. SCP 命令(转)

    \ svn 删除所有的 .svn文件 find . -name .svn -type d -exec rm -fr {} \; linux之cp/scp命令+scp命令详解   名称:cp 使用权限: ...

  7. easyui的textbox和validatebox的 赋值区别

    区别代码如下: textbox:$('userId').textbox('setValue','aaa'); validatebox :$('userId').val('aaa');  

  8. FMDB 多线程使用

    在App中保持一个FMDatabaseQueue的实例,并在所有的线程中都只使用这一个实例. [FMDatabaseQueue databaseQueueWithPath:path]; FMDatab ...

  9. 新浪微博客户端(36)-自定义带placeholder的TextView

    iOS 上自带的UITextView竟然不能设置placeholder,但是UITextView却可以,我也真是醉了.没办法了,自己写一个 DJTextView.h #import <UIKit ...

  10. Sublime Text3

    Sublime Text3 激活码: ----- BEGIN LICENSE ----- Andrew Weber Single User License EA7E- 813A03DD 5E4AD9E ...