C#.NET ORM 如何訪問 Access 數據庫 [FreeSql]


最近很多 .net QQ 群無故被封停,特別是 wpf 群幾乎全軍覆沒。依樂祝的 .net6交流群,曉晨的 .net跨平台交流群,導致很多碼友流離失所無家可歸,藉此機會使用一次召喚術,有需要的請加群:560611514【.NET C#愛好者】,6406277【C#/.Net Core社區】,822074314【DotNet開發交流群】

💻 前言

看到標題點進來,也許有人問,為什麼不用 mysql,為什麼不用 sqlite,為什麼不這樣那樣。

其實有時候情非得已,被迫使用不由得自己選擇,沒有誰天天做新項目,新項目當然不會選 Access 數據庫,複雜的業務群體對方發送過來的文件是 Access 也沒得選,難道可以要求對方公司換數據庫?

Microsoft Office Access是由微軟發佈的關係數據庫管理系統。它結合了 MicrosoftJet Database Engine 和 圖形用戶界面兩項特點,是 Microsoft Office 的系統程序之一。

Microsoft Office Access是微軟把數據庫引擎的圖形用戶界面和軟件開發工具結合在一起的一個數據庫管理系統。它是微軟OFFICE的一個成員, 在包括專業版和更高版本的office版本裏面被單獨出售。2018年9月25日,最新的微軟Office Access 2019在微軟Office 2019里發佈。

MS ACCESS以它自己的格式將數據存儲在基於Access Jet的數據庫引擎里。它還可以直接導入或者鏈接數據(這些數據存儲在其他應用程序和數據庫)。


🌳 C#.NET 訪問 Access 數據庫

從 .NETframework 1.0 到現今的 dotnet-7.0,訪問 Access 數據庫都只能用 oledb 方式,微軟歷史訪問數據庫的方式有許多種(ado、odbc、oledb、ado.net),oledb 是其中的一種。

連接字符串常見的有兩種:

  • Provider=Microsoft.Jet.OleDb.4.0;Data Source=d:/accdb/2003.mdb

  • Provider=Microsoft.ACE.OLEDB.12.0;Data Source=d:/accdb/2007.accdb

Access 支持 SQL 語句,使用起來和普通關係型數據庫差不多,由於不想在代碼中寫 SQL,為了讓 crud 操作起來更加便利,決定引入 C#.NET ORM Freesql,因為他支持 .NETFramework 4.0 及以後的所有 dotnet 版本,適應範圍更廣。


🦄 FreeSql 介紹

.NET ORM Object Relational Mapping 是一種為了解決面向對象與關係數據庫存在的互不匹配的現象的技術。

FreeSql .NET ORM 支持 .NetFramework4.0+、.NetCore、Xamarin、MAUI、Blazor、以及還有說不出來的運行平台,因為代碼綠色無依賴,支持新平台非常簡單。目前單元測試數量:8500+,Nuget下載數量:1M+。使用最寬鬆的開源協議 MIT //github.com/dotnetcore/FreeSql ,可以商用,文檔齊全,甚至拿去賣錢也可以。

FreeSql 主要優勢在於易用性上,基本是開箱即用,在不同數據庫之間切換兼容性比較好,整體的功能特性如下:

  • 支持 CodeFirst 對比結構變化遷移、DbFirst 從數據庫生成實體類;
  • 支持 豐富的表達式函數,獨特的自定義解析;
  • 支持 批量添加、批量更新、BulkCopy、導航屬性,貪婪加載、延時加載、級聯保存、級聯刪除;
  • 支持 讀寫分離、分表分庫,租戶設計,分佈式事務;
  • 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/達夢/神通/人大金倉/翰高/Clickhouse/MsAccess Ado.net 實現包,以及 Odbc 的專門實現包;

8000+個單元測試作為基調,支持10多數數據庫,我們提供了通用Odbc理論上支持所有數據庫,目前已知有群友使用 FreeSql 操作華為高斯、mycat、tidb 等數據庫。安裝時只需要選擇對應的數據庫實現包:

dotnet add packages FreeSql.Provider.MsAccess

