公司的项目中用的 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. 终于将 SQL Server 成功迁移至 MySQL8.0 啦!!!

    之前一直使用 SQL Server 作为主数据库而不是 MySQL ,原因之一是单机 SQL Server 性能比 MySQL 强很多,另一个原因是之前客户的系统管理员大多只有 SQL Server ...

  2. bzoj2437 [Noi2011]兔兔与蛋蛋

    二分图博弈果然都是一个套路,必经点必胜,非必经点必败, 但是肯定不能每走一步就重新建图判断必胜还是必败,那么我们可以这样:每走一步就把这个点删掉,然后find他原来的匹配,如果找不到,就说明他是必经点 ...

  3. bzoj2281 [Sdoi2011]黑白棋

    一眼$nimk$游戏,后来觉得不对劲,看了黄学长博客发现真的不是$nimk$. 就当是$nimk$做吧,那么我们要保证每一位上一的个数都是$d+1$的倍数. $dp$:$f[i][j]$表示从低到高第 ...

  4. BZOJ_2527_[Poi2011]Meteors_整体二分

    BZOJ_2527_[Poi2011]Meteors_整体二分 Description Byteotian Interstellar Union (BIU) has recently discover ...

  5. 通过jQuery和C#分别实现对.NET Core Web Api的访问以及文件上传

    准备工作: 建立.NET Core Web Api项目 新建一个用于Api请求的UserInfo类 public class UserInfo { public string name { get; ...

  6. SSH(Spring4+Struts2+Hibernate4)框架整合

    1.加入Spring4 ①. 加入 jar 包

  7. Spark学习之Spark调优与调试(一)

    一.使用SparkConf配置Spark 对 Spark 进行性能调优,通常就是修改 Spark 应用的运行时配置选项.Spark 中最主要的配置机制是通过 SparkConf 类对 Spark 进行 ...

  8. 从mysql中拿到的数据构造为列表

    最近测试接口遇到一个问题,用python2.7从mysql中取到的数据是元祖类型的,元祖内部的元素也是一个元祖(并且部分元素的编码格式是unicode的): 类似这样: ((10144, u''), ...

  9. Java日志正确使用姿势

    前言 关于日志,在大家的印象中都是比较简单的,只须引入了相关依赖包,剩下的事情就是在项目中“尽情”的打印我们需要的信息了.但是往往越简单的东西越容易让我们忽视,从而导致一些不该有的bug发生,作为一名 ...

  10. Java设计模式小议之1------- 迭代器模式

    定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 这里用一个具体的案例来说明一下迭代器模式的简单使用 我们都知道在商店中,经常要把商品放到书架上,并将商品的 ...