MySQL via EF6 的试用报告

  1. 云栖社区>
  2. 博客>
  3. 正文

MySQL via EF6 的试用报告

优惠码大使 2019-04-22 15:09:02 浏览814
展开阅读全文

MySQL via EF6 的试用报告
1、如何通过 EF6 来连接 MySQL?
2、如何通过 EF6 来实现 CRUD?
2.1、Create 添加
2.2、Retrieve 查询
2.3、Update 修改
2.4、Delete 删除
3、如何更好的运用 EF6 来完成工作?
3.1、传说中 EF 的三种模式
3.2、EF6 执行原生 SQL 查询
3.3、EF6 执行原生 SQL 增删改
3.4、EF6 不推荐的 CRUD 写法
3.5、EF6 性能优化
3.6、EF6 开发及调试技巧
4、总结
4.1、MySQL 官方组件的用途说明
4.2、本文 Demo 的代码补充说明
公司的项目中用的 ORM 是 Dapper,代码中充斥着大量的 SQL 语句,为了少写 SQL 语句,领导让我把 EF6 也加进去看会不会有问题。按照指示,我在新的代码分支引入了 EF6 并做了 CRUD 的测试,结论是混合使用 Dapper 和 EF6 没问题。为了让团队中没用过 EF 的同事也能快速上手 EF,我把我的试用记录重新整理了一下,于是乎就有了本文。

1、如何通过 EF6 来连接 MySQL?
1、安装 MySQL 的 .NET 驱动

要在 .NET 项目中连接 MySQL 首先得安装 MySQL 的 .NET 驱动。这个驱动是向下兼容的,官方下载地址:MySQL Connector/NET。

2、安装 MySql.Data.EntityFramework

Install-Package MySql.Data.EntityFramework -Version 8.0.15
上面的 NuGet 命令会自动帮你把 EF6 和 MySql.Data 都安装好,无需额外再安装。

3、创建模型类

有了和数据库中表对应的模型类,才能方便的操作数据库而不必写 SQL 语句。如定义一个 Person 实体,示例如下:

[Table("person")] // 这里不仅可以自定义表的 Name 还可以自定义表的 Schema
public class Person {

[Key]
public Int32 ID { get; set; }
public String Name { get; set; }
public DateTime Birthday { get; set; }
public Int32 NationID { get; set; }
public Nation Nation { get; set; }

}
定义实体的注意事项:

1、模型类名与表名不必相同。如果不同,则需要用 TableAttribute 标注一下;如果相同,则可以省略该 Attribute。
2、主键名不必非得是 ID。如果不是,则需要用 KeyAttribute 标注一下;如果是 ID,则可以省略该 Attribute。EF 遵循“约定大于配置”的开发原则,比如 EF 中主键名默认为 ID 就是 EF 的一个内置约定,EF 还支持自定义约定。
4、创建数据库上下文类

有了数据库上下文,就可以连接数据库了,然后在上下文中定义相应的 DbSet(实体对象集合),就能直接对数据库进行 CRUD 操作了。如创建一个 Demo 的上下文,示例如下:

public class DemoDbContext : DbContext {

// 声明 DbSet,实现 CRUD 的方法定义在 DbSet 中
public DbSet<Person> Persons { get; set; }
public DbSet<Nation> Nations { get; set; }

public DemoDbContext() : base("name=ConnectionString") {
    // 关闭迁移,EF Code First 默认会在 Model 发生改变后自动更新数据库
    Database.SetInitializer<DemoDbContext>(null);
}

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    base.OnModelCreating(modelBuilder);
    // 解决表名变复数的问题,EF 生成 SQL 语句时默认会将实体名变成复数
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}

}
定义上下文的注意事项:

1、创建的数据库上下文类必须继承 DbContext 类。
2、在上下文类的构造函数中通过 base 的方式指定数据库连接字符串。base 的参数写法有多种,常见的写法如下:
base("ConnectionString")
base("name=ConnectionString")
base(new MySqlConnection("..."), false)
3、由于 EF 的迁移功能过于复杂,且非必要,一般不用,在构造函数中关闭即可。
4、EF 默认生成的表名是 Model 名的复数,可在 OnModelCreating 中移除该转换规则。
2、如何通过 EF6 来实现 CRUD?
2.1、Create 添加
1、向一个表中添加一条数据,示例如下:
using (var context = new DemoDbContext()) {

var p = new Person() { Name = "Andy", Gender = 1 };
context.Persons.Add(p);
context.SaveChanges(); // 返回受影响行数 1

}
上面的代码会生成 1 条 INSERT 语句和 1 条 SELECT 语句。