public class DB
{
    static Lazy<IFreeSql> accessLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder()
        .UseConnectionString(FreeSql.DataType.MsAccess, "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=d:/accdb/2007.accdb")
        //.UseAutoSyncStructure(true) 自動建表,適合新項目
        .UseNoneCommandParameter(true)
        .UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
        .Build());
    public static IFreeSql access => accessLazy.Value;
}

定義 DB.cs 類之後就可以快樂的 CRUD 了。FreeSql 提供多種 CRUD 使用習慣,請根據實際情況選擇團隊合適的一種:

  • 要麼 FreeSql,原始用法;
  • 要麼 FreeSql.Repository,倉儲 + 工作單元習慣;
  • 要麼 FreeSql.DbContext,很像 EFCore 的使用習慣,兼容 EFCore 99% 的實體註解;
  • 要麼 FreeSql.BaseEntity,充血模式;
  • 要麼 直接像 dapper 那樣使用 SqlConnection 擴展方法;

⚡ CRUD 模式一:原始用法 API

DB.access.Select<T>(); //查詢
DB.access.Insert<T>(); //插入
DB.access.Update<T>(); //更新
DB.access.Delete<T>(); //刪除
DB.access.InsertOrUpdate<T>()// 插入或更新
DB.access.Transaction(..); //事務

DB.access.CodeFirst; //CodeFirst 對象
DB.access.DbFirst; //DbFirst 對象
DB.access.Ado; //Ado 對象
DB.access.Aop; //Aop 對象
DB.access.GlobalFilter; //全局過濾器對象


var blogs = DB.access.Select<Blog>()
    .Where(b => b.Rating > 3)
    .OrderBy(b => b.Url)
    .Page(2, 10)
    .ToList();

var blog = new Blog { Url = "//sample.com" };
blog.BlogId = (int)DB.access.Insert(blog).ExecuteIdentity();

DB.access.Update<Blog>()
    .Set(b => b.Url, "//sample2222.com")
    .Where(b => b.Url == "//sample.com")
    .ExecuteAffrows();

DB.access.Delete<Blog>()
    .Where(b => b.Url == "//sample.com")
    .ExecuteAffrows();

// 等等等。。級聯保存、級聯查詢、導航屬性。。。

⛳ CRUD 模式二:倉儲 + 工作單元

FreeSql.Repository 作為擴展,實現了通用倉儲層功能。與其他規範標準一樣,倉儲層也有相應的規範定義。FreeSql.Repository 參考 abp vnext 接口,定義和實現基礎的倉儲層(CURD),算比較通用的方法。

  • Select/Attach 快照對象,Update 只更新變化的字段;
  • Insert 插入數據,適配各數據庫優化執行 ExecuteAffrows/ExecuteIdentity/ExecuteInserted;
  • InsertOrUpdate 插入或更新;
  • SaveMany 方法快速保存導航對象(一對多、多對多);
  • 工作單元管理事務
//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFreeSql>(DB.access);
    services.AddScoped<UnitOfWorkManager>();
    services.AddFreeRepository(null, typeof(Startup).Assembly);
   //批量注入 Service
}


public class SongService
{
    readonly IBaseRepository<Song> _repoSong;
    readonly IBaseRepository<Detail> _repoDetail;

    public SongService(IBaseRepository<Song> repoSong, IBaseRepository<Detail> repoDetail)
    {
        _repoSong = repoSong;
        _repoDetail = repoDetail;
    }

    [Transactional]
    public virtual void Test1()
    {
        //這裡 _repoSong、_repoDetail 所有操作都是一個工作單元
        this.Test2();
    }

