公司的项目中用的 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<EFDbContext>());

或者在 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

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

MySQL via EF6 的试用报告的更多相关文章

  1. 活动助手Beta用户试用报告

    用户试用报告 1.面向参与者用户 1.1 日常参加各类学习(水综测)活动中,有没有遇到以下问题: (1) 信息来源混乱,不知道靠不靠谱 (2) 每次报名都要重新填写自己的学号手机号,有时候填错了就没综 ...

  2. 【鸿蒙开发板试用报告】用OLED板实现FlappyBird小游戏(中)

    小伙伴们久等了,在上一篇<[开发板试用报告]用OLED板实现FlappyBird小游戏(上)>中,我们本着拿来主义的原则,成功的让小鸟在OLED屏幕上自由飞翔起来,下面我们将加入按钮交互功 ...

  3. Visual Studio 2013 中 mysql 使用 EF6

    1.web.config <configSections> <!-- For more information on Entity Framework configuration, ...

  4. 实战MySQL集群,试用CentOS 6下的MariaDB-Galera集成版

    说起mysql的集群估计很多人会首先想起mysql自带的replication或者mysql-mmm.mysql-mmm其实也是基于mysql自带的replication的,不过封装的更好用一些,但是 ...

  5. windows的mysql无法启动 服务没有报告任何错误

    相信很多人都遇到过安装Mysql的时候出现各种各样的问题,今天小编就教大家解决window下mysql服务没有报告任何错误的情况下无法启动 的问题.本文所用的mysql版本是5.7以上版本,解决方法: ...

  6. Dephi 10.3.3试用报告

    官方没有正式发布,但出了下载及keygen,具体内容我在这篇内容写了:Delphi 10.3.3最新消息 . 也可以去Delphi多层开发交流QQ群:209321818找相关的keygen. 今早来就 ...

  7. Visual Studio 2015 & C#6.0 试用报告,持续更新。

    昨天早上看到了.net开源的消息,我是非常兴奋的,毕竟局限于Windows的.NET经常被人唾弃.VB暂且不提,C#常年被人指责跨平台性不佳,我也是无能为力.即使有Mono等第三方跨平台工程,.NET ...

  8. kbmMW 5.08.10试用报告

    1.不兼容Android 基于5.07的项目,升级到5.08,不能编译android app.已经反应给作者.作者回复将近快发布fixed,修正这个问题及其他发现的问题. 5.08.01解决了andr ...

  9. 项目Beta冲刺 用户试用报告

    课程: 软件工程1916|W(福州大学) 作业要求: 项目Beta冲刺 团队名称: 火鸡堂 作业目标: 火鸡堂 队员学号 队员姓名 博客地址 备注 221600111 彼术向 http://www.c ...

随机推荐

  1. fasthttp 的 goroutine pool 实现探究

    引言 fasthttp是一个非常优秀的web server框架,号称比官方的net/http快10倍以上.fasthttp用了很多黑魔法.俗话说,源码面前,了无秘密,我们今天通过源码来看一看她的gor ...

  2. 1.1 为什么要使用lambda 表达式

    第1章 lambda 表达式 1.1 为什么要使用lambda 表达式 1.2 lambda 表达式的语法 1.3 函数式接口 1.4 方法引用 1.5 构造器引用 1.6 变量作用域 1.7 默认方 ...

  3. MFC中ComboBox控件用法

    MFC ComboBox 一.入门篇 ComboBox (组合框)控件很简单,可以节省空间.从用户角度来看,这个控件是由一个文本输入控件和一个下拉菜单组成的.用户可以从一个预先定义的列表里选择一个选项 ...

  4. loj536 「LibreOJ Round #6」花札

    一眼二分图博弈,于是我们可以拿到69分的好成绩. 二分图暴力加边的数目是O(n^2)的,于是我们考虑网络流优化建图,将alice的每个牌向其的颜色和编号节点连边,bob的每个牌由其颜色和编号节点向其连 ...

  5. BZOJ_3239_Discrete Logging_BSGS

    BZOJ_3239_Discrete Logging_BSGS 题意:Given a prime P, 2 <= P < 231, an integer B, 2 <= B < ...

  6. BZOJ_4813_[Cqoi2017]小Q的棋盘_dfs

    BZOJ_4813_[Cqoi2017]小Q的棋盘_dfs Description 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能 在有连线的格 ...

  7. 如何在ST官网下载STM32固件库

    1.首先要注册一个ST账号,下载的时候需要. 2.找到工具与软件 3.进去之后选"产品列表" 4.在产品列表里选STM32微控制器软件,直接点下图标号2,不要点左边的加号 5.进去 ...

  8. common-pool2连接池详解与使用

    我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等.在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响.一种 ...

  9. jquery中attr()和prop()的区别

    最近项目回归使用jquery,页面渲染全是使用jquery做的,所以做的时候也遇到了许多以前没有见过的问题,如这次操作[radio]控件的"checked"属性时有遇到问题, $( ...

  10. 深入理解java虚拟机之java内存区域

    java虚拟机在执行java程序的时候会把它所管理的内存分为多个不同的区域,每个区域都有不同的作用,以及由各自的生命周期,有些随着虚拟机进行的启动而存在,有些区域则依赖于用户线程的启动或结束而建立或销 ...