2、同时向存在主外键的两个表中添加一条数据,示例如下:
using (var context = new DemoDbContext()) {

var n = new Nation() { Name = "China" };
var p = new Person() { Name = "Mark", Gender = 1, NationID = n.ID };
context.Nations.Add(n);
context.Persons.Add(p);
context.SaveChanges(); // 返回受影响行数 2

}
上面的代码会生成 1 条 INSERT 语句和 2 条 SELECT 语句。

3、一次添加多个并附加事务:
String connectionString = "server=localhost;port=3306;database=demo;uid=root;pwd=";
using (MySqlConnection connection = new MySqlConnection(connectionString)) {

connection.Open();
MySqlTransaction transaction = connection.BeginTransaction();

try {
    using(var context = new DemoDbContext(connection)) {
        context.Database.UseTransaction(transaction);

        List<Person> ps = new List<Person>();
        ps.Add(new Person { Name = "Mark",  Gender = 1 });
        ps.Add(new Person { Name = "Jack", Gender = 1 });
        ps.Add(new Person { Name = "Tom", Gender = 1 });

        context.Persons.AddRange(ps);
        context.SaveChanges();
    }

    transaction.Commit();                    
} catch {
    transaction.Rollback();
    throw;
}

}
2.2、Retrieve 查询
1、EF 查询支持 LINQ 写法,必须在最后调用ToList()才会执行查询,示例如下:
using (var context = new DemoDbContext()) {

context.Database.Log = Console.WriteLine;
var list1 = (from p in context.Persons where p.ID == 1 select p).ToList();
var list2 = (from p in context.Persons select p.Name).ToList();                
var query = from p in context.Persons select p;
query = from p in query where p.ID >= 1 select p;
query = from p in query where p.NationID == 1 select p;
query = from p in query orderby p.Name descending select p;
query.ToList();

}
2、EF 查询支持 Lambda 写法,示例如下:
using (var context = new DemoDbContext()) {

context.Database.Log = Console.WriteLine;
// LIMIT 1
var p1 = context.Persons.FirstOrDefault();
// LIMIT 2,不会做参数化处理
var p2 = context.Persons.Single(p => p.ID == 5);
// LIMIT 2,会自动做参数化处理
var p3 = context.Persons.Find(3);
// 会自动做参数化处理
var p4 = context.Persons.Where(p => p.Name.Contains("Andy")).ToList();
// 只查询部分数据行,可用这个实现分页查询
var p5 = context.Persons.OrderBy(p => p.Name).Skip(3).Take(5).ToList();
// 带条件的分页查询
var p6 = context.Persons.Where(p => p.ID > 0).OrderBy(p => p.Name).Skip(3).Take(5).ToList();

}
3、查询关联数据,示例如下:
using (var context = new DemoDbContext()) {

var persons = context.Persons.Include(p => p.Nation).ToList();

}
上面的代码会生成 1 条内连接 SELECT 语句。

2.3、Update 修改
1、修改一条确定存在的数据时,用如下语句:
using (var context = new DemoDbContext()) {

var p = new Person() { ID = 3, Name = "Andy" };
context.Persons.Attach(p);
context.Entry(p).Property(i => i.Name).IsModified = true;
context.SaveChanges(); // 返回受影响行数

}
上面的代码会生成 1 条 UPDATE 语句,数据不存在时会报错。

2、如果需要确认数据存在后再修改的话,用如下语句:
using (var context = new DemoDbContext()) {

var p = context.Persons.Find(1); // 也可以用 FirstOrDefault 或其它查询方法
if (p != null) {
    p.Name = "Peter";
    context.Persons.Attach(p);
    context.Entry(p).Property(i => i.Name).IsModified = true; // 指定更新字段
    context.SaveChanges(); // 返回受影响行数
}

}
上面的代码会生成 1 条 UPDATE 语句和 1 条 SELECT 语句。

2.4、Delete 删除
1、删除一条确定存在的数据时,用如下语句:
using (var context = new DemoDbContext()) {

var p = new Person() { ID = 1 };
context.Persons.Attach(p);
context.Persons.Remove(p);
context.SaveChanges(); // 返回受影响行数

}
上面的代码会生成 1 条 DELETE 语句,数据不存在时会报错。