    [Transactional(Propagation = Propagation.Nested)]
    public virtual void Test2() //嵌套事務
    {
        //這裡 _repoSong、_repoDetail 所有操作都是一個工作單元
    }
}
屬性 返回值 說明
EntityType Type 倉儲正在操作的實體類型,注意它不一定是 TEntity
UnitOfWork IUnitOfWork 正在使用的工作單元
Orm IFreeSql 正在使用的 Orm
DbContextOptions DbContextOptions 正在使用的 DbContext 設置,修改設置不影響其他
DataFilter IDataFilter<TEntity> 倉儲過濾器,本對象內生效
UpdateDiy IUpdate<TEntity> 準備更新數據,與倉儲同事務
Select ISelect<TEntity> 準備查詢數據
方法 返回值 參數 說明
AsType void Type 改變倉儲正在操作的實體類型
Get TEntity TKey 根據主鍵,查詢數據
Find TEntity TKey 根據主鍵,查詢數據
Delete int TKey 根據主鍵刪除數據
Delete int Lambda 根據 lambda 條件刪除數據
Delete int TEntity 刪除數據
Delete int IEnumerable<TEntity> 批量刪除數據
DeleteCascadeByDatabase List<object> Lambda 根據導航屬性遞歸數據庫刪除數據
Insert TEntity 插入數據,若實體有自增列,插入後的自增值會填充到實體中
Insert IEnumerable<TEntity> 批量插入數據
Update TEntity 更新數據
Update IEnumerable<TEntity> 批量更新數據
InsertOrUpdate TEntity 插入或更新數據
FlushState 清除狀態管理數據
Attach TEntity 附加實體到狀態管理,可用於不查詢就更新或刪除
Attach IEnumerable<TEntity> 批量附加實體到狀態管理
AttachOnlyPrimary TEntity 只附加實體的主鍵數據到狀態管理
SaveMany TEntity, string 保存實體的指定 ManyToMany/OneToMany 導航屬性(完整對比)
BeginEdit List<TEntity> 準備編輯一個 List 實體
EndEdit int 完成編輯數據,進行保存動作

狀態管理,可實現 Update 只更新變化的字段(不更新所有字段),靈活使用 Attach 和 Update 用起來非常舒服。


⚡ CRUD 模式三:DbContext

FreeSql.DbContext 實現類似 EFCore 使用習慣,跟蹤對象狀態,最終通過 SaveChanges 方法提交事務。

FreeSql 可自動識別 EFCore 實體特性 Key/Required/NotMapped/MaxLength/StringLength/DatabaseGenerated/Table/Column。

  • Select/Attach 快照對象,Update 只更新變化的字段;
  • Add/AddRange 插入數據,適配各數據庫優化執行 ExecuteAffrows/ExecuteIdentity/ExecuteInserted;
  • AddOrUpdate 插入或更新;
  • SaveMany 方法快速保存導航對象(一對多、多對多);
using (var ctx = DB.oracle.CreateDbContext()) {
  //var db1 = ctx.Set<Song>();
  //var db2 = ctx.Set<Tag>();

  var item = new Song { };
  ctx.Add(item);
  ctx.SaveChanges();
}

// 或者

public class SongContext : DbContext {

  public DbSet<Song> Songs { get; set; }
  public DbSet<Tag> Tags { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder builder) {
    builder.UseFreeSql(DB.oracle);
  }
  
