通过EF Core将Sql server数据表移植到MySql

在还是.net frameworks时,我们用ado.net 操作数据库,后来,微软为了迎合跨平台趋势,推出.net core,虽然也.net core也有ado.net,但微软更推荐ef core,无论是国内还是国内涉及.net core的C#技术书(vb.net也一样)都着重讲解了ef core,ef core不仅跨平台,还跨数据库,可以避免程序员适应不同数据库之间sql语言差异带来的开销,除了特别复杂的sql查询,ef core就体现出来了,并且它还可实现不同数据库之间迁移,下面就演示一下sql server 2019迁移到MySQL8,体现一下ef core的强大。

首先,在sql server 2019上新建一个数据库,此处取名为EFCoreDemo,在数据库中添加两张简单的表,ClassRoom和Student,ClassRoom为主表,Student为子表,ClassRoom与Student是一对多的关系(级联更新,级联删除),表字段如截图1和2。并向两表中任意填写一些数据。


创建表后,我们用visaul studio 2022新建一个.net core的类库(注意不是.net frameworks),删除自动生成的Class类,在工具栏的管理解决方案的nuge程序包,下载安装以下三个包,Microsoft.EntityFrameworkCore.Design、Microsoft.EntityFrameworkCore.SqlServer和Microsoft.EntityFrameworkCore.Tools,注意为了后期迁移到mysql,这里建议版本为8.0.0,(并非最新版本)。打开程序包管理器控制台(PM)窗口,执行

Scaffold-DbContext "Data Source=localhost;Initial Catalog=数据库名;User ID=用户名;Password=密码;TrustServerCertificate=True" Microsoft.EntityFrameworkCore.SqlServer - OutputDir Models - Context 数据库名DbContext - NoOnConfiguring - UseDatabaseNames - DataAnnotations - Verbose

成功后能看到一个Model文件夹,里在有与表对应的实体类和上下文类。接着可以手工新建Services的文件夹,用于实现数据库基本的CRUD操作。

csharp 复制代码
using EFCoreMigration;
using EFCoreMigration.Models;
using Microsoft.EntityFrameworkCore;
using System;

namespace EFCoreMigration.Services
{
    public class ClassRoomService
    {
        private readonly EFCoreDbContext _dbContext;

