如何使用 FreeSql 无缝接替 EF Core ?

如何使用 FreeSql 无缝接替 EF Core ?

编码文章call10242025-07-13 4:01:554A+A-

Gradual evolution

项目说明

  • 实现目标:使用 FreeSql 无缝接替 EF Core,并实现数据表的 CRUD 操作;

接下来我们先回顾下 EF Core 中如何实现数据表的 CRUD 操作,在操作之前我们先把数据库和相关表结构准备好。

DB & 数据表结构

  • 数据库实例名称:Test
  • 数据表结构:User

此处为了方便演示,创建一个简单的 User 表,数据结构如下:

字段说明
Id主键,用户ID
Name用户姓名

DB & 数据表创建

创建数据库实例 TestUser 表,执行如下 sql 脚本:

USE master; 
GO

-- 创建 Test
CREATE DATABASE Test
ON
( NAME = Test_dat,
FILENAME = 'D:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\testdat.mdf',
SIZE = 10,
MAXSIZE = 50,
FILEGROWTH = 5 )
LOG ON
( NAME = Test_log,
FILENAME = 'D:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\testlog.ldf',
SIZE = 5MB,
MAXSIZE = 25MB,
FILEGROWTH = 5MB );
GO

CREATE TABLE Test.dbo.[User] (
Id varchar(36) NOT primary key,
Name varchar(20) NOT
);
EXEC Test.sys.sp_addextendedproperty 'MS_Description', N'用户信息', 'schema', N'dbo', 'table', N'User';
GO

执行上面 sql 脚本创建数据库失败时,可以先执行如下 sql 脚本,然后在继续执行上面步骤。

-- 关闭数据库连接并删除数据库
ALTER DATABASE Test SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE Test;

除了上面常规的 mssql 配置,开发环境为了方便快捷,还可以使用 Docker 运行 mssql 镜像(
mcr.microsoft.com/mssql/server
),此处我是配置的 Docker 环境(这里不做详细介绍,感兴趣的小伙伴自行查看资料):

docker-mssql-2017

到这里我们就把基本的开发环境准备好了,接下来就是新建项目相关环节的操作。

数据表 User 实体模型创建

依据上面的 User 表结构,在(新建)项目
Jeff.Mes.EntityFrameworkCore
中添加 C# 模型类:

using System.ComponentModel.DataAnnotations.Schema;

namespace Jeff.Mes.EntityFrameworkCore;

/// <summary>
/// 用户信息表
/// </summary>
[Table("User")]
public class User
{
/// <summary>
/// 主键,用户ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 用户姓名
/// </summary>
public string Name { get; set; }
}

使用 EF Core 实现 User 表新增用户信息

上面我们准备好 Test 数据库、User 表和 C# 实体模型结构 后,接下来准备编写 EF Core 相应的操作代码。

添加 EF Core 相关的 nuget 包

说明:此处环境使用的 SqlServer 数据库,其他类型的数据库需添加相应的 nuget 包。

接着我们继续在上面新建的项目【
Jeff.Mes.EntityFrameworkCore
】中改造,添加 nuget 包:

  • Microsoft.EntityFrameworkCore.SqlServer
NuGet\Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 6.0.14
efcore.sqlserver

编写 EF Core 操作 User 表的 CRUD 代码

项目【
Jeff.Mes.EntityFrameworkCore
】中添加 FreeSqlNuget 包:

  • FreeSql.Provider.SqlServer
NuGet\Install-Package FreeSql.Provider.SqlServer -Version 3.2.687
FreeSql.Provider.SqlServer

在项目【
Jeff.Mes.EntityFrameworkCore
】中新建 TestContext 类,继承 DbContext,实现如下:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;

namespace Jeff.Mes.EntityFrameworkCore;

/// <summary>
/// Test 数据库上下文对象
/// </summary>
public class TestContext : DbContext
{
public TestContext() { }

/// <summary>
/// 用户信息表
/// </summary>
public virtual DbSet<User> User { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// db 连接字符串,其中符号 “***” 代表数据库访问密码
string dbConnString = "Data Source=.;Initial Catalog=Test;Persist Security Info=True;User ID=sa;Password=***";
optionsBuilder.UseSqlServer(dbConnString);
}
}

