• EntityFramework使用及优化


    1、 简介

      ORM框架Object Relation Mapping,用操作对象的方式来操作数据库

      其它框架Dapper、NHibernate,首推EF,微软官方的。

      EF底层还是ADO.NET实现的。

      EF支持SqlServer、MySQL、Oracle等主流数据库

      使用EF开发数据库有两种形式:先建数据库or先建模型类

      三种模式:DataBase First数据库优先;先建数据库表结构,生成EDM文件

      Model First模型优先;没啥用

      Code First:代码优先;开发者自己写模型生成数据库,没有EDM文件

      DataBase First:简单、方便,但如果是大项目后期会非常痛苦,会出现诸如:修改了数据库在程序中更新EF不起作用等这类问题。

      Code First:门槛高,但适合大项目,微软主推;数据库由EF帮助生成,当修改模型后,EF使用DB Miguration自动帮助修改数据库,但也可以禁用Miguration,手动创建(推荐)

      EF是采用约定大于配置的框架原则的,能遵守约定的就不要去配置

    2、EF的安装

      ①程序中右键新建项

      ②通过NuGet程序集:Install-Package EntityFramework

      会自动在App.config中增加两个entityFramework配置段,如果是MySql数据库还需要添加相关的MySql的驱动。

      在Web.config中配置连接字符串

    <connectionStrings>
    
        <add name="connStr" connectionString="Data source=.;initial catalog=School;user id=sa;password=***;" providerName="System.Data.SqlClient" />
    
      </connectionStrings>

     3、EF简单实体配置DataAnnotations

      数据库建表T_Persons

      程序中实体类Person类

    [Table("T_Persons")]//表名与类名不一样,所以需要特性标注
    
        public class Person{
    
            public long Id { get; set; }
    
     
    
            public string Name { get; set; }
    
     
    
            public DateTime CreateDateTime { get; set; }
    
    }

      备注:因为EF约定主键字段时Id,所以不用再特殊指定Id是主键;主键必须是Id,可以配置但不推荐

      常用特性:必填字段[Required]、限制字段长度[MaxLength(5)]、字段在数据库中有默认值[DatabaseGenerated]、可空字段int?用问号修饰、字段需要用public

    //创建实体类
    
    public class TestDbContext:DbContext{
    
            public TestDbContext()
    
                : base("name=connStr")
    
            {
    
            
    
            }
    
    //通过对Persons操作就可以完成对T_Persons表的操作
    
            public DbSet<Person> Persons { get; set; }
    
    }

      是否using的争议

      不using也没问题,但其实using更好。推荐using

      异常处理:

      InnerException

    4、EF模型的两种配置方式

    ①DataAnnotaions;方便,偶尔度高,上边那种

    ②FluentAPI;微软推荐使用的

    5、FluentAPI使用

    ①sql中建表

    ②C#建模型类

    ③创建PersonConfig类

    using System.Data.Entity.ModelConfiguration;
    
    public class PersonConfig:EntityTypeConfiguration<Person>{
    
            public PersonConfig() {
    
                this.ToTable("T_Persons");
    
            }
    
    }

    ④创建DbContext类

    public class MyContext:DbContext{
    
            public MyContext() : base("name=connStr") {
    
            
    
            }
    
            public DbSet<Person> Persons { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder){
    
                base.OnModelCreating(modelBuilder);
    
        //当前代码所在程序集,加载所有的继承自EntityTypeConfiguration为模型配置类  modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
    
            }
    
    }

    当存在多表时,只要添加多个实体类与DbSet就可以了

    6.、EF的原理及sql监控

    EF会自动把where、OrderBy、Select等这些编译成“表达式树(Expression Tree)”,然后会把表达式树翻译成SQL语句执行。因此不是把数据都取到内存中,然后使用集合的方式进行数据过滤,因此性能不会低。但如果这个操作不能被翻译成sql语句,则会报错,或者被放在内存中操作,性能就会很低。

    查看真正执行的sql是什么样的

    MyContext ctx = new MyContext();
    
                ctx.Database.Log = (sql) =>{
    
                    Console.WriteLine("=============Log============" + sql);
    
                };

    EF的延迟执行

      只有遍历结果集的时候才执行select查询,ToList()内部也是遍历结果集行成List

    各种语句编译后的结果

    Person p=ctx.Persons.First();
    
                p.Name = "hello word";
    
                ctx.SaveChanges();

    Log

     

    EF很智能,知道我只是更新Name字段,而且主动的帮我找到ID;

    值得一提的是如果你重复执行语句,那么EF也很聪明的不会执行该update语句...

     

    ctx.Persons.Where(o => o.Name.StartsWith("baidu")).ToList();

     

    var result = ctx.Persons.Where(p => p.Name.Contains("com"));

    则是%com%

    var result = ctx.Persons.Where(p => p.Name.Length > 5).ToList();

     

    该语句生成的sql相对复杂些,而且字段上涉及到了数据类型转换(将得到的字段值转换成int类型再比较),它的执行效率就比较低。

    var result = ctx.Persons.Where(p => p.CreateDateTime > DateTime.Now).ToList();

     

    long[] ids = { 2, 5, 6 };

    var result = ctx.Persons.Where(p => ids.Contains(p.Id)).ToList();

     

    EF很强大,自动帮助拼接了字符串

     

    IEnumerable<Person> p = ctx.Persons.Where(o => o.Id > 3);

                p=p.Where(o => o.Name.Length > 3);

                p.ToList();

     

    第一次获得IEnumerable<Person> p时,已经把数据加载入内存了,又在内存中操作效率就很低。

    IQueryable<Person> p = ctx.Persons.Where(o => o.Id > 3);

                p=p.Where(o => o.Name.Length > 3);

                p.ToList();

     

    使用IQueryable<Person> 对象时,所有的操作都是在数据库中执行的,最后返回给程序

    如果使用IEnumerable<Person>时,是把所有id>3的都加载到内存中又Linq to Object查询了一次这样非常不好。

     

    EF 是跨数据库的,如果迁移到 MYSQL 上, 就会翻译成 MYSQL 的语法。要配置对应
    数据库的 Entity Framework Provider

     

    细节
    每次开始执行的__MigrationHistory 等这些 SQL 语句是什么?

      DBMigration 用的,也就是由 EF 帮我们建数据库,现在用不到,

    通过下面的代码禁用:
    Database.SetInitializer<XXXDbContext>(null);
    XXXDbContext 就是项目 DbContext 的类名。一般建议放到 XXXDbContext 构造函数中
    注意这里的 Database System.Data.Entity 下的类,不是 DbContext Database 属性。如果写到 DbContext 中,最好用上全名,防止出错。

     

    7、EF执行sql

    在一些特殊场合,需要执行原生 SQL。比如Sqlserver有一些特有的函数,EF就无法使用语句生成,因为EF是垮数据库的,不能支持所有的特性。
    执行非查询语句,调用 DbContext Database 属性的 ExecuteSqlCommand 方法,可以
    通过占位符的方式传递参数:

    ctx.Database.ExecuteSqlCommand("update T_Persons set Name={0},CreateDateTime=GetDate()",
    "baidu.com");

    占位符的方式不是字符串拼接,经过观察生成的 SQL 语句,发现仍然是参数化查询,因此不会有 SQL 注入漏洞。
    执行查询:

    var q1 = ctx.Database.SqlQuery<Item1>("select Name,Count(*) Count from T_Persons where Id>{0} and
    CreateDateTime<={1} group by Name",2, DateTime.Now); //返回值是 DbRawSqlQuery<T> 类型,也是实现了 IEnumerable 接口

    类似于 ExecuteScalar 的操作比较麻烦:
    int c = ctx.Database.SqlQuery<int>("select count(*) from T_Persons").SingleOrDefault();

     

    8、EF中不是所有lambda写法都能被支持

    var result = ctx.Persons.Where(p => Convert.ToString(p.Id)=="3");

    出现System.NotSupportedException异常一般就说明你的写法无法翻译成 SQL 语句。

    想获取创建日期早于当前时间一小时以上的数据
    var result = ctx.Persons.Where(p => (DateTime.Now - p.CreateDateTime).TotalHours>1);
    同样也可能会报System.ArgumentException 类型的未经处理的异常

    也就是说EF使用lambda表达式中不能发生,计算or类型转换操作

    EF中提供了一个SqlServer专用的类,SqlFunctions,这个方法只对Sqlserver数据库支持,对于在EF不支持的函数提供支持;

    比如:var result = ctx.Persons.Where(p =>

    SqlFunctions.DateDiff("hour",p.CreateDateTime,DateTime.Now)>1);

     

    9、EF对象状态管理

    为什么查询出来的对象 Remove()、再 SaveChanges()就会把数据删除。 而自己 new 一个Person()对象,然后 Remove()不行?
    为什么查询出来的对象修改属性值后、再 SaveChanges()就会把数据库中的数据修改。
    因为 EF 会跟踪对象状态的改变。
    EF 中对象有五个状态:

    Detached(游离态,脱离态) 、

    Unchanged(未改变) 、Added(新增) 、Deleted(删除) 、 Modified(被修改)

    状态的装换

     

    Add()、 Remove()修改对象的状态。 所有状态之间几乎都可以通过: Entry(p).State=xxx 的方式进行强制状态转换。状态改变都是依赖于 Id 的( Added 除外)

    应用
      当 SavaChanged()方法执行期间,会查看当前对象的 EntityState 的值,决定是去新增
    Added)、修改( Modified)、删除( Deleted)或者什么也不做( UnChanged)。

    10、EF优化的一个技巧

    如果查询出来的对象只是供显示使用,不会修改、删除后保存,那么可以使用
    AsNoTracking()来使得查询出来的对象是 Detached 状态,这样对对象的修改也还是 Detached状态, EF 不再跟踪这个对象状态的改变,能够提升性能。

    var p1 = ctx.Persons.Where(p => p.Name == "baidu.com").FirstOrDefault();
    Console.WriteLine(ctx.Entry(p1).State);
    //改成:
    var p1 = ctx.Persons.AsNoTracking().Where(p => p.Name == "baidu.com").FirstOrDefault();
    Console.WriteLine(ctx.Entry(p1).State);
    因为 AsNoTracking()是 DbQuery 类( DbSet 的父类)的方法,所以要先在 DbSet 后调用
    AsNoTracking()

    如果确实还想再更新,ctx.Entry().State=System.Data.Entity.EntityState.Unchanged;后在更新即可。

  • 相关阅读:
    Day Five
    Day Four
    JS中attr和prop区别
    layui单选框radio使用form.render() 更新渲染失效的原因
    MySql的时区(serverTimezone)问题
    com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别 serverTimezone设定
    idea使用maven下载jar包,出现证书校验问题问题,unable to find valid certification path to requested target
    java实体类为什么要实现Serializeable 序列化呢?
    IntelliJ IDEA 2017 提示“Unmapped Spring configuration files found.Please configure Spring facet.”解决办法
    JS三个等号"==="是什么意思
  • 原文地址:https://www.cnblogs.com/cuijl/p/6572589.html
Copyright © 2020-2023  润新知