webapi框架搭建系列博客

为什么用ef?

  我相信很多博友和我一样都有这种“选择困难症”,我曾经有,现在也有,这是技术人的一个通病——总想用“更完美”的方式去实现,导致在技术选择上犹豫不决,或总是推翻别人的技术路线,甚至屡屡推翻自己从前的想法,这种专研的精神固然不错,但随着年龄的增大,会发现这种习惯已将自己弄得很累,其实真没有必要。我觉得技术上永远没有“完美”的解决方案,如果揪着缺点去比较和选择,不管最终选择了什么,以后都会后悔。因为你总是看到它身上的缺点。orm的框架也有很多,大家都说entityframework性能有点差,它生成的sql语句简直看不下去,我也考虑过dapper,但它要写一些sql语句,这不是我想干的事(纯属个人代码风格偏好)。分享下为什么会选择ef。我的orm框架的要求是这样的:1、简单快速上手(因为我不太想将太多的精力花在这上面,而ef各方法的资料还是很全的);2、性能只要过得去就行(又不是要开发对性能要求很苛刻的产品,我想微软的产品也不至于性能很低吧);3、后面如果要换数据库,最好不要改代码(这点是我喜欢ef的主要原因,只要将数据连接换一下,dll包换一下,数据库就可以从mysql,sqlserver,oracle任意切换)

为什么用code first?

  1、代码简洁

    相比db first,code first的代码更简洁,基本所有的代码都是真正需要的。而db first有很多自动生成的辅助型的代码。

  2、编写简单

    db first在sqlserver下也简单,但如果是mysql或是oracle,在vs的配置上就会遇到很多问题,我初学时经常遇到vs连mysql失败,或是连接成功后总是生成代码时失败。而用code first,你只要写实体的代码就行,虽然是”多写了一些代码“,但其实速度上比db first还是快的。

参考资料

  推荐:http://www.entityframeworktutorial.net/code-first/entity-framework-code-first.aspx

  微软官方:https://msdn.microsoft.com/en-us/library/aa937723(v=vs.113).aspx

下面介绍如何实用

用法和步骤

引用相关包

  如果是sqlserver数据库,只要引用entityframework就行

  如果是mysql数据库,引用mysql.data.entity包(依赖mysql.data包)

    注意:在实际开发中发现有些版本的mysql.data.entity包是有bug的,如版本6.10.5,建议安装6.9.10(测试没有问题)

  如果是oracle数据库,引用oracle.managedDataAccess.EntityFramework包(依赖Oracle.ManagedDataAccess包)

    oracle注意:微软也出过oracle的连接库,但不支持ef,现在都提倡用oracle出的odp.net技术。而odp.net以前的版本是基于oracle.dataaccess.dll的,这个有x86和x64位之分,开发时很不方便,建议用oracle.managedDataAccess.dll.

  引用包后,会在web.config里自动生成如下相应的配置信息:

  1)在configuration--》configSections节点下面生成名为entityFramework的section

    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />

  2)

编写实体类

  如下代码

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
[Table("Test")]
public class TestTable
{
[Key,Column(TypeName = "varchar"),MaxLength(50)]
public string Id { set; get; }
[Column(TypeName = "int")]
public int? Age { set; get; }
[Column(TypeName = "datetime")]
public DateTime? CreateDateTime { get; set; }
}
}

  实体通过dataannotations的方式描述在数据库里类型,长度、主键等。没有必要死记他们的写法,不会时参考http://www.entityframeworktutorial.net/code-first/stringlength-dataannotations-attribute-in-code-first.aspx就行。

 编写数据库上下文

  1)创建继承自DbContext的类

using System.Data.Entity;

namespace webapi.Entities
{
public class DB : DbContext
{
/// <summary>
/// name=DBConnection,DBConnection为数据库连接的名字,即web.config配置文件节点connectionStrings,name值为DBConnection的数据库连接字符串
/// </summary>
public DB()
: base("name=DBConnection")
{
} #region 配置所有的数据库表 public DbSet<TestTable> TestTables { set; get; } #endregion }
}

