zoukankan      html  css  js  c++  java
  • 在 EFCore 定义的实体中进行 FreeSql 开发

    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》

    解决方法

    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 的实体配置数据

    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
    

    示范项目已整理上传到 Github/Examples/efcore_to_freesql

    有几个问题

    本人对 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

  • 相关阅读:
    贫血,充血模型的解释以及一些经验(非常经典)(非原创)
    源代码管理安装大全
    20条常见的编码陷阱 你中枪了没?(转)
    从30岁到35岁:为你的生命多积累一些厚度(转)
    Model1 与Model2(转)
    白话MVP(转帖)
    stl string 使用
    TerminateThread不要使用的證據
    C++静态成员函数小结(转)
    C/C++必知必会1
  • 原文地址:https://www.cnblogs.com/kellynic/p/10384165.html
Copyright © 2011-2022 走看看