博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MySQL via EF6 的试用报告
阅读量:5342 次
发布时间:2019-06-15

本文共 10837 字,大约阅读时间需要 36 分钟。

公司的项目中用的 ORM 是 Dapper,代码中充斥着大量的 SQL 语句,为了少写 SQL 语句,领导让我把 EF6 也加进去看会不会有问题。按照指示,我在新的代码分支引入了 EF6 并做了 CRUD 的测试,结论是混合使用 Dapper 和 EF6 没问题。为了让团队中没用过 EF 的同事也能快速上手 EF,我把我的试用记录重新整理了一下,于是乎就有了本文。

1、如何通过 EF6 来连接 MySQL?

1、安装 MySQL 的 .NET 驱动

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

2、安装 MySql.Data.EntityFramework

Install-Package MySql.Data.EntityFramework -Version 8.0.15

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

3、创建模型类

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

[Table("person")] // 这里不仅可以自定义表的 Name 还可以自定义表的 Schemapublic 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
Persons { get; set; } public DbSet
Nations { get; set; } public DemoDbContext() : base("name=ConnectionString") { // 关闭迁移,EF Code First 默认会在 Model 发生改变后自动更新数据库 Database.SetInitializer
(null); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 解决表名变复数的问题,EF 生成 SQL 语句时默认会将实体名变成复数 modelBuilder.Conventions.Remove
(); }}

定义上下文的注意事项:

  • 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
ps = new List
(); 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
("SELECT t.ID,t.Name FROM Person t").ToList();}

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

  • 4、统计表中的数据条数,示例如下:
using (var context = new DemoDbContext()) {    var count = context.Database.SqlQuery
("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

版权声明:本文为博客园博主 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!个人博客,能力有限,若有不当之处,敬请批评指正,谢谢!

转载于:https://www.cnblogs.com/hanzongze/p/ef6-trial-report.html

你可能感兴趣的文章
mysql的数据备份和还原 记录
查看>>
noip模拟题——单词分类
查看>>
sql server安装部署,采用Yum方式
查看>>
springboot-quartz 实现动态添加,修改,删除,暂停,恢复等功能
查看>>
【花神的数论题】
查看>>
python 网络基础 之 网络协议
查看>>
git教程
查看>>
Cleaning up old NVIDIA driver files
查看>>
自己也是一个21岁的大人了,需要有点责任感了
查看>>
let,const,var三者之间的区别
查看>>
nc+管道符的一种反弹shell姿势
查看>>
记一次Burpsuite安装SQLmap的sqlipy的小坑
查看>>
[Django学习]入门
查看>>
Stm32启动文件选择
查看>>
【汉诺塔问题】汉诺塔问题系列
查看>>
[转]Windows下Python多版本共存
查看>>
Java ExecutorService四种线程池的例子与说明(转发)
查看>>
邮件配置之spf word 发布
查看>>
40.编写简单的正则表达式
查看>>
SpringBoot修改Servlet相关配置
查看>>