  2)在web.config里配置数据库连接字符串

  由于数据库连接字符串比较重要,为方便,在实际的开发中经常单独配置在一个文件里,然后在web.config里去引用配置文件。

  创建ConnectionStrings.config文件,内容如下

<?xml version="1.0" encoding="utf-8"?>
<connectionStrings>
<!--sqlserver连接字符串-->
<!--<add name="DBConnection" providerName="System.Data.SqlClient" connectionString="Data Source = localhost; Initial Catalog = WebApiFramework; User Id = sa; Password = 1" />-->
<!--mysql连接字符串-->
<add name="DBConnection" providerName="MySql.Data.MySqlClient" connectionString="Data Source=localhost;Initial Catalog=webapi;User id=root;Password=root;charset=utf8;port=3306;" />
<!--oracle连接字符串-->
<!--<add name="DBConnection" providerName="Oracle.ManagedDataAccess.Client" connectionString="Data Source=localhost;User Id=system;Password=root;" />-->
</connectionStrings>

  上面将常用的数据库的连接方式举例出来,以后要换数据库,只要按一下数据库的连接字符串就行。

编写测试接口

  code first的基本来法就上面三步了。我们来测试下是否正常。

  1)创建测试接口,代码如下:

using System.Linq;
using System.Web.Http;
using webapi.Entities; namespace webapi.example
{
public class EFTestController : ApiController
{
public IHttpActionResult Get()
{
using (DB db=new DB())
{
var list = db.TestTables;
return Ok(list.ToList());
}
}
}
}

  2)运行接口

  用postman访问接口地址(get方法):http://localhost:101/api/EFTest,返回结果为空json数组:[];

  注意:我的电脑上已经安装了mysql,但并没有webapi数据库啊,为什么接口正常运行呢?原因是entityframework会自动检测是否存在数据库,如果没有就会去创建。下面是ef自动创建的数据库

  注意到数库里还自动生了了”_MigrationHistory"的表,它有什么用处呢?先说下几个例子

  (1)如果现在在TestTable实体里增加一个数据库对应的字段,或是在数据库表里增加一个字段,编译后再访问上面同样的接口,会出现下面的错误

{
"Message": "The model backing the 'DB' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)."
}

   (2)如果在(1)的操作后,删除了_MigrationHistory表再访问接口,同样会报错,但错误的内容已经变成如下

    Unknown column 'Extent1.AddColumn' in 'field list'

   (3)如果在(2)的操作后,删除TestTable实体在(1)中增加的字段,再次访问接口时会运行成功

  _MigrationHistory作用总结:用于比较model和数据库的版本是否一致,如果不一致则报(1)中的错误,不会执行sql语句;如果这个表没有,就不会进行model和数据库的版本检测,直接执行ef生成的sql语句。code first在目标数据库没有的情况下会创建数据库,并创建_MigrationHistory表,也可以用于连接已经存在数据库(此时目标数据库里可能没有_MigrationHistory),但这时要确保自己的实体和目标数据库的表是一样的。

增加Migration机制

  Migration即是将model的更改应用到数据库里,分自动和手动两种机制,具体可参考http://www.entityframeworktutorial.net/code-first/migration-in-code-first.aspx。

  1)在vs的程序包管理器控制台里运行enable-migrations –EnableAutomaticMigration:$true

  如果是mysql数据库,可能会出现如下错误:

Checking if the context targets an existing database...
No MigrationSqlGenerator found for provider 'MySql.Data.MySqlClient'. Use the SetSqlGenerator method in the target migrations configuration class to register additional SQL generators.

  原因:codefirst默认是生成sqlserverr的sql语句,要在migrations的配置代码里改成用mysql 。此时在项目里已经成功生成了Migrations文件夹,里面有个Configurations.cs类,修改Configurations.cs代码,如下

using System.Configuration;

