SQLite CodeFirst、Migration 的趟坑过程 [附源码]

负二、配置说明

最近想做个东西,用到了SQLite,按照之前的方法步骤搭建的结果失败了,因为SQLite的版本升级了,导致Migration自动迁移各种报错,而且我查了一下自动迁移的包貌是不再更新了。——2018年1月24日

能正常使用的配置清单如下(不要升级包,升级包会导致一系列的坑):
EF 6.1.3
SQLite.CodeFirst 1.3.1.18
System.Data.SQLite 1.0.105.2
System.Data.SQLite.Core 1.0.105.2
System.Data.SQLite.EF6 1.0.105.2
System.Data.SQLite.EF6.Migrations 1.0.104
System.Data.SQLite.Linq 1.0.105.2
文章最底下可以下载源码,能正常使用,但是SQLite升级到了1.0.106之后,就不能再使用原来的System.Data.SQLite.EF6.Migrations了。

负一、吐个槽

关于SQLite的CodeFirst,我找了很久,有很多博客都写过,但是真正好用的非常少,还有很多根本就是DBFirst的代码,真是坑的我够呛,研究了几天也算有点成果,整理一下,希望对路过的朋友有所帮助。

零、冷知识

1、SQLite.CodeFirst

使用NuGet安装的EF和SQLite可能是没有CodeFirst功能的,安装了“SQLite.CodeFirst”之后可以为SQLite提供CodeFirst的功能。

2、System.Data.SQLite.EF6.Migrations 数据库迁移

SQLite 是没有数据库迁移 MigrationSqlGenerator 功能的 ,我所查询的资料是这样的(有错请指正,非常感谢),甚至有人说得自己写一个数据迁移的功能,那是不是有点辛苦。

还好在 NuGet 上有一个“System.Data.SQLite.EF6.Migrations”(详细内容可以访问他的网站,网站上有说明文档),安装之后可以做简单的数据库修改,可以添加表中的列,但是并不能删除和修改列,因为 SQLite 并不认识 drop column 和 rename column,至于为什么不认识看下条。

3、SQLite不支持的语法

关于这个,可以自己百度查一下 ^ _ ^ ~

4、关于SQLite中的数据关系

我在试验的过程中,发现SQLite的数据关系并不可靠,尤其是EF的子查询我就没有尝试成功,还有使用外键的时候也并不正常。

5、SQLite 中的 Guid

在 SQLite 中使用 Guid 的时候,因为 SQLite 中 Guid 会使用 BLOB 类型来存储(就是二进制),所以用 Linq 直接 Id==id 是查不到东西的,使用 tostring 也得不到正确的格式,建议在使用 EF SQLite 的时候,使用 string 来存储 Guid 数据。

使用 SQLite 读取工具打开数据库文件,就会发现 Guid 是 BLOB 存储的,并且顺序和 C# 生成的 Guid 并不相同。(如果非要使用 Guid 可以研究一下 Guid 和二进制的转换。)

一、安装过程

OK 进入正题:

使用NuGet安装时,相关联的都会自动下载,所以这点还是很方便的,这里列一下安装完之后都有啥:

  1. EntityFramework
  2. EntityFramework.zh-Hans(中文说明)
  3. SQLite.CodeFirst(实现CodeFirst)
  4. System.Data.SQLite
  5. System.Data.SQLite.Core
  6. System.Data.SQLite.EF6
  7. System.Data.SQLite.EF6.Migrations(提供数据库迁移)
  8. System.Data.SQLite.Linq

安装过程就这样……

二、简单配置一下

虽然安装的时候会自动写入一些配置信息,但是还是可能有些遗漏,查漏补缺一下:

App.config 文件:

entityFramework->providers 下检查添加:

<provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />

connectionStrings 下检查添加:

<add name="DefaultConnection" connectionString="data source=sqliter.db" providerName="System.Data.SQLite" />

三、数据库工具类

一个简单例子只需要4个类:

  1. Entity 类(实体类)
  2. Mapping 类(实体配置)
  3. DbContext 类(数据库操作)
  4. Configuration 类(设置信息)

首先来一个简单“栗子”:

Entity 类:

public class Bus
{public string Id { get; set; }public string Name { get; set; }
}
public class Person
{public string Id { get; set; }public string Name { get; set; }
}