2、如果需要确认数据存在后再删除的话,用如下语句:
using (var context = new DemoDbContext()) {

var p = context.Persons.FirstOrDefault(it => it.ID == 1);
if (p != null) {
    context.Persons.Attach(p);
    context.Persons.Remove(p);
    context.SaveChanges();
}

}
3、如何更好的运用 EF6 来完成工作?
技术好的人经常讲业务场景,相反,有些技术差的人却喜欢不由分说的吐槽那些他根本就没搞懂的技术。在 .NET 圈子里,有人对 EF 是爱不释手,也有人对 EF 是各种吐槽。

我很喜欢的一句话是:“没有不好的技术,只有没被用好的技术”,我的理解是任何技术都有局限性,作为程序员,我们要做的是结合实际业务场景来选用最合适的技术。要想在项目中更好的运用 EF,就得更多的了解 EF 技术,本节就来分享一下我试用 EF6 过程中的一些收获。

3.1、传说中 EF 的三种模式
为什么说 EF 的三种模式是传说呢?因为新版的 EF 默认只支持 Code First 这一种模式了。要想用 Database First 或 Model First 还得把 Visual Studio 降级到 VS10 或 VS12 才行,实在没必要,下面简单罗列下每种模式的特点:

1、Database First:即数据库优先,先创建好数据库和表,然后自动生成 EDM(实体数据模型)文件,再由 EDM 文件生成模型类。当现有数据库结构比较成熟稳定时,可用这种模式实现快速开发。
2、Model First:即模型优先,先创建可视化的 EDM 文件,然后由 EDM 文件来自动生成模型类和数据库。开发速度快,但代码冗余。写个小 Demo 还行,但企业级开发一般没人用这个模式。
3、Code First:即代码优先,先写好模型类,然后自动生成数据库,没有 EDM 文件。代码简洁可控,也是官方和业界首推的模式。
3.2、EF6 执行原生 SQL 查询
总会有些时候,我们为了性能或者其它各种各样的缘故,而不得不写 SQL 语句,EF 提供了直接执行 SQL 语句的方法SqlQuery()。

1、执行无参数的原生 SQL 查询,示例如下:
using (var context = new DemoDbContext()) {

var persons = context.Persons.SqlQuery("SELECT * FROM Person").ToList();

}
2、执行带参数的原生 SQL 查询,示例如下:
using (var context = new DemoDbContext()) {

var sql = "SELECT t.* FROM Person t WHERE t.Gender=@Gender";
var p1 = context.Persons.SqlQuery(sql, new MySqlParameter("@Gender", 1)).ToList();
// 下面这种更简单的写法相当于上面两句,EF 会自动将其转换为参数化查询
var p2 = context.Persons.SqlQuery("SELECT t.* FROM Person t WHERE t.Gender={0}", 1).ToList();

}
3、只查询部分可选字段,示例如下:
using (var context = new DemoDbContext()) {

var persons = context.Database.SqlQuery<MiniPerson>("SELECT t.ID,t.Name FROM Person t").ToList();

}
注意:这里用的是MiniPerson类,而不是模型类Persons,因为用模型类时,查询返回的字段必须与其模型中的字段对应,而用非模型类时则没有这个限制,EF 会自动把值赋给相应的字段,并忽略其它字段,即便完全不匹配也不会报错。

4、统计表中的数据条数,示例如下:
using (var context = new DemoDbContext()) {

var count = context.Database.SqlQuery<Int32>("SELECT COUNT(1) FROM Person").SingleOrDefault();

}
其实 EF 的SqlQuery()还支持调用存储过程,但实际开发中,一般最好不要存储过程。因为一旦用了存储过程,相比较得到的性能提升,往往付出的维护代价会更大,得不偿失。

3.3、EF6 执行原生 SQL 增删改
EF6 调用增删改等命令语句的方法是ExecuteSqlCommand(),示例如下:

using (var context = new DemoDbContext()) {

context.Database.ExecuteSqlCommand("INSERT INTO Person VALUES(DEFAULT,'小明',NOW(),1)");
context.Database.ExecuteSqlCommand("UPDATE Person SET Name='小王' WHERE ID=8");
context.Database.ExecuteSqlCommand("DELETE FROM Person WHERE ID=14");

}
一般用 EF 就是为了不写 SQL 语句,尤其是大多数时候不会造成性能问题的增删改语句,所以使用ExecuteSqlCommand()的概率是比较低的。

3.4、EF6 不推荐的 CRUD 写法
有些朋友通过别人的帖子发现直接更改实体状态也能修改数据,然后就一直这么用。但如果你不是很了解 EF 的实体状态管理机制,就很可能会给自己挖坑,所以一般不推荐这种 CRUD 的写法。

