MySQL via EF6 的試用報告

来源:https://www.cnblogs.com/hanzongze/archive/2019/04/22/ef6-trial-report.html
-Advertisement-
Play Games

" 1、如何通過 EF6 來連接 MySQL? " " 2、如何通過 EF6 來實現 CRUD? " "2.1、Create 添加" "2.2、Retrieve 查詢" "2.3、Update 修改" "2.4、Delete 刪除" " 3、如何更好的運用 EF6 來完成工作? " "3.1、傳說中 ...


公司的項目中用的 ORM 是 Dapper,代碼中充斥著大量的 SQL 語句,為了少寫 SQL 語句,領導讓我把 EF6 也加進去看會不會有問題。按照指示,我在新的代碼分支引入了 EF6 並做了 CRUD 的測試,結論是混合使用 Dapper 和 EF6 沒問題。為了讓團隊中沒用過 EF 的同事也能快速上手 EF,我把我的試用記錄重新整理了一下,於是乎就有了本文。

1、如何通過 EF6 來連接 MySQL?

1、安裝 MySQL 的 .NET 驅動

要在 .NET 項目中連接 MySQL 首先得安裝 MySQL 的 .NET 驅動。這個驅動是向下相容的,官方下載地址:MySQL Connector/NET

2、安裝 MySql.Data.EntityFramework

Install-Package MySql.Data.EntityFramework -Version 8.0.15

上面的 NuGet 命令會自動幫你把 EF6 和 MySql.Data 都安裝好,無需額外再安裝。

3、創建模型類

有了和資料庫中表對應的模型類,才能方便的操作資料庫而不必寫 SQL 語句。如定義一個 Person 實體,示例如下:

[Table("person")] // 這裡不僅可以自定義表的 Name 還可以自定義表的 Schema
public class Person {
    [Key]
    public Int32 ID { get; set; }
    public String Name { get; set; }
    public DateTime Birthday { get; set; }
    public Int32 NationID { get; set; }
    public Nation Nation { get; set; }
}

定義實體的註意事項:

  • 1、模型類名與表名不必相同。如果不同,則需要用 TableAttribute 標註一下;如果相同,則可以省略該 Attribute。
  • 2、主鍵名不必非得是 ID。如果不是,則需要用 KeyAttribute 標註一下;如果是 ID,則可以省略該 Attribute。EF 遵循“約定大於配置”的開發原則,比如 EF 中主鍵名預設為 ID 就是 EF 的一個內置約定,EF 還支持自定義約定。

4、創建資料庫上下文類

有了資料庫上下文,就可以連接資料庫了,然後在上下文中定義相應的 DbSet(實體對象集合),就能直接對資料庫進行 CRUD 操作了。如創建一個 Demo 的上下文,示例如下:

public class DemoDbContext : DbContext {
    // 聲明 DbSet,實現 CRUD 的方法定義在 DbSet 中
    public DbSet<Person> Persons { get; set; }
    public DbSet<Nation> Nations { get; set; }

    public DemoDbContext() : base("name=ConnectionString") {
        // 關閉遷移,EF Code First 預設會在 Model 發生改變後自動更新資料庫
        Database.SetInitializer<DemoDbContext>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);
        // 解決表名變複數的問題,EF 生成 SQL 語句時預設會將實體名變成複數
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

定義上下文的註意事項:

  • 1、創建的資料庫上下文類必須繼承 DbContext 類。
  • 2、在上下文類的構造函數中通過 base 的方式指定資料庫連接字元串。base 的參數寫法有多種,常見的寫法如下:
    • base("ConnectionString")
    • base("name=ConnectionString")
    • base(new MySqlConnection("..."), false)
  • 3、由於 EF 的遷移功能過於複雜,且非必要,一般不用,在構造函數中關閉即可。
  • 4、EF 預設生成的表名是 Model 名的複數,可在 OnModelCreating 中移除該轉換規則。

2、如何通過 EF6 來實現 CRUD?

2.1、Create 添加

  • 1、向一個表中添加一條數據,示例如下:
using (var context = new DemoDbContext()) {
    var p = new Person() { Name = "Andy", Gender = 1 };
    context.Persons.Add(p);
    context.SaveChanges(); // 返回受影響行數 1
}

上面的代碼會生成 1 條 INSERT 語句和 1 條 SELECT 語句。

