当前位置: 移动技术网 > IT编程>开发语言>.net > ADO.NET Entity Framework 在哪些场景下使用?

ADO.NET Entity Framework 在哪些场景下使用?

2018年09月21日  | 移动技术网IT编程  | 我要评论

郑云工作室 陈佳,彼得兔毛绒玩具,俏妞出招电影

enity framework已经是.net下最主要的orm了。而orm从一个mapping的概念开始,到现在已经得到了一定的升华,特别是ef等对orm框架面向对象能力的升华。切实地说,就是orm让在整个应用过程中更好地被封装和抽象化。

 

orm一开始只是mapping,最基础的就是表与类的对应、column和属性的对应,这只是最基础的。在这个层次上,数据库对象通过mapping在面向对象语言层面,也就是业务层面被封装成了业务对象,然后允许以操作业务对象的方式对数据库进行操作。

 

但是,在很长时间里,orm的提升都被对象与关系间的“阻抗失配”困扰。一直以来很多orm的水平都只是维持在了用对象的方式进行crud而已,除了减少代码错误、提高简单查询的开发效率,在复杂查询、性能等等一些方面结果都还是要跨层回到底层的操作框架比如ado.net甚至存储过程去解决问题。

 

所以,在应用场景上来说简单查询的场景ef和其他orm都是能够胜任的。

 

 

从应用场景说起,这点在b/s和c/s里也会很明显。用户使用web的时候和使用桌面软件的最大体验不同是什么?——所见即所得。你在网页上操作了半天,一个关闭就全没了,还必须提交然后获得下一个页面才能把数据状态和ui更新;而同样在桌面上,你的操作比如画图,在操作的一瞬间结果就出来了。当然了,web 2.0技术就在解决这个问题。

 

同样在oo和rdbms中的问题也在这里。

 

从oo的角度上看,你运行下一段代码结果如何:

 

user.name = "indream luo";

在oo里,就是user对象的name属性被更新了。如果是一个桌面软件,那么用户的名称应该也更改了。

 

但是如果这个对象的数据是存在关系数据库中,或者任意数据库,那么结果都逃脱不出这个套路:

 

var object = db.get(id);

change(ref object);

db.update();

你需要把更新push过去,将操作和数据持久化。

 

在存储分层开始,推送更新就不可避免,哪怕在说面应用中,也是将对象的更新推送到了ui。orm站立在应用场景不一致中间所要扮演的角色,就是一个润滑剂的角色。

 

在我所能马上想起的特性,就是ef和nhibernate的缓存机制。ef是一级缓存,nh是二级缓存,手动的话似乎ef也可以做到二级。然后在.net下最重要的一点是有linq。linq在有合适的provider的情形下可以把oo的序列化操作转化成目标的序列化操作,在这里就是linq转sql,这样就省去了拼接sql、sql注入等很多麻烦。另外linq延迟加载的特性也很大地减少了用户控制sql执行的工作。

 

在操作同步的基础上,还有结构同步的问题。表结构和对象结构同步是使用orm一大工作内容。ef有默认的生成工具,db first、model first、code first三种模式提供选择,加上自动生成同步sql,选择性是现在最广的,nh也有一些相应的生成器,数据库优先方面小弟linq to sql的拖拽最惊艳。

 

在这个场景下,加上对相关工具的利用,ef等orm适用于序列操作、减少数据库操作管理和结构同步工作量,减少开发成本。

 

 

最后,不可回避的就是阻抗失配的问题。

对象关系模型和关系数据库模型在以前很大程度上不一致,这是在以前。现在orm要做的就是如何让两者更接近,让一边的特性能更顺滑地体现在另一边。

 

我 在早几个月写过一篇总结,关于最近一个项目ef使用的一些方法——《entity framework 与 面向对象》。太长就选重点来说明。

 

ef所做的涵盖:类型匹配、对象结构、数据源区分。

 

类型匹配方面,就是把oo类型和数据库类型进行匹配,这是orm的基础。基础类型中的整数、浮点、字符串、日期这些不在话下,ef比较有特点的可能是枚举类型(enum)、复杂类型(complex type)、地理位置的功能,实现方式也比较理想。

 

