EFCore 和 FreeSql 都是 ORM, 在各自领域都有着独特的优势.
问题起源
假设某项目是使用 EFCore 开发的, 且实体 特性或 FluentApi 都配置好了, 如:
- protected override void MapTable( EntityTypeBuilder builder ) {
- builder.ToTable( "cg_kssqbs" ); // 实体表名有单独定义
- }
此时用 FreeSql 操作实体会报错: 数据库表不存在. 除非又配置一套 FreeSql 的 特性或 FluentApi, 这显然会比较麻烦.
问: 为什么不统一, 非要各自定义标准?
答: 每个 ORM 的理念不同, 提供的功能也不尽相同, FreeSql 的理念是 "打造 .NETCore 最方便的 ORM". 与 EFCore 相比只提供了极少的特性配置 (如: 主键, 自增, 类型, 别名, 可空), 并且这些设定针对已现实的数据库都是一致的. 因此 FreeSql 有单独一套简单的实体配置语法, 特别声明: 方便, 简单指的是上手简单, 并非说 FreeSql 功能简单.
来自 Issues 4 《建议能结合 EF Core 的一些特性来弄 #4》 https://github.com/2881099/FreeSql/issues/4
解决方法
1, 关闭 FreeSql 迁移功能
- IFreeSql fsql = new FreeSql.FreeSqlBuilder()
- .UseConnectionString(FreeSql.DataType.SQLite, @"Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=10")
- //.UseAutoSyncStructure(true) // 自动同步实体结构到数据库, 这行一定要关闭
- .Build();
原因是 EFCore 与 FreeSql 迁移会发生冲突, 那边迁移好了, 这边又迁移的逻辑显然不对.
2, 读取 EFCore 的实体配置数据
Microsoft.EntityFrameworkCore.Metadata.IModel 可以获取 EFCore 的实体配置数据;
FreeSql 已实现了 特性 + FluentApi 配置实体, 参考: https://github.com/2881099/FreeSql/wiki/CodeFirst;
ICodeFirst.ConfigEntity 方法可以在程序运行中配置, 从而改变实体的映射
以扩展类库的方式现实需求代码如下:
- public static void ConfigEntity(this ICodeFirst codeFirst, IModel efmodel) {
- foreach (var type in efmodel.GetEntityTypes()) {
- codeFirst.ConfigEntity(type.ClrType, a => {
- // 表名
- var relationalTableName = type.FindAnnotation("Relational:TableName");
- if (relationalTableName != null)
- a.Name(relationalTableName.Value?.ToString() ?? type.ClrType.Name);
- foreach (var prop in type.GetProperties()) {
- var freeProp = a.Property(prop.Name);
- // 列名
- var relationalColumnName = prop.FindAnnotation("Relational:ColumnName");
- if (relationalColumnName != null)
- freeProp.Name(relationalColumnName.Value?.ToString() ?? prop.Name);
- // 主键
- freeProp.IsPrimary(prop.IsPrimaryKey());
- // 自增
- freeProp.IsIdentity(
- prop.ValueGenerated == ValueGenerated.Never ||
- prop.ValueGenerated == ValueGenerated.OnAdd ||
- prop.GetAnnotations().Where(z =>
- z.Name == "SqlServer:ValueGenerationStrategy" && z.Value.ToString().Contains("IdentityColumn") //sqlserver 自增
- || z.Value.ToString().Contains("IdentityColumn") // 其他数据库实现未经测试
- ).Any()
- );
- // 可空
- freeProp.IsNullable(prop.AfterSaveBehavior != PropertySaveBehavior.Throw);
- // 类型
- var relationalColumnType = prop.FindAnnotation("Relational:ColumnType");
- if (relationalColumnType != null) {
- var dbType = relationalColumnType.ToString();
- if (!string.IsNullOrEmpty(dbType)) {
- var maxLength = prop.FindAnnotation("MaxLength");
- if (maxLength != null)
- dbType += $"({maxLength})";
- freeProp.DbType(dbType);
- }
- }
- }
- });
- }
- }
测试
- public class Song {
- [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; set; }
- public string Title { get; set; }
- }
- public class SongContext : DbContext {
- public DbSet<Song> Songs { get; set; }
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
- optionsBuilder.UseSqlite(@"Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=10");
- }
- }
- Console.WriteLine(fsql.Insert<Song>().AppendData(new Song()).ToSql());
- //INSERT INTO "Song"("Id", "Title") VALUES(@Id0, @Title0)
- using (var sdb = new SongContext()) {
- Fsql.CodeFirst.ConfigEntity(sdb.Model);
- //ps: 只需要配置一次
- }
- Console.WriteLine(fsql.Insert<Song>().AppendData(new Song()).ToSql());
- "INSERT INTO"Songs"("Title") VALUES(@Title0)"
- // 此处配置已生效, Id 为自增时不插入, 表名也改名了 Songs
有几个问题
本人对 EF 不太熟, 有几个问题请教:
1,EFCore 是不是非要定义 DBContext 来使用?
2,Microsoft.EntityFrameworkCore.Metadata.IModel 有没有变化通知或拦截的方法? 简化配置;
3,EFCore 自增各种数据库的现实貌似有差异?
结束语
感谢观看, 以上是我的解决思路, 如果有更好的建议或方法欢迎讨论.
FreeSql 虽然目前的版本发布为 0.xx, 但功能和可能性已经较高了.
- GitHub: https://github.com/2881099/FreeSql
- Wiki: https://github.com/2881099/FreeSql/wiki
来源: https://www.cnblogs.com/kellynic/p/10384165.html