/// <summary>
/// 【EF Core 模式】添加用户测试
/// </summary>
/// <returns></returns>
public async Task<int> AddUserAsync()
{
using (var dbContext = new TestContext())
{
var guid = Guid.NewGuid().ToString();
var user = new User
{
Id = guid,
Name = $"efcore-{guid.Substring(0, 5)}"
};
dbContext.Add(user);
return await dbContext.SaveChangesAsync();
}
}

// 注意对比下面的,【FreeSql 模式】添加用户测试
}

上面操作 User 表的 CRUD 代码,为了简化此处只写 AddUserAsync 添加操作,其他代码就不在详细介绍。

FreeSql 使用 DbContext 接替 EF Core

Entity Framework(简称 EF) 中创建模型后,应用程序所交互的主要类是
System.Data.Entity.DbContext
(通常称为 上下文类 )。默认情况下,上下文管理与数据库的连接。

单例对象构造器 SingletonConstructor

单例对象构造器 SingletonConstructor 代码如下:

namespace Jeff.Mes.Common;

/// <summary>
/// 单例对象构造器
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonConstructor<T> where T : class, new()
{
private static T? _Instance;
private readonly static object _lockObj = new();

/// <summary>
/// 获取单例对象的实例
/// </summary>
/// <returns></returns>
public static T GetInstance()
{
if (_Instance != ) return _Instance;
lock (_lockObj)
{
if (_Instance == )
{
var item = System.Activator.CreateInstance<T>();
System.Threading.Interlocked.Exchange(ref _Instance, item);
}
}
return _Instance;
}
}

使用 SingletonConstructor 构建 IFreeSql

新建 FreeSqlHelper.cs 文件,使 FreeSqlHelper 类继承自 SingletonConstructor 单例构造器,代码示例如下:

using System.Collections.Concurrent;
using FreeSql;
using Jeff.Mes.Common;

namespace Jeff.Mes.DbHelper;

/// <summary>
/// 【Singleton 单例模式】构建 freesql 对象
/// </summary>
public sealed class FreeSqlHelper : SingletonConstructor<FreeSqlHelper>
{
//连接字符串作为 key,存储构建的 IFreeSql 对象的字典集合
private readonly static ConcurrentDictionary<string, IFreeSql> _FreeDic = new();

#region 构建 freesql 对象
public IFreeSql? FreeBuilder(string dbType, string connStr)
{
if (string.IsOrWhiteSpace(dbType) || string.IsOrWhiteSpace(connStr))
{
return default;
}

bool isOk = _FreeDic.TryGetValue(connStr, out IFreeSql? fsql);
if (isOk)
{
return fsql;
}

DataType dataType;
string myDbType = dbType.Contains('.') ? dbType.Substring(dbType.LastIndexOf('.') + 1) : dbType;
switch (myDbType.ToLower())
{
case "mysql":
dataType = DataType.MySql;
break;

default:
dataType = DataType.SqlServer;
break;
}

return FreeBuilder(dataType, connStr);
}

public IFreeSql? FreeBuilder(DataType dbType, string connStr)
{
if (string.IsOrWhiteSpace(connStr))
{
return default;
}

/*
bool hasKey = _FreeDic.ContainsKey(connStr);
if (hasKey)
{
return _FreeDic[connStr];
}*/


bool isOk = _FreeDic.TryGetValue(connStr, out IFreeSql? fsql);
if (isOk)
{
return fsql;
}

fsql = new FreeSqlBuilder()
.UseConnectionString(dbType, connStr)
.UseAutoSyncStructure(false) //自动同步实体结构到数据库
.Build(); //请务必定义成 Singleton 单例模式

bool isAdd = _FreeDic.TryAdd(connStr, fsql);
if (isAdd)
{
return fsql;
}
else
{
fsql.Dispose();
return _FreeDic[connStr];
}
}

public (bool isOk, IFreeSql? fsql) GetFreeSql(DataType dbType, string connStr)
{
bool isOk = _FreeDic.TryGetValue(connStr, out IFreeSql? fsql);
if (!isOk)
{
fsql = FreeBuilder(dbType, connStr);
isOk = fsql != ;
}
return (isOk, fsql ?? default);
}
#endregion
}

使用 FreeSqlHelper 对象构建 IFreeSql,如下代码:

 IFreeSql fsql = FreeSqlHelper.GetInstance().FreeBuilder(dbType, connStr);

