Sqlite && EF Code FIRST 终极解决方案 2019.5.17#

包括根据模型自动生成数据库,初始化数据,模型改变时的自动数据迁移等

2019.12.25 更新

支持EF6.3的SQL Generation:NuGet:Link.EntityFramework.Sqlite

我是真的服了,用nuget自动安装的config每次都用不了,需要添加factory:

 <remove invariant="System.Data.SQLite" />
<add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />

-----------------------------------以下原文-----------------------------------------

目录

  • 01 知识汇总
  • 02 在过去的经验里EF与Sqlite的使用经验
  • 03 Sqlite数据库在EF Code First中遇到的困难
  • 04 解决方案
  • 05 解决方案的不足
  • 06 与老方案对比

01 知识汇总

What is EF

Entity Framework是MS提供的一个ORM框架,它旨在为小型应用程序中数据层的快速开发提供便利。

ORM 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化,这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQ L 语句打交道,只需简单的操作实体对象的属性和方法。

Code First模式我们称之为“代码优先”模式,是从EF4.1开始新建加入的功能。使用Code First模式进行EF开发时开发人员只需要编写对应的数据类(其实就是领域模型的实现过程),然后自动生成数据库。这样设计的好处在于我们可以针对概念模型进行所有数据操作而不必关系数据的存储关系,使我们可以更加自然的采用面向对象的方式进行面向数据的应用程序开发。

遇到的讲解EF非常好的博客

EF性能全面讲解:https://www.cnblogs.com/yaopengfei/p/9196962.html

EF迁移全面讲解:https://www.cnblogs.com/farb/p/DBMigration.html

What is Sqlite

SQLite是一种嵌入式数据库,它的数据库就是一个文件,它占用资源非常的低。

02 Sqlite数据库在EF Code First 中遇到的困难

  • 设计上的EF瓶颈:无法实现接口类的数据库化
  • EF的Sqlite Data Provider无法提供SQL Generation
  • Sqlite数据库自身许多语法不支持,后续模型修改的数据迁移Sql语句比较受限
  • EF瓶颈:对于视图支持不好,但有临时的解决方案

03 在过去的经验里EF与Sqlite的使用经验

由于Sqlite的Data Provider不支持Sql Generation 所以EF无法从模型中自动创建数据库,所以创建数据库以及后续修改模型时需要的修改数据库工作手动去完成.

  • Model - 构建模型 - 修改模型
  • DataBase - 手写Sql构建数据库,手写Sql填充初始数据 - 手写Sql修改模型

EF COde First利用数据库中自动生成的__MigrationHistory表的信息与当前代码的模型去判断是否需要更新数据库表结构

手写SQL语句是通过数据库某一个标识去判断需不需要更新,例如:Version列

04 解决方案

Sql Generation解决方法

目前我发现两个实现了Sqlite SQL Generation的开源库:

System.Data.SQLite.EF6.Migrations

SQLite.CodeFirst

目前来看的话两种库几乎一样,都是添加了关于Sql Generation的方法,两个库都可在NuGet上获取。

需要注意的是,SQLite.CodeFirst在NuGet上的最新版1.5.2.28并不具有数据迁移功能,该Github项目并没有集成,具有数据迁移功能的版本可以点击上方链接,是另一个人在此基础上集成的。

另外,最新的EF6.2在初始化数据的时候会有错误,项目建议在Add-Migration的时候降级EF到6.13,我当前的办法是使用EF6.3预览版.

解决接口类的数据库化

目前想到的方法只有一个基类去实现这个接口,然后接口的实现类去继承这个基类

public interface ICourse: Iid
{
string Name { get; set; }
ICollection<Student> Students { get; set; }
} public class CourseBase : ICourse
{
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; }
public string ID { get; set ; }
} [Table("Chinese")]
public class Chinese : CourseBase
{
public Chinese()
{
ID = Guid.NewGuid().ToString("N");
}
public string Extend { get; set; }
public string Extend1 { get; set; }
}

然后在模型中:

public class Student:Iid
{
public Student()
{
ID = Guid.NewGuid().ToString("N");
}
public string Name { get; set; }
public Weapon.Weapon Weapon { get; set; }
public string ID { get; set; }
public virtual ICollection<CourseBase> Courses { get; set; }
}

用CourseBase代替ICource

视图解决方案(通用)

EF本身是不支持View的,但是如果你的数据库已经存在View,你是可以去建Table模型一样去建立View模型并查询他。