我多次看到网上有人问诸如 EF 改了数据保存报错之类的问题,基本都是他自己还没搞清楚 EF 各个实体状态的含义,然后就在那儿强制更改实体状态,然后遇到坑自己还解决不了。这种做法有可能还会破坏 EF 的乐观并发控制,而且有些版本也不支持这种做法。下面给出两个负面案例:

1、不推荐的修改写法,会更新所有字段,示例如下:
using (var context = new DemoDbContext()) {

context.Database.Log = Console.WriteLine;
var p = new Person() { ID = 3, Name = "Andy" };
context.Entry(p).State = EntityState.Modified;
context.SaveChanges(); // 返回受影响行数 1

}
上面的代码会生成 1 条 UPDATE 语句。

2、不推荐的删除写法,示例如下:
using (var context = new DemoDbContext()) {

var p = new Person() { ID = 1 };
context.Entry(p).State = EntityState.Deleted;
context.SaveChanges(); // 返回受影响行数 1

}
上面的代码会生成 1 条 DELETE 语句。

3.5、EF6 性能优化
1、非跟踪查询 AsNoTracking
默认情况下,EF 会一直跟踪实体的状态,这也是为什么当我们调用SaveChanges()的时候,EF 能够把最终的数据状态准确提交到数据库的原因。但有些时候,我们查询出数据只是为了做展示,并不需要修改或删除,这时候就可以调用AsNoTracking()来使得对象为 Detached 状态,之后 EF 就不再跟踪这个对象状态了,在合适的场景下能显著提升性能。
using (var context = new DemoDbContext()) {

// 查询所有人并且不跟踪他们的状态
var p1 = context.Persons.AsNoTracking().ToList();
// 查询部分人并且不跟踪他们的状态
var p2 = context.Persons.Where(i => i.NationID == 1).AsNoTracking().ToList();

}
2、EF 默认是开启了 LoayLazy 的,别手贱关了就行。如下是默认配置:
this.Configuration.ProxyCreationEnabled = true;
this.Configuration.LazyLoadingEnabled = true;
3.6、EF6 开发及调试技巧
1、如果想知道 EF 会执行什么 SQL 语句,比如是控制台项目,在执行代码块中增加如下语句即可:
context.Database.Log = Console.WriteLine;
2、如果是自己测试,可以让 EF 每次都根据代码更新数据库,在上下文构造函数中增加如下代码即可:
// 当数据库模型发生改变时,则删除当前数据库,重建新的数据库(实际开发中永远不要这么写,太危险了)
Database.SetInitializer(new DropCreateDatabaseIfModelChanges());
或者在 CRUD 代码块中加入如下代码,仅当数据库不存在时,才由 EF 创建数据库:

context.Database.CreateIfNotExists();
4、总结
本文主要讲解了如何快速上手 EF6 和基本的 CRUD 操作。用 .NET 技术的博友都知道,如今 .NET 阵营除了经典的 .NET Framework 之外,还有一个开源版的 .NET Core。对应的,EF 也适时地推出了 EF Core 版,如果你的项目是 .NET 的,那就继续用 EF6 吧,毕竟是久经考验的版本,而 EF Core 是全新开发的,更适合 .NET Core 类型的项目。而且官方也说从 EF6 到 EF Core 是移植而不是升级。

4.1、MySQL 官方组件的用途说明
1、mysql-connector-net:MySQL Connector/NET 是 MySQL 官方的 .NET 驱动程序,或者说是 MySQL for .NET 的客户端开发包,其中包含了 .NET 连接 MySQL 所必须的 dll 文件。
2、mysql-for-visualstudio:6.7 以下版本的驱动中会包含该组件,它的作用是在通过 VS 建立实体模型时,在数据源中增加 MySQL 类型选项。如果只用 Code First,那么就不需要该组件了。
3、mysql-connector-odbc:MySQL Connector/ODBC 使得用户可以通过 ODBC(Open Database Connectivity,开放数据库互联)来连接 MySQL 服务器。
4.2、本文 Demo 的代码补充说明
文中的 Nation 实体定义如下:
public class Nation {

public Int32 ID { get; set; }
public String Name{ get; set; }

}
文中的 MiniPerson 类定义如下:
public class MiniPerson {

public Int32 ID { get; set; }
public String Name { get; set; }

}
本文链接:http://www.cnblogs.com/hanzongze/p/ef6-trial-report.html
版权声明:本文为博客园博主 韩宗泽 原创

网友评论

作者关闭了评论
优惠码大使
+ 关注