上面环节我们就准备好了 IFreeSql 对象的构建,万事俱备,只欠东风(下面我们解析解析 DbContext 获取有效信息);

反射解析 DatabaseFacade

在反射解析 DbContext 对象之前,我们先回顾下相关概念定义和类库基本信息。

EF Core 中 DbContext 官方定义

  • Microsoft.EntityFrameworkCore.DbContext

上面我简单的介绍了 DbContextEF Core数据库连接对象,接下来我们看下官方定义:

  • 命名空间:Microsoft.EntityFrameworkCore
  • 程序集:Microsoft.EntityFrameworkCore.dll
  • Nuget 包:Microsoft.EntityFrameworkCore v7.0.0

DbContext 实例表示与数据库的会话,可用于查询和保存实体的实例。DbContext 是工作单元和存储库模式的组合。

public class DbContext : IAsyncDisposable, 
IDisposable, Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>,
Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies,
Microsoft.EntityFrameworkCore.Internal.IDbContextPoolable,
Microsoft.EntityFrameworkCore.Internal.IDbSetCache

关于 DbContext 更多信息,请查看 =》
https://learn.microsoft.com/zh-cn/dotnet/api/microsoft.entityframeworkcore.dbcontext?view=efcore-7.0

EF Core 中 DatabaseFacade 官方定义

  • Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade

DatabaseFacade 类定义如下:

  • 命名空间:Microsoft.EntityFrameworkCore.Infrastructure
  • 程序集:Microsoft.EntityFrameworkCore.dll
  • Nuget 包:Microsoft.EntityFrameworkCore v7.0.0

提供对上下文的数据库相关信息和操作的访问。此类的实例通常是从 Database 中获取的,它不是在应用程序代码中直接构造的。

public class DatabaseFacade : 
Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>,
Microsoft.EntityFrameworkCore.Infrastructure.IResettableService,
Microsoft.EntityFrameworkCore.Storage.IDatabaseFacadeDependenciesAccessor

关于 DatabaseFacade 更多信息,请查看 =》
https://learn.microsoft.com/zh-cn/dotnet/api/microsoft.entityframeworkcore.infrastructure.databasefacade?view=efcore-7.0

反射解析对象 DatabaseFacade

  • Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade

添加反射解析 DatabaseFacade 对象的代码:

/// <summary>
/// 根据对象实例和属性名称获得属性值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="property"></param>
/// <returns></returns>
public static T? GetProperty<T>(this object obj, string property)
{
var result = default(T);
try
{
var t = obj.GetType();
var propertyObj = t.GetProperty(property)?.GetValue(obj, );
result = (T?)propertyObj.ChangeType(typeof(T));
return result;
}
catch(Exception ex)
{
throw ex;
}
}

/// <summary>
/// 类型转换
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object? ChangeType(this object? value, Type type)
{
if (value == && type.IsGenericType) return Activator.CreateInstance(type);
if (value == ) return ;
if (type == value.GetType()) return value;
if (type.IsEnum)
{
if (value is string valEnum) return Enum.Parse(type, valEnum);
else return Enum.ToObject(type, value);
}
if (!type.IsInterface && type.IsGenericType)
{
Type innerType = type.GetGenericArguments()[0];
object? innerValue = ChangeType(value, innerType);
return Activator.CreateInstance(type, new object?[] { innerValue });
}
if (value is string valGuid && type == typeof(Guid)) return new Guid(valGuid);
if (value is string valVersion && type == typeof(Version)) return new Version(valVersion);
if (value is Guid && type == typeof(string)) return value.ToString();
if (value is not IConvertible) return value;

if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(able<>)))
{
var underlyingType = able.GetUnderlyingType(type);
type = underlyingType ?? type;
} // end if
return Convert.ChangeType(value, type);
}

继续在 TestContext.cs 类中添加如下代码:

/// <summary>
/// 【FreeSql 模式】添加用户测试
/// </summary>
/// <returns></returns>
public async Task<int> FreeSqlAddUserAsync()
{
using (var dbContext = new TestContext())
{
var (myDbContext, fsql) = GetDbContext(dbContext);

var guid = Guid.NewGuid().ToString();
var user = new User
{
Id = guid,
Name = $"fsql-{guid.Substring(0, 5)}"
};

// fsql.Insert(user).AsTable("User").ExecuteAffrowsAsync();
return await fsql.Insert(user).ExecuteAffrowsAsync();
}
}

