EF 6 code first - 从SQL server迁移到MySQL

最近鼓捣一个SaaS项目,后台用的是entity framework 6 code first +SQL server,需要把数据库迁移到MySQL,在这里记录一下遇到的问题。时间比较久了记得不是很准确,供参考。

1, 以前残留的Migration .cs文件(在项目的Migration目录里,包括Configuration.cs)要统统删除重新生成一遍,因为由SQL server connector生成的.cs文件与MySQL connector生成的.cs并不兼容。在nuget manager console里依次使用enable-migrations和add-migration <name>重新生成。

2,创建数据库的时候报异常The ADO.NET provider with invariant name 'MySql.Data.MySqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details."(或者是一个别的异常?记得不是很清楚),一番度娘之后发现要在DbContext的子类上添加以下:

[DbConfigurationType(typeof(MySqlEFConfiguration))]

3,再次创建数据库,又有异常Specified key was too long; max key length is 767 bytes,再一番度娘之后发现要在Configuration.cs文件里增加如下代码:

SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());

4,前进到建表的SQL语句,发现Procedure是MySQL的SQL语句的关键字,需要用`作为escape字符,而在SQL server里用的是[].

 _helper.ExecuteCommand("INSERT INTO DBUpdateProcedure SET `Procedure` = @p0");

5,开始迁移stored procedure。SQL server创建stored procedure是在migration cs文件里手动调用this.CreateStoredProcedure()实现的,但是MySQL创建stored procedure有一个很特殊的语法,需要先将DELIMETER设为别的东西(比如“//”),再结束的时候再设回来。这个东西通过Migration cs文件没法实现,即使通过this.SQL()方法也不行,EF貌似不能正确的理解DELIMETER语法。所以只能在别的地方创建一个MySQLScript对象,通过设定它的Delimeter属性来达到同样的效果。

                sqlConn.Open();
                MySqlScript script_add_sp = new MySqlScript(sqlConn);
                StringBuilder sql = new StringBuilder();
                sql.Append(@"...");
                script_add_sp.Query = sql.ToString();
                script_add_sp.Delimiter = "//";
                script_add_sp.Execute();
                script_add_sp.Delimiter = ";";

 

6,stored procedure迁移好了,在调用的时候又报异常Only MySqlParameter objects may be stored。原因是有如下代码:

            var tenantIdParam = new SqlParameter("TenantId", tenantId);
            var officeIdParam = new SqlParameter("OfficeId", officeId);
            var searchTextParam = new SqlParameter("SearchText", searchText ?? "");
            var pageIndexParam = new SqlParameter("PageIndex", pageIndex);
            var pageSizeParam = new SqlParameter("PageSize", pageSize);
            var orderFieldParam = new SqlParameter("OrderField", orderField);
            var patients = DataContext.Database.SqlQuery<PatientPageResult>(
                "csp_SearchPatient(@TenantId, @OfficeId, @SearchText, @PageIndex, @PageSize, @OrderField)",
                tenantIdParam, officeIdParam, searchTextParam, pageIndexParam, pageSizeParam, orderFieldParam).ToList();

这里我们必须构建MySqlParameter对象,而不是SQLParameter对象。并且MySql调用stored procedure要显示的加CALL关键字,所以改正之后的代码为:

            var tenantIdParam = new MySqlParameter("TenantId", tenantId);
            var officeIdParam = new MySqlParameter("OfficeId", officeId);
            var searchTextParam = new MySqlParameter("SearchText", searchText ?? "");
            var pageIndexParam = new MySqlParameter("PageIndex", pageIndex);
            var pageSizeParam = new MySqlParameter("PageSize", pageSize);
            var orderFieldParam = new MySqlParameter("OrderField", orderField);
            var patients = DataContext.Database.SqlQuery<PatientPageResult>(
                "CALL csp_SearchPatient(@TenantId, @OfficeId, @SearchText, @PageIndex, @PageSize, @OrderField)",
                tenantIdParam, officeIdParam, searchTextParam, pageIndexParam, pageSizeParam, orderFieldParam).ToList();

7,在Linq语句区数据转化为对象的时候,报异常:System.FormatException occurred, Message=String was not recognized as a valid Boolean.貌似因为实体类上有boolean属性,MySQL connector生成的默认migration cs文件把这个属性存在类型为tinyint的列里,但读出来的时候又不知道怎么把0/1转换为true/false,所以抛了这个异常。这个问题作为MySQL connector的一个bug报在了这里。就在快要绝望的时候,发现了stack overflow上牛人提的解决方案(原文),方法就是在modelbuilder里指定将boolean类型用bit列存储,没太明白,先记录下来。

 modelBuilder.Properties()
             .Where(x => x.PropertyType == typeof(bool))
             .Configure(x => x.HasColumnType("bit"));

 

这只是个开始,后面的坑再开新的随笔记录。

 

posted @ 2015-08-21 15:02  KennethYip  阅读(825)  评论(1编辑  收藏  举报