  • 2、同時向存在主外鍵的兩個表中添加一條數據,示例如下:
using (var context = new DemoDbContext()) {
    var n = new Nation() { Name = "China" };
    var p = new Person() { Name = "Mark", Gender = 1, NationID = n.ID };
    context.Nations.Add(n);
    context.Persons.Add(p);
    context.SaveChanges(); // 返回受影響行數 2
}

上面的代碼會生成 1 條 INSERT 語句和 2 條 SELECT 語句。

  • 3、一次添加多個並附加事務:
String connectionString = "server=localhost;port=3306;database=demo;uid=root;pwd=";
using (MySqlConnection connection = new MySqlConnection(connectionString)) {
    connection.Open();
    MySqlTransaction transaction = connection.BeginTransaction();

    try {
        using(var context = new DemoDbContext(connection)) {
            context.Database.UseTransaction(transaction);

            List<Person> ps = new List<Person>();
            ps.Add(new Person { Name = "Mark",  Gender = 1 });
            ps.Add(new Person { Name = "Jack", Gender = 1 });
            ps.Add(new Person { Name = "Tom", Gender = 1 });

            context.Persons.AddRange(ps);
            context.SaveChanges();
        }

        transaction.Commit();                    
    } catch {
        transaction.Rollback();
        throw;
    }
}

2.2、Retrieve 查詢

  • 1、EF 查詢支持 LINQ 寫法,必須在最後調用ToList()才會執行查詢,示例如下:
using (var context = new DemoDbContext()) {
    context.Database.Log = Console.WriteLine;
    var list1 = (from p in context.Persons where p.ID == 1 select p).ToList();
    var list2 = (from p in context.Persons select p.Name).ToList();                
    var query = from p in context.Persons select p;
    query = from p in query where p.ID >= 1 select p;
    query = from p in query where p.NationID == 1 select p;
    query = from p in query orderby p.Name descending select p;
    query.ToList();
}
  • 2、EF 查詢支持 Lambda 寫法,示例如下:
using (var context = new DemoDbContext()) {
    context.Database.Log = Console.WriteLine;
    // LIMIT 1
    var p1 = context.Persons.FirstOrDefault();
    // LIMIT 2,不會做參數化處理
    var p2 = context.Persons.Single(p => p.ID == 5);
    // LIMIT 2,會自動做參數化處理
    var p3 = context.Persons.Find(3);
    // 會自動做參數化處理
    var p4 = context.Persons.Where(p => p.Name.Contains("Andy")).ToList();
    // 只查詢部分數據行,可用這個實現分頁查詢
    var p5 = context.Persons.OrderBy(p => p.Name).Skip(3).Take(5).ToList();
    // 帶條件的分頁查詢
    var p6 = context.Persons.Where(p => p.ID > 0).OrderBy(p => p.Name).Skip(3).Take(5).ToList();
}
  • 3、查詢關聯數據,示例如下:
using (var context = new DemoDbContext()) {
    var persons = context.Persons.Include(p => p.Nation).ToList();
}

上面的代碼會生成 1 條內連接 SELECT 語句。

2.3、Update 修改

  • 1、修改一條確定存在的數據時,用如下語句:
using (var context = new DemoDbContext()) {
    var p = new Person() { ID = 3, Name = "Andy" };
    context.Persons.Attach(p);
    context.Entry(p).Property(i => i.Name).IsModified = true;
    context.SaveChanges(); // 返回受影響行數
}

上面的代碼會生成 1 條 UPDATE 語句,數據不存在時會報錯。

  • 2、如果需要確認數據存在後再修改的話,用如下語句:
using (var context = new DemoDbContext()) {
    var p = context.Persons.Find(1); // 也可以用 FirstOrDefault 或其它查詢方法
    if (p != null) {
        p.Name = "Peter";
        context.Persons.Attach(p);
        context.Entry(p).Property(i => i.Name).IsModified = true; // 指定更新欄位
        context.SaveChanges(); // 返回受影響行數
    }
}

上面的代碼會生成 1 條 UPDATE 語句和 1 條 SELECT 語句。

2.4、Delete 刪除

