EF Core 6.0中SQLite将byte[]转Base64的迁移数据保留问题
在EF Core 6.0中迁移SQLite的byte[]列到Base64字符串(保留数据)
问题1:能否让EF Core调用SQLite扩展实现方案?
可以,按以下步骤操作:
- 启用SQLite扩展加载:在DbContext配置中开启扩展支持,确保连接允许加载扩展:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=your_database.db", sqliteOptions => sqliteOptions.EnableExtensions()); }
也可以在连接字符串中添加EnableExtensions=1参数来开启。
- 自定义迁移实现数据转换:生成迁移类,在
Up方法中先加载sqlean-crypto扩展,再通过临时列完成数据迁移:
public partial class ConvertByteArrayToBase64 : Migration { protected override void Up(MigrationBuilder migrationBuilder) { // 加载sqlean-crypto扩展(注意匹配平台:Windows为sqlean-crypto.dll,Linux为.so,macOS为.dylib) migrationBuilder.Sql("LOAD EXTENSION 'sqlean-crypto';"); // 添加临时字符串列 migrationBuilder.AddColumn<string>( name: "Temp_Base64Column", table: "YourTargetTable", type: "TEXT", nullable: true); // 将byte[]列转换为Base64字符串存入临时列 migrationBuilder.Sql(@" UPDATE YourTargetTable SET Temp_Base64Column = encode(OldByteArrayColumn, 'base64'); "); // 删除原byte[]列 migrationBuilder.DropColumn( name: "OldByteArrayColumn", table: "YourTargetTable"); // 重命名临时列为目标列名 migrationBuilder.RenameColumn( name: "Temp_Base64Column", table: "YourTargetTable", newName: "NewBase64Column"); } protected override void Down(MigrationBuilder migrationBuilder) { // 回滚逻辑:加载扩展并将Base64转回byte[] migrationBuilder.Sql("LOAD EXTENSION 'sqlean-crypto';"); migrationBuilder.AddColumn<byte[]>( name: "OldByteArrayColumn", table: "YourTargetTable", type: "BLOB", nullable: true); migrationBuilder.Sql(@" UPDATE YourTargetTable SET OldByteArrayColumn = decode(NewBase64Column, 'base64'); "); migrationBuilder.DropColumn( name: "NewBase64Column", table: "YourTargetTable"); migrationBuilder.RenameColumn( name: "OldByteArrayColumn", table: "YourTargetTable", newName: "OldByteArrayColumn"); } }
注意:sqlean-crypto扩展文件需放置在项目输出目录或SQLite可识别的路径下。
问题2:若不行,能否在EF Core迁移中通过SqlCommand编程访问数据库?
完全可以,直接通过EF Core提供的数据库连接服务手动处理数据转换:
public partial class ConvertByteArrayToBase64 : Migration { protected override void Up(MigrationBuilder migrationBuilder) { // 添加临时字符串列 migrationBuilder.AddColumn<string>( name: "Temp_Base64Column", table: "YourTargetTable", type: "TEXT", nullable: true); // 获取数据库连接并处理数据转换 using var connection = migrationBuilder.GetService<IRelationalConnection>(); connection.Open(); // 查询需转换的数据 using var queryCommand = connection.CreateCommand(); queryCommand.CommandText = "SELECT Id, OldByteArrayColumn FROM YourTargetTable;"; using var reader = queryCommand.ExecuteReader(); while (reader.Read()) { var entityId = reader.GetInt32(0); // 根据实际主键类型调整 var byteData = reader.IsDBNull(1) ? null : (byte[])reader[1]; var base64Str = byteData != null ? Convert.ToBase64String(byteData) : null; // 更新临时列 using var updateCommand = connection.CreateCommand(); updateCommand.CommandText = @" UPDATE YourTargetTable SET Temp_Base64Column = @Base64Value WHERE Id = @EntityId; "; updateCommand.Parameters.AddWithValue("@Base64Value", base64Str ?? DBNull.Value); updateCommand.Parameters.AddWithValue("@EntityId", entityId); updateCommand.ExecuteNonQuery(); } // 删除原列并重命名临时列 migrationBuilder.DropColumn( name: "OldByteArrayColumn", table: "YourTargetTable"); migrationBuilder.RenameColumn( name: "Temp_Base64Column", table: "YourTargetTable", newName: "NewBase64Column"); } protected override void Down(MigrationBuilder migrationBuilder) { // 回滚逻辑:反向转换 migrationBuilder.AddColumn<byte[]>( name: "OldByteArrayColumn", table: "YourTargetTable", type: "BLOB", nullable: true); using var connection = migrationBuilder.GetService<IRelationalConnection>(); connection.Open(); using var queryCommand = connection.CreateCommand(); queryCommand.CommandText = "SELECT Id, NewBase64Column FROM YourTargetTable;"; using var reader = queryCommand.ExecuteReader(); while (reader.Read()) { var entityId = reader.GetInt32(0); var base64Str = reader.IsDBNull(1) ? null : reader.GetString(1); var byteData = base64Str != null ? Convert.FromBase64String(base64Str) : null; using var updateCommand = connection.CreateCommand(); updateCommand.CommandText = @" UPDATE YourTargetTable SET OldByteArrayColumn = @ByteValue WHERE Id = @EntityId; "; updateCommand.Parameters.AddWithValue("@ByteValue", byteData ?? DBNull.Value); updateCommand.Parameters.AddWithValue("@EntityId", entityId); updateCommand.ExecuteNonQuery(); } migrationBuilder.DropColumn( name: "NewBase64Column", table: "YourTargetTable"); migrationBuilder.RenameColumn( name: "OldByteArrayColumn", table: "YourTargetTable", newName: "OldByteArrayColumn"); } }
这种方式不依赖SQLite扩展,直接用.NET的Convert类处理转换,兼容性更强。
备选方案:种子数据处理+保留旧列
如果上述两种方式都无法实现,可暂时保留原byte[]列,在应用启动或种子数据阶段完成转换:
- 在实体类中同时保留原byte[]属性和新的Base64字符串属性;
- 在DbContext的
OnModelCreating中配置新属性为持久化列; - 应用启动时查询所有实体,将byte[]转换为Base64字符串后保存;
- 待所有数据转换完成后,再生成迁移删除原byte[]列。
内容的提问来源于stack exchange,提问作者James Hobson
相关产品推荐
相关产品推荐

