• Ado.net Entity FrameWork的性能问题


        当我们需要大批量追加数据,或者一次查询海量数据的时候,必须注意,Ado.net Entity Framework的效率几乎是无法忍受的。简单的例子,我们将中国股市历年的日线,约500万条数据插入数据库,使用Ado.net一般仅需要2分钟左右,而使用EF,则这个时间会变为半小时的样子。当我们要一次性的将所有数据读取到内存的时候,这个效率同样的令人难以忍受。所以,要注意以下要点:

    1、使用Ado.net Entity,使用Esql性能略好,因为Linq To entities将首先转换为esql再到sql语句。不过,我们多数的时候仍将使用Linq查询,这样相对来说知识能够复用,而无需急于学习Esql。

    2、如果是查询一条记录,用GetObjectByKey

        一般情况下,EF每次查询都会从数据库中获取资料,不会使用缓存,这样效率较低。此时可以考虑使用GetObjectByKey,这样,若该记录已经在缓存中则不会再次向数据库发出Sql命令,不过如果该记录

    在缓存中不存在,则会引发异常,所以我们通常使用TryGetObjectByKey

        using (QuoteEntities entity = new QuoteEntities())

       {

           object quote;

           EntityKey key = new EntityKey("Quote", "StockCode", "300001");

           entity.TryGetObjectByKey(key, out entity);

       }

    3、如果无需对查询的结果进行更新,则使用Notracking查询,默认情况下系统预设的是MergeOption.AppendOnly

        using (QuoteEntities entity = new QuoteEntities())

       {

           Quote quote= entity.Quotes.Execute(MergeOption.NoTracking).Where(a => a.Stockcode == "300001").FirstOrDefault();

       }

    4、如果某个查询会多次使用,用预编译查询:我们每次发出Linq To entities查询的时候,系统会将其编译为Esql,然后使用Provider转换为相应数据库的Sql语句,这个过程很长。

    我们可以编译好查询,这样EF会缓存编译的结果,这样,后续的查询就无需执行从linq到Esql再到Sql的转换,速度会提高。

    private static readonly Func<QuoteEntities, string ,User> CompiledQueryGetByStockCode =

    CompiledQuery.Compile<QuoteEntities, string, User>((entity, stockCode) => entity.Quotes.Where(a=>a.StockCode == stockCode).FirstOrDefault());

    然后,执行该查询:

        using (QuoteEntities entity = new QuoteEntities())

       {

           Quote quote= CompiledQueryGetByStockCode.Invoke(entity, "300001");

       }

    5、可以使用原生的Ado.net命令,但EF的Context中的Connection并不能直接利用,因为其预设为执行ESQL,那么要直接执行Sql,包括如下三个命令:

            ObjectContext.ExecuteStoreCommand(),用来执行非Select的Sql命令。

            ObjectContext.ExecuteStoreQuery<T>(),用来执行Select的Sql命令,返回实体集合。

            ObjectContext.Translate<T>:这个是用来衔接Ado.net的,当你得到一个DbDataReader的时候,可以将结果转换成实体集合。

            但实际上只有查询比较有效。

    6、批量insert的时候,只能使用原生Ado.net,自己根据Factory创建DbConnection对象和insert命令。当然,可以为DbCommand建立两个扩展方法,添加参数和为参数赋值。当然,批量insert应该包裹在

    事务中才能保证速度,否则一些数据库默认是每insert一条就开启一个事务,性能低下。我们可以每insert数万条记录才提交一次事务,速度会更快些。

    7、在以性能为主的应用中,不建议使用EF的实体类,当然伴随而来的,是不建议使用EF:

        在使用Sqlite追加数据的时候,在资源管理器中观察文件变化,文件大小每次缓缓的增加4K的样子,但是在不使用EF的实体对象而单纯使用Ado.net的情形下,每次增加1M的样子,要快得多。由此可见即使将数据读入到EF的实体类,然后使用Ado.net直接insert到数据库,性能的下降也在10倍以上,这或许和tracking有关。

         而使用原生Ado.net,常见的DbHelper方式和反射到实体类的方式,都有些奇怪。

         事实上,建立轻量级的DbHelper,只要为DbCommand命令建立两个扩展方法处理参数问题,实体类实现一个IEntity接口(用于使用序号方式为各属性Get或Set),再实现两个扩展方法,即将DataReader返回的对象,转换成实体类的IEnumerator,将实体对象转换为DbParameter以作为insert、update的输入参数,则数据访问可简单的实现。当然,为了避免写太多Sql语句,为每个实体对象中加入insert、GetByKey之类的支持也是可行的。

        从这个角度来说,我们将实体对象和实体集合对象,混合在实体类中当然也是可行的,不过我们可以建立专门的对应类,继承自定义的泛型Repository<实体>来处理,项目结构更为简单,与数据库有关的全部放在Data目录下面。当然,由于实体对象需要创建才能执行其方法,因此涉及到实体对象集合的方法往往写成静态方法更合适。

        不过,考虑到软件系统,什么都无所谓,惟快不破,则EF实际上已经淡出视野,并无实际价值。

  • 相关阅读:
    IE8下提示'console'未定义错误
    左右添加和删除
    箭头函数
    事件冒泡
    选中状态改变,并且实现左边选中便便添加
    appcan里面模板的使用
    白面机器学习笔记(一)
    常见的模型加速方法
    相机的参数
    深度学习和机器学习的区别
  • 原文地址:https://www.cnblogs.com/by1990/p/1887794.html
Copyright © 2020-2023  润新知