  • 1、刪除一條確定存在的數據時,用如下語句:
using (var context = new DemoDbContext()) {
    var p = new Person() { ID = 1 };
    context.Persons.Attach(p);
    context.Persons.Remove(p);
    context.SaveChanges(); // 返回受影響行數
}

上面的代碼會生成 1 條 DELETE 語句,數據不存在時會報錯。

  • 2、如果需要確認數據存在後再刪除的話,用如下語句:
using (var context = new DemoDbContext()) {
    var p = context.Persons.FirstOrDefault(it => it.ID == 1);
    if (p != null) {
        context.Persons.Attach(p);
        context.Persons.Remove(p);
        context.SaveChanges();
    }
}

3、如何更好的運用 EF6 來完成工作?

技術好的人經常講業務場景,相反,有些技術差的人卻喜歡不由分說的吐槽那些他根本就沒搞懂的技術。在 .NET 圈子裡,有人對 EF 是愛不釋手,也有人對 EF 是各種吐槽。

我很喜歡的一句話是:“沒有不好的技術,只有沒被用好的技術”,我的理解是任何技術都有局限性,作為程式員,我們要做的是結合實際業務場景來選用最合適的技術。要想在項目中更好的運用 EF,就得更多的瞭解 EF 技術,本節就來分享一下我試用 EF6 過程中的一些收穫。

3.1、傳說中 EF 的三種模式

為什麼說 EF 的三種模式是傳說呢?因為新版的 EF 預設只支持 Code First 這一種模式了。要想用 Database First 或 Model First 還得把 Visual Studio 降級到 VS10 或 VS12 才行,實在沒必要,下麵簡單羅列下每種模式的特點:

  • 1、Database First:即資料庫優先,先創建好資料庫和表,然後自動生成 EDM(實體數據模型)文件,再由 EDM 文件生成模型類。當現有資料庫結構比較成熟穩定時,可用這種模式實現快速開發。
  • 2、Model First:即模型優先,先創建可視化的 EDM 文件,然後由 EDM 文件來自動生成模型類和資料庫。開發速度快,但代碼冗餘。寫個小 Demo 還行,但企業級開發一般沒人用這個模式。
  • 3、Code First:即代碼優先,先寫好模型類,然後自動生成資料庫,沒有 EDM 文件。代碼簡潔可控,也是官方和業界首推的模式。

3.2、EF6 執行原生 SQL 查詢

總會有些時候,我們為了性能或者其它各種各樣的緣故,而不得不寫 SQL 語句,EF 提供了直接執行 SQL 語句的方法SqlQuery()

  • 1、執行無參數的原生 SQL 查詢,示例如下:
using (var context = new DemoDbContext()) {
    var persons = context.Persons.SqlQuery("SELECT * FROM Person").ToList();
}
  • 2、執行帶參數的原生 SQL 查詢,示例如下:
using (var context = new DemoDbContext()) {
    var sql = "SELECT t.* FROM Person t WHERE t.Gender=@Gender";
    var p1 = context.Persons.SqlQuery(sql, new MySqlParameter("@Gender", 1)).ToList();
    // 下麵這種更簡單的寫法相當於上面兩句,EF 會自動將其轉換為參數化查詢
    var p2 = context.Persons.SqlQuery("SELECT t.* FROM Person t WHERE t.Gender={0}", 1).ToList();
}
  • 3、只查詢部分可選欄位,示例如下:
using (var context = new DemoDbContext()) {
    var persons = context.Database.SqlQuery<MiniPerson>("SELECT t.ID,t.Name FROM Person t").ToList();
}

註意:這裡用的是MiniPerson類,而不是模型類Persons,因為用模型類時,查詢返回的欄位必須與其模型中的欄位對應,而用非模型類時則沒有這個限制,EF 會自動把值賦給相應的欄位,並忽略其它欄位,即便完全不匹配也不會報錯。