/// <summary>
/// 反射获取信息,并构建 FreeSql
/// </summary>
/// <param name="dbContext"></param>
/// <returns></returns>
public (DbContext dbContext, IFreeSql fsql) GetDbContext(DbContext dbContext)
{
/*
((EQuality.Framework.Dal.EFDbContext)_IDbContext).ConnStr
((Microsoft.EntityFrameworkCore.DbContext)ss).Database.ProviderName
*/


//- Database
// {Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade}
// Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade
var dbFacade = dbContext.GetProperty<DatabaseFacade>("Database");
string dbType = dbFacade.ProviderName; // db 类型
string connStr = dbContext.Database.GetConnectionString(); // 获取 db 连接字符串
IFreeSql fsql = FreeSqlHelper.GetInstance().FreeBuilder(dbType, connStr); // 单例构造器构建 IFreeSql 对象

return (dbContext, fsql);
}

此处项目【
Jeff.Mes.EntityFrameworkCore
】中 TestContext.cs 类已经完成两种 orm 模式下新增 User 信息的操作,分别是:

  • AddUserAsync,【EF Core 模式】添加用户测试;
  • FreeSqlAddUserAsync,【FreeSql 模式】添加用户测试;

到这里我们就可以使用 FreeSql 无缝替换 EF Core 的操作,同时也保留了 EF Core 模式的玩法,小伙伴们又可以继续愉快的玩耍了哟。

测试 orm 两种模式下的操作

添加 User 用户信息

新增控制台项目【
Jeff.Mes.EntityFrameworkCore.Test
】,项目引用【
Jeff.Mes.EntityFrameworkCore
】,添加如下代码:

namespace Jeff.Mes.EntityFrameworkCore.Test;

internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello, EF Core & FreeSQL!");

var test = new TestContext();
int rcount1 = await test.AddUserAsync();
string info1 = rcount1 > 0 ? "User 信息添加成功" : "User 信息添加失败";
Console.WriteLine($"efcore 模式:{info1}");

int rcount2 = await test.FreeSqlAddUserAsync();
string info2 = rcount2 > 0 ? "User 信息添加成功" : "User 信息添加失败";
Console.WriteLine($"fsql 模式:{info2}");

Console.ReadKey();
}
}

使用 vs 【工具 => 连接到数据库】测试数据库连接是否能正常通信访问,测试如下:

vs 连接数据库

启动控制台,执行代码,输出如下信息:

Hello, EF Core & FreeSQL!
efcore 模式:User 信息添加成功
fsql 模式:User 信息添加成功
控制台测试

使用数据库客户端工具 DBeaver 查看:

user 表数据

FreeSql 查询 User 用户信息

使用 FreeSQL 查询 User 表信息:

/// <summary>
/// 【FreeSql 模式】查询用户信息
/// </summary>
/// <returns></returns>
public async Task<List<User>> FreeSqlGetUserAsync()
{
using (var dbContext = new TestContext())
{
var (myDbContext, fsql) = GetDbContext(dbContext);
return await fsql.Select<User>().ToListAsync();
}
}

控制台 Main 函数中调用 FreeSqlGetUserAsync 方法:

fsql 查询 user 信息

关于 FreeSql 更多信息,请查看相关文档

  • FreeSql 官方文档,https://freesql.net/
  • FreeSql 博客文档,https://www.cnblogs.com/FreeSql/p/11531300.html

总结

在使用 EF Core 作为默认的 ORM 工具操作数据库时,项目中我们或许只能接触到 DbContext 对象,没法直接获取 db 数据库连接信息,假如有小伙伴想接入 FreeSQL 继续使用熟悉的模式,那该怎么办呢?此时我们可以这样操作,为了不影响原有项目结构的操作,又想接入 FreeSQL 的小伙伴们,通过上面的方式我们就可以使用 FreeSQL 无缝替换 EF Core

其实接入 FreeSQL 的方式很简单,只需具备两点条件即可,首先就是 有效的 db 连接字符串
dbContext.Database.GetConnectionString()
】,其次就是获取对应的 数据库类型dbFacade.ProviderName】。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4