Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 422|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-5-29 04:34:58 | 显示全部楼层 |阅读模式

    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库

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-1-23 12:09 , Processed in 0.061933 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表