对象结构方面是ef让我最惊艳的地方。ef的model,也就是entity能实现集成关系,也可以通过此同步在表结构中;ef中通过对外键的控制,对引用和依赖关系实现得十分出色;支持虚类、对象层面的get/set、访问控制都很好用。

 

插一段,通过使用model first,我倒是发现数据库的设计更加接近于范式了。因为linq和对象结构方面带来的便利,我可以把表结构设计得更“合理”。比如如果要获取user的上司的上司,假设每个user都只有一个上司,那么用ef或者一些orm就是:

 

var bigboss = user.superior.superior;

如果要写sql,感觉有点烦,算代码量和可读性已经能看出区别了。

 

最后是数据源区分。有一个问题是一个对象,它的数据不一定完全源于数据库,或者一个数据库,这个例子我常用。

比如user有三个字段:firstname、lastname、fullname。可以知道fullname其实就是firstname和lastname的拼接,如果创建model/entity,一般:

 

复制代码

public partial class user

{

    public string firstname { get; set; }

    public string lastname { get; set; }

    public string fullname

    {

        get

        {

            if (fullname == null)

            {

                this.fullname = string.format("{0} {1}", this.firstname, this.lastname);

            }

            return this.fullname;

        }

    } string fullname;

}

复制代码

而在数据库里,只需要存储firstname和lastname,fullname作为计算值就可以了,而且还是延迟加载的。

 

更甚者,我们还可以从上面的例子延伸,在entity中封装一些数据库操作:

 

复制代码

public partial class user

{

    public user superior { get; set; }

    public user bigboss

    {

        get

        {

            return this.superior.superior;

        }

    }

}

复制代码

此时在数据库中只存储一个superior关系即可,bigboss作为计算值就可以了。当然,你乐意还可以缓存和延迟加载,但ef已经处理了缓存了。

 

极端情况,确实是可以在表关系中“玩”不少面向对象的设计模式。

 

终上所述,ef适用于面向对象结构和特性优先性比较高的 场景。

 

 

那么相对地,也说说不适用的场景。

 

首先大家所诟病的是性能问题,这点希望不要抛开原理去说ef的性能。

 

ef由于其执行原理,性能损耗一般发生在:

 

linq也就是expression tree创建和转换成sql的过程

缓存比对的过程

特殊操作实现不合理

极限性能压力下的问题

性能泄露

 

1是不可避免的,2通过关闭比对或者缓存可以解决,3、4和5是主要问题。

特殊操作不合理举例来说,比如递归,获取一个树结构的一条索引链。如果是通过oo来做,那么就是要么要往返很多次数据库,要么要至少遍历一次对象表,要么就是要加一些特殊的“丑陋的”索引字段。

极限性能压力在包含上个问题的情况下扩展,比如sql server的存储过程执行的特殊操作是最快的,纯oo的方式肯定达不到。

这两项特殊项通过更原生的数据库方式去解决是最佳的解决方案。你可以和ef混用,也可以单独使用,但不要妄想着有银弹能同时解决所有问题。ef提供了sql的执行方式。

性能泄露不是一个专有名词,是我临时用的。意思是因为ef导致的不必要的性能浪费。特别是linq的延迟加载特性,许多不清楚linq特性的开发人员容易将linq序列无谓地实例化,浪费了资源。通常会是:

 

遍历查询全表数据, 然后再在oo层面进行筛选

无谓地执行实例化,进行查询,要么浪费缓存比对的资源,要么浪费查询资源

我只能说这是开发人员水平问题,虽然出现问题后很难定位,特别是一般情况下都会造成内存泄露。

 

最后最常见的还是回到数据模型同步的问题。当数据模型更改后,需要同步,这时候如果已经有业务数据了,是一件麻烦的事情。ef的migration我没用过,是一个解决方案但似乎不那么完美。在一些非orm应用的系统,sql集中管理架构下,在这个场景,可能会更容易进行维护。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网