namespace webapi.Migrations
{
using System.Data.Entity.Migrations; internal sealed class Configuration : DbMigrationsConfiguration<webapi.Entities.DB>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;//自动更新数据库
AutomaticMigrationDataLossAllowed = true;//重命名和删除表字段时会丢失数据,设置成允许,否则此情况下同步数据库会出错
var providerName = ConfigurationManager.ConnectionStrings["DBConnection"].ProviderName;
if (providerName == "MySql.Data.MySqlClient")
{
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());//如果数据库用mysql,加上这一句
}
} protected override void Seed(webapi.Entities.DB context)
{
// This method will be called after migrating to the latest version. // You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data.
}
}
}

如果AutomaticMigrationDataLossAllowed不设置成true(默认为false),重命名实体的属性名是会出现如下错误

Automatic migration was not applied because it would result in data loss. Set AutomaticMigrationDataLossAllowed to 'true' on your DbMigrationsConfiguration to allow application of automatic migrations even if they might cause data loss. Alternately, use Update-Database with the '-Force' option, or scaffold an explicit migration.

 

 2)修改数据库上下文的配置

using System.Data.Entity;

namespace webapi.Entities
{
public class DB : DbContext
{
/// <summary>
/// name=DBConnection,DBConnection为数据库连接的名字,即web.config配置文件节点connectionStrings,name值为DBConnection的数据库连接字符串
/// </summary>
public DB()
: base("name=DBConnection")
{
// 默认策略为CreateDatabaseIfNotExists,即如果数据库不存在则创建,用migrations时改成MigrateDatabaseToLatestVersion,即每次第一次访问数据库时同步最新的数据库结构
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DB, webapi.Migrations.Configuration>("DBConnection"));
} #region 配置所有的数据库表 public DbSet<TestTable> TestTables { set; get; } #endregion }
}

  加入migrations机制后,如果在实体里改变了实体的结构,在第一次网站访问时就会自动去更新数据库的结构,非常方便。

经验:在开发时要避免手动去改数据库的结构,这样会导致各种migrations失败的错误,code first,顾名思义就是让我们以code为先,我们通常修改code的实体间接的去同步数据库结构。手动改数据库那是db first的思想。在项目正式上线还是要慎用migration机制,最好是将AutomaticMigrationDataLossAllowed还原为默认false,不然一个实体的重命名会导致数据库表的数据丢失,切记切记。