那么我们只需要解决两个问题就可以实现视图的控制:

  • 初始化数据库时如何建立View
  • 修改数据模型时,Migration时屏蔽View的实体类修改的影响

目前通用的解决方案是

  • 初始化数据时手写Sql去建立View
  • 不用自动数据迁移,而用API去控制迁移
  • 如果需要修改View,手动Drop视图并再次Add New View

1 关闭自动迁移,创建初始化API Add-Migration

AutomaticMigrationsEnabled = false;

2 在Up方法中手写Sql语句创建View

 public override void Up()
{
string script =
@"
CREATE VIEW [StudentWeaponView]
AS SELECT p.ID AS StudentID, p.Name AS StudentName,u.ID AS WeaponID,u.Name AS WeaponName
FROM [Students] p
INNER JOIN [Weapon] u ON u.Id = p.Id";
DBContext.DBContext ctx = new DBContext.DBContext();
ctx.Database.ExecuteSqlCommand(script);
}

3 当你想修改模型时,执行Add-Migration ChangeViewNmae

 public override void Up()
{
string script =
@"
Drop View If Exists [StudentWeaponView];";
DBContext.DBContext ctx = new DBContext.DBContext();
ctx.Database.ExecuteSqlCommand(script); string script2 =
@"
CREATE VIEW [StudentWeaponView]
AS SELECT p.ID AS StudentID, p.Name AS StudentName,u.ID AS WeaponID,u.Name AS WeaponName
FROM [Students] p
INNER JOIN [Weapon] u ON u.Id = p.Id";
ctx.Database.ExecuteSqlCommand(script2);
}

05 当前解决方案的不足

  • 需要使用EF3 Preview Or Data Migration时降级EF到6.13版本
  • 视图支持不好,当然不仅仅是针对Sqlite,EF的解决方案通病
  • EF数据迁移初始化数据方式单一

这里解释一下第三点,看一下使用方法的对比就可以了

老方案(手写SQL创建数据库)初始化数据库:

  • 脚本1:创建数据库
  • 脚本2:添加两条数据
  • 脚本3:增加一个字段
  • 脚本4:添加一条数据

EF Code First 初始化数据:

  • Migration1:创建数据库
  • Migration1:增加一个字段
  • Seed方法:添加两条数据,添加一条数据

Seed方法是每次执行完从低版本到高版本的数据迁移都会执行的方法,官方推荐用AddOrUpdate方法,就是因为每次迁移都会调用,所以会出现重复数据的情况

那么上面两种方法有什么区别呢?

如果下面Seed方法中,原封不动翻译上面的脚本2和脚本4,那么是一定会报错的,为什么?

因为在上面的脚本2执行中,Sql模型中是没有脚本三增加的字段的,而下面Seed方法执行时,脚本三的字段已经添加完毕了,即在Seed方法执行的时候,调用的是最终的数据模型,上面每次执行脚本的时候,调用的当前版本的数据模型

也就是说,如果你想实现一个版本一个版本增量的初始化数据库内容,那么在Seed方法中你要进行大量的对于版本的判断。

目前的解决办法:

数据库架构方面的版本是由EF的__MigrationHistory控制的

那么数据库初始内容上也加一个版本号

然后

if(version>1)
{
//脚本1初始数据内容.....
} if(version>2)
{
//脚本2初始数据内容.....
}

附上完整代码地址实现了Sqlite有关于EF Code First的全部功能。

Master利用的是System.Data.SQLite.EF6.Migrations库

分支用的是SQLite.CodeFirst库