        // 通过依赖注入获取数据库上下文
        public ClassRoomService(EFCoreDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        
        public async Task<ClassRoom> AddClassRoomAsync(ClassRoom classRoom)
        {
            try
            {
                _dbContext.ClassRooms.Add(classRoom);
                await _dbContext.SaveChangesAsync();
                return classRoom;
            }
            catch (Exception ex)
            {
                throw new Exception( ex.Message, ex);
            }
        }

       
        public async Task<List<ClassRoom>> GetAllClassRoomsAsync()
        {
            try
            {
                return await _dbContext.ClassRooms.ToListAsync();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        
        public async Task<ClassRoom?> GetClassRoomByIdAsync(int id)
        {
            try
            {
                return await _dbContext.ClassRooms.FindAsync(id);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        
        public async Task<bool> UpdateClassRoomAsync(int id, ClassRoom updatedClassRoom)
        {
            try
            {
                var existingClassRoom = await _dbContext.ClassRooms.FindAsync(id);
                if (existingClassRoom == null)
                {
                    return false;
                }

                // 更新属性
                existingClassRoom.ClassRoomName = updatedClassRoom.ClassRoomName;

                _dbContext.ClassRooms.Update(existingClassRoom);
                await _dbContext.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

      
        public async Task<bool> DeleteClassRoomAsync(int id)
        {
            try
            {
                var classRoom = await _dbContext.ClassRooms.FindAsync(id);
                if (classRoom == null)
                {
                    return false;
                }

                _dbContext.ClassRooms.Remove(classRoom);
                await _dbContext.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }
    }
}
csharp 复制代码
using EFCoreMigration.Models;
using Microsoft.EntityFrameworkCore;
using System;

namespace EFCoreMigration.Services
{
    public class StudentService
    {
        private readonly EFCoreDbContext _dbContext;

        public StudentService(EFCoreDbContext dbContext)
        {
            _dbContext = dbContext;
        }

       
        public async Task<Student> AddStudentAsync(Student student)
        {
            try
            {
                _dbContext.Students.Add(student);
                await _dbContext.SaveChangesAsync();
                return student;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        
        public async Task<List<Student>> GetAllStudentsAsync()
        {
            try
            {
                // 可根据需要Include关联的ClassRoom信息
                return await _dbContext.Students
                    .Include(s => s.ClassRoom) // 关联查询教室信息
                    .ToListAsync();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public async Task<Student?> GetStudentByIdAsync(int id)
        {
            try
            {
                return await _dbContext.Students
                    .Include(s => s.ClassRoom)
                    .FirstOrDefaultAsync(s => s.Id == id);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public async Task<bool> UpdateStudentAsync(int id, Student updatedStudent)
        {
            try
            {
                var existingStudent = await _dbContext.Students.FindAsync(id);
                if (existingStudent == null)
                {
                    return false;
                }

               
                existingStudent.StudentName = updatedStudent.StudentName;
                existingStudent.ClassRoomId = updatedStudent.ClassRoomId;

                _dbContext.Students.Update(existingStudent);
                await _dbContext.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

       
        public async Task<bool> DeleteStudentAsync(int id)
        {
            try
            {
                var student = await _dbContext.Students.FindAsync(id);
                if (student == null)
                {
                    return false;
                }

                _dbContext.Students.Remove(student);
                await _dbContext.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }
    }
}

接着在解决方案资源管理器新建一个控制台项目,此处命名为InkoveDemo。用于测试数据库代码能否正常调用,简单起见,这里只完成ClassRoom添加与列出所有数据,代码如下(注意,此处为测试,生产环境下数据库配置不建议直接硬编码)

csharp 复制代码
using EFCoreMigration;
using EFCoreMigration.Services;
using EFCoreMigration.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace InkoveDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
           
            var optionsBuilder = new DbContextOptionsBuilder<EFCoreDbContext>();
            string ConnSqlserverStr = "Data Source=localhost;Initial Catalog=EFCoreDemo;User ID=sa;Password=123;TrustServerCertificate=True";
            optionsBuilder.UseSqlServer(ConnSqlserverStr);

            // 创建数据库上下文实例
            using var dbContext = new EFCoreDbContext(optionsBuilder.Options);

            // 确保数据库和表已创建
            await dbContext.Database.EnsureCreatedAsync();

            // 实例化班级服务(注入数据库上下文)
            var classRoomService = new ClassRoomService(dbContext);

            try
            {
                // 1. 硬编码添加班级数据
                ClassRoom classroom =
                new ClassRoom { ClassRoomName = "文秘班" };
              await classRoomService.AddClassRoomAsync(classroom);

                

                // 2. 列出所有班级数据
                var allClassRooms = await classRoomService.GetAllClassRoomsAsync();
                Console.WriteLine("\n===== 所有班级列表 =====");
                if (allClassRooms.Any())
                {
                    foreach (var room in allClassRooms)
                    {
                        Console.WriteLine($"ID: {room.Id}");
                        Console.WriteLine($"班级名称: {room.ClassRoomName}");
                        
                        Console.WriteLine("---------------------");
                    }
                }
                else
                {
                    Console.WriteLine("暂无班级数据");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"操作失败:{ex.Message}");
            }

            Console.WriteLine("\n按任意键退出...");
            Console.ReadKey();
        }
    }
}

需要在依赖项引入自已刚建的类库EFCoreMigration,并将启动项目切换为InkoveDemo。运行后可看到如图3效果。

下面是迁移数据库了,再下载安装一个Pomelo.EntityFrameworkCore.MySql的nuge程序包,版本号8.0.0,接着新一个Factory类,代码如下,注意将mysql8数据库配置和mysql8版本与替换成你自己的。

csharp 复制代码
using EFCoreMigration;
using EFCoreMigration.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Pomelo.EntityFrameworkCore.MySql;


using Microsoft.Extensions.Configuration;
using System.IO;

namespace EFCoreMigration;

// 仅用于迁移,迁移完成后直接删除此类!
public class EFCoreDemoDbContextDesignTimeFactory 
    : IDesignTimeDbContextFactory<EFCoreDbContext>
{
    public EFCoreDbContext CreateDbContext(string[] args)
    {

        string connectionString = "Server=localhost;Port=3306;Database=efcoredemo;Uid=root;Pwd=123456;Charset=utf8mb4;";

        // 配置 MySQL 选项(版本是你的MySql一致)
        var optionsBuilder = new DbContextOptionsBuilder<EFCoreDbContext>();
        optionsBuilder.UseMySql(
            connectionString,
            new MySqlServerVersion(new Version(8, 0, 43))
        );

        // 返回 DbContext 实例,供迁移工具使用
        return new EFCoreDbContext(optionsBuilder.Options);
    }
}

接着PM窗口执行

Pomelo.EntityFrameworkCore.MySql,成功后会有一个Migrations目录,不用管里面的代码,继续在PM窗口执行Update-Database,重新连接或刷新mysql数据库,见到两张与sql server相同的表,至此表结构已完成迁移。

接下来,便是迁移表中的数据了,建立一个DataMigrationHelper类代码

csharp 复制代码
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using EFCoreMigration;
using EFCoreMigration.Models;
namespace EFCoreMigration
{
    public class DataMigrationHelper
    {
        // SQL Server 连接字符串(迁移源)
        private readonly string _sqlServerConnStr;
        // MySQL 连接字符串(迁移目标)
        private readonly string _mysqlConnStr;

        /// <summary>
        /// 初始化迁移工具
        /// </summary>
        public DataMigrationHelper(string sqlServerConnStr, string mysqlConnStr)
        {
            _sqlServerConnStr = sqlServerConnStr;
            _mysqlConnStr = mysqlConnStr;
        }

        /// <summary>
        /// 执行全量数据迁移(按表顺序迁移,处理外键依赖)
        /// </summary>
        public async Task MigrateAllDataAsync()
        {
            // 1. 连接 SQL Server(读数据)
            var sqlServerOptions = new DbContextOptionsBuilder<EFCoreDbContext>()
                .UseSqlServer(_sqlServerConnStr)
                .Options;

            // 2. 连接 MySQL(写数据)
            var mysqlOptions = new DbContextOptionsBuilder<EFCoreDbContext>()
                .UseMySql(_mysqlConnStr, new MySqlServerVersion(new Version(8, 0, 43)))
                .Options;

            try
            {
                Console.WriteLine("=== 开始数据迁移 ===");

                // 🔴 关键:按「无外键依赖 → 有外键依赖」的顺序迁移(避免插入失败)
                // 例:先迁移 AdminTab、News(无外键),再迁移 Pic(依赖 News)
                await MigrateTableAsync<ClassRoom>(sqlServerOptions, mysqlOptions);
               
                await MigrateTableAsync<Student>(sqlServerOptions, mysqlOptions); // 最后迁移有外键的表

                Console.WriteLine("=== 数据迁移完成!===");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"迁移失败:{ex.Message}");
                if (ex.InnerException != null)
                    Console.WriteLine($"   内部错误:{ex.InnerException.Message}");
            }
        }

        /// <summary>
        /// 单个表的数据迁移(通用方法,复用所有实体)
        /// </summary>
        private async Task MigrateTableAsync<TEntity>(DbContextOptions<EFCoreDbContext> sqlServerOptions,
                                                      DbContextOptions<EFCoreDbContext> mysqlOptions)
            where TEntity : class
        {
            Console.WriteLine($"正在迁移 {typeof(TEntity).Name} 表...");

            // 从 SQL Server 读取所有数据
            using (var sqlServerDb = new EFCoreDbContext(sqlServerOptions))
            {
                // 读取数据(AsNoTracking 提高性能,不跟踪实体状态)
                List<TEntity> dataList = await sqlServerDb.Set<TEntity>()
                    .AsNoTracking()
                    .ToListAsync();

                if (dataList.Count == 0)
                {
                    Console.WriteLine($"   {typeof(TEntity).Name} 表无数据,跳过...");
                    return;
                }

                // 批量插入 MySQL
                using (var mysqlDb = new EFCoreDbContext(mysqlOptions))
                {
                    // 先清空 MySQL 目标表(避免重复数据,可选)
                    await mysqlDb.Set<TEntity>().ExecuteDeleteAsync();

                    // 批量添加(EF Core 8 支持 AddRange 高效插入)
                    mysqlDb.Set<TEntity>().AddRange(dataList);

                    // 提交事务(保存数据)
                    await mysqlDb.SaveChangesAsync();

                    Console.WriteLine($"   成功迁移 {dataList.Count} 条数据到 {typeof(TEntity).Name} 表");
                }
            }
        }
    }
}

快去看看mysql数据库,数据也成功迁移,只要对InkoveDemo略作修改,便可对mysql数据库进行操作了。

...

string ConnSqlserverStr = "Data Source=localhost;Initial Catalog=EFCoreDemo;User ID=sa;Password=123;TrustServerCertificate=True";

optionsBuilder.UseSqlServer(ConnSqlserverStr);

//如果使用mysql使用以下语句,别忘记将配置替换成你自己的

// string Connmysqlstr = "Server=localhost;Port=3306;Database=efcoredemo;Uid=root;Pwd=123456;Charset=utf8mb4;";

// optionsBuilder.UseMySql(

// Connmysqlstr,

// new MySqlServerVersion(new Version(8, 0, 36))

//);

...

收工。

相关推荐
0***86331 小时前
SQL Server2019安装步骤+使用+解决部分报错+卸载(超详细 附下载链接)
javascript·数据库·ui
聪聪那年221 小时前
Oracle 11g windows 10安装与卸载
数据库·oracle
故渊ZY1 小时前
从入门到精通:MySQL 核心技术与业务落地实践
mysql
计算机毕设匠心工作室1 小时前
【python大数据毕设实战】全面皮肤病症状数据可视化分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
后端·python·mysql
前端之虎陈随易1 小时前
MoonBit内置数据结构详解
数据结构·数据库·redis
qq_12498707532 小时前
基于SpringBoot+vue的小黄蜂外卖平台(源码+论文+部署+安装)
java·开发语言·vue.js·spring boot·后端·mysql·毕业设计
小二·2 小时前
Spring框架入门:TX 声明式事务详解
java·数据库·spring
万邦科技Lafite2 小时前
一键获取淘宝店铺所有商品信息,实时监控商品数据
开发语言·数据库·python·api·开放api·电商开放平台·淘宝开放平台
SUPER52662 小时前
运维hbase服务重启,导致应用查询异常 hbase:meta
运维·数据库·hbase