最近由於伺服器變更為Linux系統.MsSql for Linux什麼時候出來到生產環境使用還是要很長時間的.於是考慮使用Mysql資料庫,ORM使用EF.於是先踩下坑順便記錄一下,有需要的tx可以參考下.當你考慮使用EF連接Mysql的時候肯定是已經在網上搜了一堆教程.網上教程基本都是使用控制台做 ...
最近由於伺服器變更為Linux系統.MsSql for Linux什麼時候出來到生產環境使用還是要很長時間的.於是考慮使用Mysql資料庫,ORM使用EF.於是先踩下坑順便記錄一下,有需要的tx可以參考下.
當你考慮使用EF連接Mysql的時候肯定是已經在網上搜了一堆教程.網上教程基本都是使用控制台做演示.跟著一步步來姿勢沒錯的話可能會正常運行,但項目中使用分層後,把數據層剝離出去,再使用code first連接瞬間蒙B了,各種奇葩問題隨之而來.咋跟教程說的不一樣呢...所以本文就一步步的介紹如何在分層的項目中使用EF code first連接Mysql.
ps:本文測試環境為windows 8.1+Vs2013+MySql5.7.12
一.搭建環境及安裝對應組件
首先創建一個空的MVC項目後簡單的再創建一個類庫項目用於EF的操作以及實體的存放(這裡為了演示沒有創建過多的項目).最終結果如下:
然後在.Data項目中用Nuget安裝EF.然後安裝MySql數據驅動所需dll:
MySQL.Data.Entities.dll //Nuget預設會帶上MySQL.Data.dll
添加一個繼承自DBContext的類MyDbContext.cs:
namespace EF2MySqlApp.Data { public class MyDbContext:DbContext { public MyDbContext() { } public DbSet<BookInfo> BookInfoes { get; set; } } }
接著建個文件夾放實體類BookInfo.cs:
namespace EF2MySqlApp.Data { public class BookInfo { public int Id { get; set; } public string Number { get; set; } public string Name { get; set; } public string Author { get; set; } public decimal Price { get; set; } public bool Deleted { get; set; } public DateTime CreatedOn { get; set; } } }
結構類似這樣:
二.配置EF
接著該是配置資料庫連接字元串了,現在在App.Config下添加connectionString位元組,註意別寫到configSections上邊去了,否則進行添加Migration會報節點配置錯誤.
<connectionStrings> <add name="MyDbContext" connectionString="Data Source=localhost;port=3306;Initial Catalog=myappdb;uid=root;password=123456;Charset=utf8" providerName="MySql.Data.MySqlClient" /> </connectionStrings>
然後打開程式包管理器控制台(-_-不知道的請自定搜索),預設項目選擇.Data項目,然後輸入這個命令:
Enable-Migrations //啟用遷移
沒有錯誤提示接著輸入:
Add-Migration record1 //給本次遷移記錄起個名字 這個名字可以隨便起,儘量別重名 註意這裡的Migration沒有s結尾.
這時看到項目下已經多了幾個文件:
Configuration.cs為進行遷移前的配置文件.以時間戳+剛纔輸入的名字的文件為遷移記錄文件.本文重點不在此,所以各個作用在此不做過多介紹.有興趣可自行搜索學習.
在Configuration類的構造函數中有這麼一句話,它的作用在於是否啟用自動遷移,預設不啟用:
AutomaticMigrationsEnabled=false;
在Seed方法中可以添加種子數據,遷移成功後將會執行這個方法,把數據插入到資料庫中.
protected override void Seed(EF2MySqlApp.Data.MyDbContext context) { context.BookInfoes.AddOrUpdate(x => x.Id, new BookInfo { Name="C#大法好",Author="summit", Number="1234",Price=1024} ); context.SaveChanges(); }
註意最後需要保存一下
這個時候如果直接update-database將會報Sql Server連接失敗的錯誤:如下:
在與 SQL Server 建立連接時出現與網路相關的或特定於實例的錯誤。未找到或無法訪問伺服器。....
問題來了,想連接MySql怎麼預設使用了Sql Server呢.這是EF預設連接的是SqlServer 所以要告訴EF我們需要使用Mysql:
1.
在Configuration類的構造函數中加入: SetSqlGenerator("MySql.Data.MySqlClient",new MySql.Data.Entity.MySqlMigrationSqlGenerator());
2.
給MyDbcontext類增加DbConfigurationType特性: [DbConfigurationType(typeof(MySql.Data.Entity.MySqlEFConfiguration))] public class MyDbContext:DbContext { public MyDbContext() { } public DbSet<BookInfo> BookInfoes { get; set; } } ps:這個也可以在config文件里配置,個人比較喜歡用代碼控制.
再次運行Update-Database -v 依然報錯,但提示字元串格式不正確,往上翻翻看到DbContenniton獲取字元串的時候出錯了,於是猜測是否是連接字元串的問題:
System.ArgumentException: 從索引 0 處開始,初始化字元串的格式不符合規範。 在 System.Data.Common.DbConnectionOptions.GetKeyValuePair(String connectionString, Int32 currentPosition, StringBuilder buffer, Boolean useOdbcRules, String& keyname, String& keyvalue)
連接字元串測試沒發現問題,那會不會是找不到這個連接字元串呢? N久後發現在這個控制臺中使用的config文件預設是到當前啟動項目下尋找config文件.
把Data項目設為啟動項後再次運行報:
The underlying provider does not support the type 'nvarchar(max)'.
這個錯誤由於MySql欄位不相容string類型,在string類型欄位前加註解[MaxLength(100)]即可解決(奇怪的是我用vs2015測試是不會報這個錯誤的..).
再次執行Update-DataBase -v 成功!
如果使用的mysql命令行:輸入這些命令查看是否創建成功:
show databases; use myappdb; select * from bookinfoes;
用其他客戶端的那就更不必多說了.
三.遺留問題
1.不設置Data項目為啟動項找不到該項目下App.Config里配置的字元串(控制台預設項目已為該項目)
如果把連接字元串放到Web.Config里則需要在該項目里也安裝ef,mysql.data... 但個人原因並不想在表現層出現ef等東西,不放又不去找App.config的配置.
這點著實疑惑了好久.每次這樣設置啟動項也挺麻煩的,所以可以手動指定連接字元串(亦可以寫在其他地方讀取過來放進去):
public MyDbContext() : base("Data Source=127.0.0.1;port=3306;Initial Catalog=myappdb;uid=root;password=123456;Charset=utf8") { }
ps:如果遷移過程中出現未將對象引用到實例之類的錯誤多半原因就是連接字元串找不到或無效導致的,遇到這種問題先從連接字元串排查.
2.插入數據中含有中文亂碼.
這個問題網上眾說紛紜,改這個配置改那個配置的.跟著改後會發現然並卵...我這裡插入中文後使用Mysql控制台查看數據亂碼了,使用Navicat查看卻是正常的.所以這個問題還在觀測中,如果各位有什麼好的解決辦法可以留言 :)
3.前幾天有園友問了個問題,他使用MiniProfiler監控ef to mysql 監控不到sql語句.給他回覆後也不知道他解決了沒..
再次提示下:如果使用MiniProfiler過程中報了這個錯誤:
在嘗試添加“Loaded”事件處理程式前,實體框架已在使用一個 DbConfiguration 實例。
檢查下你的項目是否使用了EF的初始化數據配置.使用了的話需要把MiniProfilerEF6.Initialize();這句放到初始化配置之前.(我一般直接放到最前邊 :) )
踩坑才剛剛開始,使用過程中肯定還會有各種問題.還會繼續記錄下來,給有需要的朋友提個醒.
如果看官中有老司機還望不吝賜教. -_-!