  • 4、統計表中的數據條數,示例如下:
using (var context = new DemoDbContext()) {
    var count = context.Database.SqlQuery<Int32>("SELECT COUNT(1) FROM Person").SingleOrDefault();
}

其實 EF 的SqlQuery()還支持調用存儲過程,但實際開發中,一般最好不要存儲過程。因為一旦用了存儲過程,相比較得到的性能提升,往往付出的維護代價會更大,得不償失。

3.3、EF6 執行原生 SQL 增刪改

EF6 調用增刪改等命令語句的方法是ExecuteSqlCommand(),示例如下:

using (var context = new DemoDbContext()) {
    context.Database.ExecuteSqlCommand("INSERT INTO Person VALUES(DEFAULT,'小明',NOW(),1)");
    context.Database.ExecuteSqlCommand("UPDATE Person SET Name='小王' WHERE ID=8");
    context.Database.ExecuteSqlCommand("DELETE FROM Person WHERE ID=14");
}

一般用 EF 就是為了不寫 SQL 語句,尤其是大多數時候不會造成性能問題的增刪改語句,所以使用ExecuteSqlCommand()的概率是比較低的。

3.4、EF6 不推薦的 CRUD 寫法

有些朋友通過別人的帖子發現直接更改實體狀態也能修改數據,然後就一直這麼用。但如果你不是很瞭解 EF 的實體狀態管理機制,就很可能會給自己挖坑,所以一般不推薦這種 CRUD 的寫法。

我多次看到網上有人問諸如 EF 改了數據保存報錯之類的問題,基本都是他自己還沒搞清楚 EF 各個實體狀態的含義,然後就在那兒強制更改實體狀態,然後遇到坑自己還解決不了。這種做法有可能還會破壞 EF 的樂觀併發控制,而且有些版本也不支持這種做法。下麵給出兩個負面案例:

  • 1、不推薦的修改寫法,會更新所有欄位,示例如下:
using (var context = new DemoDbContext()) {
    context.Database.Log = Console.WriteLine;
    var p = new Person() { ID = 3, Name = "Andy" };
    context.Entry(p).State = EntityState.Modified;
    context.SaveChanges(); // 返回受影響行數 1
}

上面的代碼會生成 1 條 UPDATE 語句。

  • 2、不推薦的刪除寫法,示例如下:
using (var context = new DemoDbContext()) {
    var p = new Person() { ID = 1 };
    context.Entry(p).State = EntityState.Deleted;
    context.SaveChanges(); // 返回受影響行數 1
}

上面的代碼會生成 1 條 DELETE 語句。

3.5、EF6 性能優化

  • 1、非跟蹤查詢 AsNoTracking
    預設情況下,EF 會一直跟蹤實體的狀態,這也是為什麼當我們調用SaveChanges()的時候,EF 能夠把最終的數據狀態準確提交到資料庫的原因。但有些時候,我們查詢出數據只是為了做展示,並不需要修改或刪除,這時候就可以調用AsNoTracking()來使得對象為 Detached 狀態,之後 EF 就不再跟蹤這個對象狀態了,在合適的場景下能顯著提升性能。
using (var context = new DemoDbContext()) {
    // 查詢所有人並且不跟蹤他們的狀態
    var p1 = context.Persons.AsNoTracking().ToList();
    // 查詢部分人並且不跟蹤他們的狀態
    var p2 = context.Persons.Where(i => i.NationID == 1).AsNoTracking().ToList();
}
  • 2、EF 預設是開啟了 LoayLazy 的,別手賤關了就行。如下是預設配置:
this.Configuration.ProxyCreationEnabled = true;
this.Configuration.LazyLoadingEnabled = true;

3.6、EF6 開發及調試技巧

  • 1、如果想知道 EF 會執行什麼 SQL 語句,比如是控制台項目,在執行代碼塊中增加如下語句即可:
context.Database.Log = Console.WriteLine;
  • 2、如果是自己測試,可以讓 EF 每次都根據代碼更新資料庫,在上下文構造函數中增加如下代碼即可:
// 當資料庫模型發生改變時,則刪除當前資料庫,重建新的資料庫(實際開發中永遠不要這麼寫,太危險了)
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EFDbContext>());

或者在 CRUD 代碼塊中加入如下代碼,僅當資料庫不存在時,才由 EF 創建資料庫:

context.Database.CreateIfNotExists();

4、總結

本文主要講解瞭如何快速上手 EF6 和基本的 CRUD 操作。用 .NET 技術的博友都知道,如今 .NET 陣營除了經典的 .NET Framework 之外,還有一個開源版的 .NET Core。對應的,EF 也適時地推出了 EF Core 版,如果你的項目是 .NET 的,那就繼續用 EF6 吧,畢竟是久經考驗的版本,而 EF Core 是全新開發的,更適合 .NET Core 類型的項目。而且官方也說從 EF6 到 EF Core 是移植而不是升級。