Sqlite && EF Code FIRST 终极解决方案 2019.5.17的更多相关文章

  1. EF Code First 初体验

    Code First 顾名思义就是先代码,再由代码生成数据库的开发方式. 废话不多说,直接来一发看看:在VS2010里新建一个空白解决方案,再依次添加两个类库项目:Model.DataAccess和一 ...

  2. EF Code First 一对多、多对多关联,如何加载子集合?

    应用场景 先简单描述一下标题的意思:使用 EF Code First 映射配置 Entity 之间的关系,可能是一对多关系,也可能是多对多关系,那如何加载 Entity 下关联的 ICollectio ...

  3. svn 集成 redmine 账户验证的终极解决方案

    svn 集成 redmine 账户验证的终极解决方案 赖勇浩(http://laiyonghao.com) 动机 对于大部分开发团队来说,一般都需要一套 SCM 系统,通常是 svn + redmin ...

  4. SQLite EF Core Database Provider

    原文链接 This database provider allows Entity Framework Core to be used with SQLite. The provider is mai ...

  5. Android使用gradle依赖管理、依赖冲突终极解决方案(转)

    Android使用gradle依赖管理.依赖冲突终极解决方案在Android开发中,相信遇到关于版本依赖的问题的同学有不少.虽然Android Studio一般都会自动帮我们去重,但是有时候去重失败了 ...

  6. EF Code First Migrations数据库迁移

    1.EF Code First创建数据库 新建控制台应用程序Portal,通过程序包管理器控制台添加EntityFramework. 在程序包管理器控制台中执行以下语句,安装EntityFramewo ...

  7. EF Code First学习系列

    EF Model First在实际工作中基本用不到,前段时间学了一下,大概的了解一下.现在开始学习Code First这种方式.这也是在实际工作中用到最多的方式. 下面先给出一些目录: 1.什么是Co ...

  8. EF和MVC系列文章导航:EF Code First、DbContext、MVC

    对于之前一直使用webForm服务器控件.手写ado.net操作数据库的同学,突然来了EF和MVC,好多新概念泉涌而出,的确犹如当头一棒不知所措.本系列文章可以帮助新手入门并熟练使用EF和MVC,有了 ...

  9. 【极力分享】[C#/.NET]Entity Framework(EF) Code First 多对多关系的实体增,删,改,查操作全程详细示例【转载自https://segmentfault.com/a/1190000004152660】

      [C#/.NET]Entity Framework(EF) Code First 多对多关系的实体增,删,改,查操作全程详细示例 本文我们来学习一下在Entity Framework中使用Cont ...

随机推荐

  1. Java类初始化顺序,大神3个示例带你躺坑。。

    最近发现微信群里面有些群友在讨论类的初始化顺序,如类的静态变量.成员变量.静态代码块.非静态代码块.构造器,及继承父类时,它们的初始化顺序都是怎样的,下面我通过例子来说明这个情况,以免被人误导. 示例 ...

  2. MyEclipse中最常用的快捷键大全

    1. [ALT+/]    此快捷键为用户编辑的好帮手,能为用户提供内容的辅助,不要为记不全方法和属性名称犯愁,当记不全类.方法和属性的名字时,多体验一下[ALT+/]快捷键带来的好处吧. 2. [C ...

  3. USACO2012 overplanting /// 矩阵切割 递归 oj21547

    题目大意: 在农场的任何一个“轴向对齐”的长方形区域(即垂直和水平方向)种植草坪. 现种植了N(1≤ N ≤10)个不同的矩形区域,其中一些甚至可能重叠. Input Multiple test ca ...

  4. springfox-swagger原理解析与使用过程中遇到的坑

    swagger简介 swagger确实是个好东西,可以跟据业务代码自动生成相关的api接口文档,尤其用于restful风格中的项目,开发人员几乎可以不用专门去维护rest api,这个框架可以自动为你 ...

  5. linux每日命令(1):gzip命令

    gzip是在Linux系统中经常使用的一个对文件进行压缩和解压缩的命令,既方便又好用. gzip不仅可以用来压缩大的.较少使用的文件以节省磁盘空间,还可以和tar命令一起构成Linux操作系统中比较流 ...

  6. set -x 调试shell

    在上面的结果中,前面有“+”号的行是shell脚本实际执行的命令,前面有“++”号的行是执行trap机制中指定的命令,其它的行则是输出信息. shell的执行选项除了可以在启动shell时指定外,亦可 ...

  7. __init__初始化方法

    使用场景:多个对象(由同一个类产生)的属性同名且值都一样,这时就需要使用init()方法. # 多个对象(由同一个类产生)的属性同名且值都一样,这时就需要使用__init__()方法. # class ...

  8. thinkphp PATH_INFO支持

    如果发生在本地测试正常,但是一旦部署到服务器环境后会发生只能访问首页的情况,很有可能是你的服务器或者空间不支持PATH_INFO所致. 系统内置提供了对PATH_INFO的兼容判断处理,但是不能确保在 ...

  9. JavaWeb学习篇之----web应用的虚拟目录映射和主机搭建(Tomcat)

    从今天开始来学习JavaWeb的相关知识,之前弄过一段时间JavaWeb的,就是在做毕业设计的时候搞过,但是那时候完全是为了任务去学习,所以效果不好,好多东西都没有深入的研究过,所以接下来的一段时间我 ...

  10. NX二次开发-算法篇-找相切面

    方法1:通过判断相邻面公共边的光顺性来找相切面 1 #include <uf.h> 2 #include <uf_modl.h> 3 #include <uf_obj.h ...