C#.NET ORM 如何访问 Access 数据库 [FreeSql]

时间:2022-09-27 10:07:11

最近很多 .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 https://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 = "http://sample.com" };
blog.BlogId = (int)DB.access.Insert(blog).ExecuteIdentity();

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

DB.access.Delete<Blog>()
    .Where(b => b.Url == "http://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();
}

// 或者

```csharp
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 聊天系统架构 https://github.com/2881099/FreeIM MIT
FreeRedis Redis SDK https://github.com/2881099/FreeRedis MIT
csredis https://github.com/2881099/csredis MIT
FightLandlord 斗DI主网络版 https://github.com/2881099/FightLandlord 学习用途
FreeScheduler 定时任务 https://github.com/2881099/FreeScheduler MIT
IdleBus 空闲容器 https://github.com/2881099/IdleBus MIT
FreeSql ORM https://github.com/dotnetcore/FreeSql MIT
FreeSql.Cloud 分布式tcc/saga https://github.com/2881099/FreeSql.Cloud MIT
FreeSql.AdminLTE 低代码后台生成 https://github.com/2881099/FreeSql.AdminLTE MIT
FreeSql.DynamicProxy 动态代理 https://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 https://github.com/dotnetcore/FreeSql ,可以商用,文档齐全,甚至拿去卖钱也可以。

8500+个单元测试作为基调,支持10多数数据库,我们提供了通用Odbc理论上支持所有数据库,目前已知有群友使用 FreeSql 操作华为高斯、mycat、tidb 等数据库。安装时只需要选择对应的数据库实现包。

轻量化解释:了解 FreeRedis、FreeSql、csredis 的人都知道,我们发布的开源项目是绿色著称,零依赖发布后只有一个DLL,不会造成使用者项目依赖冲突,支持 .NET 4.0 堪称屎山项目的救星。现在还有很多.NET FX4.0 的项目,这些项目因历史遗留原因或硬件限制,不能更换 .NET Core 版本。因此这些项目很难使用到现有的开源库,不能使用可靠的开源库,那么很多时候都要自行实现,在堆积代码的同时,项目也有可能越来越乱,代码越来越渣,项目逐渐变得不稳定。