4.1、MySQL 官方組件的用途說明

  • 1、mysql-connector-net:MySQL Connector/NET 是 MySQL 官方的 .NET 驅動程式,或者說是 MySQL for .NET 的客戶端開發包,其中包含了 .NET 連接 MySQL 所必須的 dll 文件。
  • 2、mysql-for-visualstudio:6.7 以下版本的驅動中會包含該組件,它的作用是在通過 VS 建立實體模型時,在數據源中增加 MySQL 類型選項。如果只用 Code First,那麼就不需要該組件了。
  • 3、mysql-connector-odbc:MySQL Connector/ODBC 使得用戶可以通過 ODBC(Open Database Connectivity,開放資料庫互聯)來連接 MySQL 伺服器。

4.2、本文 Demo 的代碼補充說明

  • 文中的 Nation 實體定義如下:
public class Nation {
    public Int32 ID { get; set; }
    public String Name{ get; set; }
    
}
  • 文中的 MiniPerson 類定義如下:
public class MiniPerson {
    public Int32 ID { get; set; }
    public String Name { get; set; }
}

本文鏈接:http://www.cnblogs.com/hanzongze/p/ef6-trial-report.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!個人博客,能力有限,若有不當之處,敬請批評指正,謝謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這是幾個簡單的例子,但是實際的開發中委托還還只在反射時用到過,事件的話只自己做了一次,並且還是特意去用的 ,實際上可以不用。線程的話,因為需要,所以用的會多點,這裡主要是WS上的線程。 委托 在前面的反射實例中,也有用到委托執行方法。 這個例子是將一個方法或者委托實例作為另外一個函數的入參傳遞。 至 ...
  • 我就不說這個Bug讓我調試了多久,怎麼坑的。。直接說結果。 伺服器上有兩個介面,一個是Web Services(asmx文件)介面,一個是MVC API (普通的GET請求介面) 神奇的事情是這樣的,我迴圈請求兩個介面的時候(不要問我為啥這樣寫),在第三次Web Services 超時。。 for( ...
  • TOTP 介紹及基於C 的簡單實現 Intro TOTP 是基於時間的一次性密碼生成演算法,它由 "RFC 6238" 定義。和基於事件的一次性密碼生成演算法不同 "HOTP" ,TOTP 是基於時間的,它和 HOTP 具有如下關係: 其中: T:T = (Current Unix time T0) / ...
  • 下麵是兩個反射的實例 案例1: 動態調用類中的方法、傳入參數,並獲得返回值。 xxxx:類名 Event:類中的方法 pra1,pra2,pra3:方法對應的入參 DoRet:方法返回的執行結果 案例2: 動態載入外部DLL,並執行其中的方法。 xxx:外部Dll的名字 zzz:Dll中的類名 yy ...
  • 遇到問題: 參照官方文檔敲完命令之後 出現了提示的問題Unable to lpcate package... ,隨後參照官方文檔解決方案,又出現瞭如下問題: 提示找不到依賴的Runtime,在嘗試過很多方法之後,終於在在Github上看到了一位解決過這個問題的方案如下 ...
  • 近期在做了一個winform的項目的附件上傳的需求 最初項目選型的時候有三種 1.使用webservice、webapi上傳文件 2,直接保存在資料庫中 3.使用共用目錄+dos命令 第一種有文件大小限制、設計到的知識比較多,第二種會給資料庫增加不小的壓力,於是最後選了第三種 下麵上關鍵代碼,代碼很 ...
  • redis報錯,Invalid Db Index . 需要清理一下redis, 進入redis文件加下,登錄redis, redis-cli ,輸入賬號密碼, flush all, 回收程式池,重新生成配置項 ...
  • 基礎準備 1.創建asp.net core Web 應用程式選擇Api 2.appsettings.json 配置consul伺服器地址,以及本機ip和埠號信息 3.程式入口(program.cs)配置useurls,ip和port從配置文件(或者命令行中)讀取(命令行啟動方式:dotnet Co ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...