Mapping 类:

public class Mapping
{public class BusMap : EntityTypeConfiguration<Bus>{public BusMap(){}}public class PersonMap : EntityTypeConfiguration<Person>{public PersonMap(){}}
}

DBContext 类:

public class SQLiteDb : DbContext
{public SQLiteDb() : base("DefaultConnection"){Database.SetInitializer(new MigrateDatabaseToLatestVersion<SQLiteDb, Configuration>());}protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();modelBuilder.Configurations.AddFromAssembly(typeof(SQLiteDb).Assembly);}
}

Configuration 类:

public class Configuration : DbMigrationsConfiguration<SQLiteDb>
{public Configuration(){AutomaticMigrationsEnabled = true;AutomaticMigrationDataLossAllowed = true;SetSqlGenerator("System.Data.SQLite", new SQLiteMigrationSqlGenerator());}protected override void Seed(SQLiteDb 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. E.g.////    context.People.AddOrUpdate(//      p => p.FullName,//      new Person { FullName = "Andrew Peters" },//      new Person { FullName = "Brice Lambson" },//      new Person { FullName = "Rowan Miller" }//    );//}
}

四、操作代码

public partial class Form1 : Form
{//创建BusBus Bus = new Bus() { Id = Guid.NewGuid().ToString(), Name = "11 路" };public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){//默认往数据库添加一个Bususing (SQLiteDb db = new SQLiteDb()){db.Set<Bus>().Add(Bus);db.SaveChanges();}}//添加人员void InsertPerson(string name){using (SQLiteDb db = new SQLiteDb()){Bus dbBus = db.Set<Bus>().Where(x => x.Id == Bus.Id).FirstOrDefault();db.Set<Person>().Add(new Person() { Id = Guid.NewGuid().ToString(), Name = name, Bus = dbBus });db.SaveChanges();}}//删除人员void DeletePerson(string id){using (SQLiteDb db = new SQLiteDb()){Person p = new Person() { Id = id };Person person = db.Set<Person>().Attach(p);db.Set<Person>().Remove(person);db.SaveChanges();}}//更新人员void UpdatePerson(string id, string name){using (SQLiteDb db = new SQLiteDb()){Person p = db.Set<Person>().Where(x => x.Id == id).FirstOrDefault();p.Name = name;db.SaveChanges();}}#region 点击按钮操作private void BtInsert_Click(object sender, EventArgs e){//清空文本框TbTxt.Text = "";//插入人员InsertPerson(TbInsert.Text);//显示人员信息using (SQLiteDb db = new SQLiteDb()){List<Person> persons = db.Set<Person>().ToList();if (persons != null){persons.ForEach(x =>{TbTxt.Text += x.Id + "  " + x.Name + Environment.NewLine;});}}}private void BtDelete_Click(object sender, EventArgs e){TbTxt.Text = "";DeletePerson(TbDelete.Text);using (SQLiteDb db = new SQLiteDb()){List<Person> persons = db.Set<Person>().ToList();if (persons != null){persons.ForEach(x =>{TbTxt.Text += x.Id + "  " + x.Name + Environment.NewLine;});}}}private void BtUpdate_Click(object sender, EventArgs e){TbTxt.Text = "";UpdatePerson(TbUpdate.Text, DateTime.Now.ToString("mm:ss"));using (SQLiteDb db = new SQLiteDb()){List<Person> persons = db.Set<Person>().ToList();if (persons != null){persons.ForEach(x =>{TbTxt.Text += x.Id + "  " + x.Name + Environment.NewLine;});}}}private void BtSelect_Click(object sender, EventArgs e){TbTxt.Text = "";if (TbSelect.Text == ""){//查询所有人员信息using (SQLiteDb db = new SQLiteDb()){List<Person> persons = db.Set<Person>().ToList();if (persons != null){persons.ForEach(x =>{TbTxt.Text += x.Id + "  " + x.Name + Environment.NewLine;});}}}else{//根据Id查询人员using (SQLiteDb db = new SQLiteDb()){Person person = db.Set<Person>().Where(x => x.Id == TbSelect.Text).FirstOrDefault();TbTxt.Text = person.Id + "  " + person.Name + Environment.NewLine;}}}#endregion
}

长了点,但一看就懂的。

五、异常处理

一、“System.InvalidOperationException”类型的未经处理的异常在 EntityFramework.dll 中发生

其他信息: No Entity Framework provider found for the ADO.NET provider with invariant name ‘System.Data.SQLite’. Make sure the provider is registered in the ‘entityFramework’ section of the application config file. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information.

解决:App.config中,使用NuGet安装了SQLite,SQLite.Core,EF……之后,默认的配置是这样的:

<providers><provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /><provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
</providers>

而异常提示 provider with invariant name ‘System.Data.SQLite’,所以只需要加上这行provider 配置就可以。

<providers><provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
</providers>

二、“System.InvalidOperationException”类型的未经处理的异常在 EntityFramework.dll 中发生

其他信息: Unable to determine the principal end of an association between the types ‘SQLiter.TestEF.DbTool.IdCard’ and ‘SQLiter.TestEF.DbTool.Student’. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

解决:在MVC中如果数据库表关系是一对一的,需要加 [Required] ,如:

public class Employee
{//员工必须有工牌[Required]public virtual ECard ECard { get; set; }
}

三、“System.Data.Entity.Validation.DbEntityValidationException”类型的未经处理的异常在 EntityFramework.dll 中发生

其他信息: Validation failed for one or more entities. See ‘EntityValidationErrors’ property for more details.

解决:检查是否有字段是必须的。

注:可以通过catch来获取更详细的提示:

catch (DbEntityValidationException dbEx)
{foreach (var validationErrors in dbEx.EntityValidationErrors){foreach (var validationError in validationErrors.ValidationErrors){Console.WriteLine("Class: {0}, Property: {1}, Error: {2}",validationErrors.Entry.Entity.GetType().FullName,validationError.PropertyName,validationError.ErrorMessage);}}
}

代码下载

完整源码
如果没有积分,也可以关注我获取哟~
原来的公众号图被封了…(@_@|||)
如果有需要的话,可以手动搜索公众号找一下【大鱼code】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544723.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux中lvm的缩减

问题提出&#xff1a;服务器硬盘做成了lvm&#xff0c;但是/home目录空间较大&#xff0c;于是想缩减一下&#xff0c;分配给其他目录。实验环境&#xff1a;操作系统&#xff1a;redhat企业版&#xff0c;硬盘已经做成了lvm。问题解决&#xff1a;操作前的注意事项&#xff1a…

SpringBoot 过滤器、拦截器、监听器对比及使用场景!

来源 | blog.csdn.net/qq_38020915/article/details/116431612作者 | dingwen_blog一、关系图理解二、区别1.过滤器过滤器是在web应用启动的时候初始化一次, 在web应用停止的时候销毁可以对请求的URL进行过滤, 对敏感词过滤挡在拦截器的外层实现的是 javax.servlet.Filter 接口…

Java StringBuilder length()方法与示例

StringBuilder类的length()方法 (StringBuilder Class length() method) length() method is available in java.lang package. length()方法在java.lang包中可用。 length() method is used to return the length of this sequence (i.e. it counts the number of characters …

进程通信:匿名管道和命名管道

一、进程间通信方式 管道( pipe )&#xff1a;管道是一种半双工的通信方式&#xff0c;数据只能单向流动&#xff0c;而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。有名管道 (named pipe) &#xff1a; 有名管道也是半双工的通信方式&#xff0c…

Jenkins Build Radiators(构建发射源)

为什么80%的码农都做不了架构师&#xff1f;>>> information radiators&#xff08;信息发射源&#xff09;的概念通常被用在敏捷的圈子里。 据敏捷专家Alistair Cockburn所说&#xff1a; 一个信息发射源是一个贴在一个地方的显示器&#xff0c;当人们工作或路过时…

线程池是如何重复利用空闲的线程来执行任务的?

来源&#xff1a;blog.csdn.net/anhenzhufeng/article/details/88870374在Java开发中&#xff0c;经常需要创建线程去执行一些任务&#xff0c;实现起来也非常方便&#xff0c;但如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&#xff0c…

strictmath_Java StrictMath nextAfter()方法与示例

strictmathStrictMath类的nextAfter()方法 (StrictMath Class nextAfter() method) Syntax: 句法&#xff1a; public static double nextAfter(double starts , double directions);public static float nextAfter(float starts , double directions);nextAfter() method is …

C# 将程序添加开机启动的三种方式

前言 最近在研究程序随系统启动&#xff0c;发现在 win7 上因为权限的问题&#xff0c;写注册表的时候总是会出现问题&#xff0c;写不进去导致的不能自动启动&#xff0c;随后决定仔细的看一看这方面的问题。 查资料过程中主要发现有三种方式可以添加到启动&#xff0c;分别…

SpringBoot 中的 3 种条件装配!

一、介绍在实际的项目开发中&#xff0c;我们往往需要根据不同的环境做出不同的配置&#xff0c;例如&#xff1a;在开发环境下&#xff0c;我们会使用内存数据库以便快速启动服务并进行开发调试&#xff0c;在test环境、生产环境&#xff0c;会使用对应环境的数据库。如果我们…

java中intvalue_Java Short类intValue()方法及示例

java中intvalue短类intValue()方法 (Short class intValue() method) intValue() method is available in java.lang package. intValue()方法在java.lang包中可用。 intValue() method is used to return the value denoted by this Short object converted to type int (by c…

C# Winform 窗体美化(目录)

最近在看 C# Winform 的窗体美化&#xff0c;发现一些很有用的美化皮肤库&#xff0c;学习过后也把一些资料整理一下。 一、IrisSkin 换肤库&#xff08;IrisSkin4&#xff09; 二、LayeredSkin 界面库&#xff08;LayeredSkinDemo&#xff09; 三、不规则窗体&#xff08;G…

图说 mysql 事务隔离级别

转载于:https://blog.51cto.com/kingbox/1657916

@Autowired报错的4种解决方案和原因分析!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;上图的报错信息相信大部分程序员都遇到过&#xff0c;奇怪的是虽然代码报错&#xff0c;但丝毫不影响程序的正常执行&#x…

C# Winform 窗体美化(一、IrisSkin 换肤库)

IrisSkin 换肤库 IrisSkin 是为Microsoft Visual Studio dotNET开发的最易用的界面增强dotNET(WinForm)组件包。能完全自动的为应用程序添加支持换肤功能。[百度百科] 1、文件 IrisSkin4.dll - 544 KB各种 .ssk 格式的皮肤文件&#xff08;一般在网上搜的是13个皮肤的压缩包…

java double方法_Java Double类compare()方法与示例

java double方法双类compare()方法 (Double class compare() method) compare() method is available in java.lang package. compare()方法在java.lang包中可用。 compare() method is used to check equality or inequality of the given two double values or in other word…

MySQL开发规范

命名规范> 库名、表名、字段名必须使用小写字母并采用下划线分割> 库名、表名、字段名禁止超过32个字符&#xff0c;须见名知意 * 库名、表名、字段名支持最多64个字符&#xff0c;统一规范、易于辨识以及减少传输量不要超过32> 库名、表名、字段名禁止使用MySQL…

厉害了,Spring中bean的12种定义方法!

前言在庞大的java体系中&#xff0c;spring有着举足轻重的地位&#xff0c;它给每位开发者带来了极大的便利和惊喜。我们都知道spring是创建和管理bean的工厂&#xff0c;它提供了多种定义bean的方式&#xff0c;能够满足我们日常工作中的多种业务场景。那么问题来了&#xff0…

C# Winform 窗体美化(二、LayeredSkin 界面库)

二、LayeredSkin 界面库 概况 这部分资源是 Winform 美化最多的了&#xff0c;效果还不错&#xff0c;使用时只需引入 LayeredSkin.dll - 696 KB 即可。 网上能找到的最后 LayeredSkin 版本应该是 LayeredSkin Demo2014-12-10.zip&#xff0c;之后作者就整合成一个更加强大的…

Java Currency getInstance()方法与示例

货币类getInstance()方法 (Currency Class getInstance() method) Syntax: 句法&#xff1a; public static Currency getInstance(Locale lo);public static Currency getInstance(String curr_code);getInstance() method is available in java.util package. getInstance()…

【WebSocket初探 】

众所周知&#xff0c;socket是编写网络通信应用的基本技术&#xff0c;网络数据交换大多直接或间接通过socket进行。对于直接使用socket的client与服务端&#xff0c;一旦连接被建立则均可主动向对方传送数据&#xff0c;而对于使用更上层的HTTP/HTTPS协议的应用&#xff0c;因…