webapi框架搭建-数据访问ef code first的更多相关文章

  1. webapi框架搭建系列博客

    webapi框架搭建系列博客 webapi框架搭建-创建项目(一) webapi框架搭建-创建项目(二)-以iis为部署环境的配置 webapi框架搭建-创建项目(三)-webapi owin web ...

  2. webapi框架搭建-创建项目(三)-webapi owin

    上一篇:创建项目(二) 在上一篇里,我们已经创建好了webapi应用,并已经部署到iis里,本篇讲如何用owin自宿主或是iis宿主来部署webapi应用. owin介绍 传统的asp.net网站只能 ...

  3. webapi框架搭建-创建项目(二)-以iis为部署环境的配置

    上篇:webapi快速框架搭建-创建项目(一) 在"创建项目(一)"这一篇里已经创建了一个空的项目,但项目上什么都没有,本篇描述如何将webapi配置成部署在iis上. 步骤 用n ...

  4. webapi框架搭建-日志管理log4net

    前言 本篇讲怎么在前几篇已经创建好的项目里加上日志处理机制,我们采用Log4net技术.跟多的log4net技术的细节请查阅log4net的官网. log4net官网:http://logging.a ...

  5. webapi框架搭建-安全机制(四)-可配置的基于角色的权限控制

    webapi框架搭建系列博客 在上一篇的webapi框架搭建-安全机制(三)-简单的基于角色的权限控制,某个角色拥有哪些接口的权限是用硬编码的方式写在接口上的,如RBAuthorize(Roles = ...

  6. webapi框架搭建-安全机制(三)-简单的基于角色的权限控制

    webapi框架搭建系列博客 上一篇已经完成了“身份验证”,如果只是想简单的实现基于角色的权限管理,我们基本上不用写代码,微软已经提供了authorize特性,直接用就行. Authorize特性的使 ...

  7. webapi框架搭建-安全机制(一)

    本系列博客链接:webapi框架搭建系列博客 前言 webapi接口是开放给外部使用的,包括接口的地址,传参的规范,还有返回结果的说明.正因为接口的开放性,使得接口的安全很重要.试想一下,用抓包工具( ...

  8. NHibernate:教你如何搭建数据访问层?

    NHibernate:教你如何搭建数据访问层? 什么是NHibernate NHibernate 是一个基于.net 的针对关系型数据库的对象持久化类库.NHibernate 来源于非常优秀的基于Ja ...

  9. webapi框架搭建-依赖注入之autofac

    前言 c#的依赖注入框架有unity.autofac,两个博主都用过,感觉unity比较简单而autofac的功能相对更丰富(自然也更复杂一点),本篇将基于前几篇已经创建好的webapi项目,引入au ...

随机推荐

  1. “吃神么,买神么”的第一个Sprint计划(第七天)

    “吃神么,买神么”项目Sprint计划 ——5.25  星期一(第五天)立会内容与进度 摘要: 所有的部件都完成,在贴每个人负责的部件时发现很多问题,很多网页布局的运用不熟练,一部分的div会跑位置~ ...

  2. IEEE 802.11 无限局域网

    (1)无线通讯的两个重要特征 ——Hidden node problem 双方虽然听不到对方的讯号,但同时传送给相同的对象导致了碰撞(这个时候双方都不知道发生了碰撞) ——Exposed node p ...

  3. 团队作业7——第二次项目冲刺(Beta版本12.07——12.08)

    1.当天站立式会议照片 本次会议在5号公寓3楼召开,本次会议内容:①:熟悉每个人想做的模块.②:根据项目要求还没做的完成. 2.每个人的工作 经过会议讨论后确定了每个人的分工 组员 任务 陈福鹏 实现 ...

  4. es6箭头函数的注意要点

    具有一个参数的简单函数 var single = a => a single('hello, world') // 'hello, world' 没有参数的需要用在箭头前加上小括号 var lo ...

  5. git使用教程推荐

    Git使用教程 一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是 ...

  6. xpath 去除空格

    normalize,字面意思就是正规化 加入space  大概意思就是空格的处理了 官方解释是这样的: 通过去掉前导和尾随空白并使用单个空格替换一系列空白字符,使空白标准化.如果省略了该参数,上下文节 ...

  7. ERROR 1698 (28000): Access denied for user 'root'@'localhost' 解决方法

    之前MySQL服务端本机上使用密码登陆root账号是没有问题的,但是今天不知道是因为动了哪里,登陆失败并有这个错误代码: ~$ mysql -u root -p Enter password: ERR ...

  8. Putty+Xming实现在Windows客户端显示Linux服务器端的图形化程序

    走了不少弯路啊~~~言归正传,最近研发和我说要在一台EC2的机器上运行一个带GUI的程序,当时我就纳闷了:EC2的机器应该没有桌面套件的吧,那该怎么运行GUI的程序呢?百思不得其解时收到一封邮件,大致 ...

  9. python利用unittest测试框架组织测试用例的5种方法

    利用unittest测试框架可以编写测试用例,执行方式分两大类:利用main方法和利用testsuite,其中利用测试套件来组织测试用例可以有4种写法. 在此之前,先了解几个概念 TestCase:所 ...

  10. 解决MySQL Slave 触发 oom-killer

    最近经常有收到MySQL实例类似内存不足的报警信息,登陆到服务器上一看发现MySQL 吃掉了99%的内存,God ! 有时候没有及时处理,内核就会自己帮我们重启下MySQL,然后我们就可以看到 dme ...