  //每個 DbContext 只觸發一次
  protected override void OnModelCreating(ICodeFirst codefirst)
  {
    codefirst.Entity<Song>(eb =>
    {
      eb.ToTable("tb_song");
      eb.Ignore(a => a.Field1);
      eb.Property(a => a.Title).HasColumnType("varchar(50)").IsRequired();
      eb.Property(a => a.Url).HasMaxLength(100);
    }
  }
}

提示:FreeSql 兼容 EFCore 99% 的實體特性


🌴 CRUD 模式四:BaseEntity

BaseEntity 是一種極簡單的 CodeFirst 開發方式,特別對單表或多表CRUD,利用繼承節省了每個實體類的重複屬性(創建時間、ID等字段),軟件刪除等功能,進行 crud 操作時不必時常考慮倉儲的使用;

dotnet add package FreeSql.Extensions.BaseEntity

public class UserGroup : BaseEntity<UserGroup, int>
{
    public string GroupName { get; set; }
}

//添加
var item = new UserGroup { GroupName = "組一" };
item.Insert();

//更新
item.GroupName = "組二";
item.Update();

//添加或更新
item.Save();

//軟刪除
item.Delete();

//恢復軟刪除
item.Restore();

//根據主鍵獲取對象
var item = UserGroup.Find(1);

//查詢數據
var items = UserGroup.Where(a => a.Id > 10).ToList();

📃 CRUD 模式五:SqlConnection 擴展方法(類似 Dapper)

提供了類似 Dapper 的使用方法,FreeSql 增加了 IDbConnection/IDbTransaction 對象的擴展方法 Select/Insert/Update/Delete 實現 CRUD。

using FreeSql;

using (var conn = new SqlConnection(...))
{
  conn.Select<T>().Where(...).ToList();

  conn.Insert(new T {}).ExecuteAffrows();
  conn.Update().SetSource(new T {}).ExecuteAffrows();
  conn.InsertOrUpdate().SetSource(new T {}).ExecuteAffrows();

  conn.Delete<T>().Where(...).ExecuteAffrows();
}
  • 每個 SqlConnection GetFreeSql() 返回的 IFreeSql 實例相同;
  • 可以對 fsql 設置 Aop 事件,比如監視 SQL;
  • IFreeSql 自身的成員 IDbFirst、Transaction 不可用;

利用本功能可以快速將 FreeSql 使用到項目中,只需要處理好實體類的特性。


🌈 結束語

作者是什麼人?

作者是一個入行 18年的老批,他目前寫的.net 開源項目有:

開源項目 描述 開源地址 開源協議
FreeIM 聊天系統架構 //github.com/2881099/FreeIM MIT
FreeRedis Redis SDK //github.com/2881099/FreeRedis MIT
csredis //github.com/2881099/csredis MIT
FightLandlord 斗DI主網絡版 //github.com/2881099/FightLandlord 學習用途
FreeScheduler 定時任務 //github.com/2881099/FreeScheduler MIT
IdleBus 空閑容器 //github.com/2881099/IdleBus MIT
FreeSql ORM //github.com/dotnetcore/FreeSql MIT
FreeSql.Cloud 分佈式tcc/saga //github.com/2881099/FreeSql.Cloud MIT
FreeSql.AdminLTE 低代碼後台生成 //github.com/2881099/FreeSql.AdminLTE MIT
FreeSql.DynamicProxy 動態代理 //github.com/2881099/FreeSql.DynamicProxy 學習用途

需要的請拿走,這些都是最近幾年的開源作品,以前更早寫的就不發了。

FreeSql .NET ORM 支持 .NetFramework4.0+、.NetCore、Xamarin、MAUI、Blazor、以及還有說不出來的運行平台,因為代碼綠色無依賴,支持新平台非常簡單。目前單元測試數量:8500+,Nuget下載數量:1M+。QQ群:4336577(已滿)、8578575(在線)、52508226(在線)

  • 支持 CodeFirst 模式;
  • 支持 DbFirst 模式,支持從數據庫導入實體類,或使用實體類生成工具生成實體類;
  • 支持 豐富的表達式函數,以及靈活的自定義解析;
  • 支持 導航屬性一對多、多對多貪婪加載,延時加載,級聯保存,級聯刪除;
  • 支持 讀寫分離、分表分庫、過濾器、樂觀鎖、悲觀鎖、分佈式事務、多租戶(按字段/表/庫);
  • 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/達夢/人大金倉/神舟通用/南大通用/翰高/華為高斯/ClickHouse/Access 等數據庫;

FreeSql 使用最寬鬆的開源協議 MIT //github.com/dotnetcore/FreeSql ,可以商用,文檔齊全,甚至拿去賣錢也可以。

8500+個單元測試作為基調,支持10多數數據庫,我們提供了通用Odbc理論上支持所有數據庫,目前已知有群友使用 FreeSql 操作華為高斯、mycat、tidb 等數據庫。安裝時只需要選擇對應的數據庫實現包。

輕量化解釋:了解 FreeRedis、FreeSql、csredis 的人都知道,我們發佈的開源項目是綠色著稱,零依賴發佈後只有一個DLL,不會造成使用者項目依賴衝突,支持 .NET 4.0 堪稱屎山項目的救星。現在還有很多.NET FX4.0 的項目,這些項目因歷史遺留原因或硬件限制,不能更換 .NET Core 版本。因此這些項目很難使用到現有的開源庫,不能使用可靠的開源庫,那麼很多時候都要自行實現,在堆積代碼的同時,項目也有可能越來越亂,代碼越來越渣,項目逐漸變得不穩定。