MSSQLからMySQLおよびBLToolkitへの移行時の落とし穴

MSSQL 2008があります

MySQLバージョン5.xが必要です



なぜこれが必要なのでしょうか?


.NET開発者にとって、MSSQLをMySQLに交換することは、おそらくメルセデスからもっと簡単なものに移行することと同じでしょう。 彼らが言うように、あなたはすぐに良いことに慣れます。

ただし、これには少なくとも2つの理由があります。



この場合のMSSQLベースの操作は、LINQプロバイダーを介して実行されます。

移行時には、この機会を失いたくありません。そのため、MySQLを使用する場合、 BLToolkitを選択しました



移行します


最も簡単なことは、コードを書き直すことです。 BLToolkitは、MSプロバイダーとは異なり、ライトORMのクラスに属しているため、データベース接続の設計は若干異なりますが、LINQ式は変わりません。



データを転送することは残っていると思いますが、すべて機能しますか?

どんなに。





データを転送する


移行には、MySQL Migration Toolkitを使用しました。

ほとんどのタイプのMSSQLフィールドは問題なくエクスポートされましたが、カットごとに2つのフィールドが拒否されました。



この例では、ソースデータベースで、varcharをnvarcharに、datetime2をdatetimeに変更しました。 最初のものでは、すべてが明確です-フィールドは文字をユニコードで保存し始めましたが、2番目では、開発者がDateTime.Nowをデータベースに入れて抽出するという事実を除いて、100ナノ秒の精度でdatetimeを保存する必要がある理由はありませんでした丸めのために値が等しくない場合があります(この方法で記述された一部の機能テストは落ち、開発者はdatetime2を使用して問題を解決しました)。



LINQマッピング


タイプがtimestampのフィールドは同じタイプのフィールドにエクスポートされますが、その動作はわずかに異なります。

MSSQLの.NETクラスでは、未完成のBinary型で表されます。その結果、 このようにLINQをハックする必要があります

MySQLでは、通常のDateTimeに変換されますが、2つの落とし穴があります

  1. エクスポート後、すべてのタイムスタンプ値を初期化する必要があります(タイムスタンプフィールドのSET NULLなど)。 エクスポート後、BLToolkitが理解できない固体ゼロがあります
  2. DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP



    設定すると、MSSQLで使用されているように、フィールドは自動的に更新されません。 ( MySQLでタイムスタンプを更新する機能について読む)


この段階では、おそらく既にプロジェクトが進行中であり、おそらく何かが機能しているかもしれません。



クラスの一部のフィールドでテーブル内の対応するフィールドが見つからないためにマッピングが機能しない場合、MS LINQでは必要なクラスフィールドを[Column]



属性でマークする必要があり、BLToolkitでは反対のフィールドを属性でマークする必要があるため[MapIgnore]







次の不快な驚きは、BLToolkitがフィールド名をエスケープしないため、フィールド名のテーブルでKeyなどのキーワードを使用しないでください。 LINQ式をSQLクエリに変換すると、構文エラーでクラッシュします。



機能テストの利点について


プログラムがテストでカバーされていない場合、これは非常に悪いです。

データベースで返されたNULLフィールドが.NETのNULL文字列に等しいことを確認した単純な機能テストのおかげで、BLToolkitのデフォルトはstring.Emptyであることがわかりました。

どこかにif(value == null)



が立っているif(value == null)



、それは不愉快な驚きでしょう。



この動作を変更するには、マッピングクラスの属性を登録する必要があります

[NullValue(typeof(string), null)]







私が話せる最後の落とし穴は、Guidタイプのフィールドマッピングです。

MySQLでご存知のように、Guidを保存するための特別なタイプはありません。

このために、char(36)が使用されます。

BLToolkitは、表示時にGuid.Parse



関数を使用します。

たとえば、このようなコードは、GUIDでchar(36)フィールドを表示するために使用する必要があります

 [TableName("Boxes")] public class Box: { [PrimaryKey] public Guid BoxId { get; set; } }
      
      





誰かがGuid以外のものをテーブルに挿入しようとするまで、すべてが素晴らしく見えます。

マッピングクラスでこのフィールドをstring



として定義しても、BLToolkitはGuid.Parseを作成しようとします

たとえば、char(36)guidではない何かからStringMayContainsGuidを読み取ろうとすると、そのようなコードはクラッシュします。

 [TableName("Boxes")] public class Box: { public string StringMayContainsGuid { get; set; } }
      
      





この場合の簡単な解決策は、フィールドタイプをvarcharに変更することです。



そのような問題を見つけるためのツールとして、テーブルからすべてのデータを読み取る簡単な機能テストを作成しました。

 [TestFixture, Category("Functional")] public class DbFunctionalTest { private readonly IMysqlClient mysqlClient = new MysqlClient(); //        [Test] public void ReadAllTables() { var dbMappingClasses = from classType in Assembly.GetExecutingAssembly().GetTypes() where classType.IsClass && classType.GetCustomAttributes(typeof(TableNameAttribute), true).Length > 0 && classType.Namespace == typeof(DbFunctionalTest).Namespace select classType; mysqlClient.PerformRequest(db => { foreach (Type dbMappingClass in dbMappingClasses) { var tableName = ((TableNameAttribute) dbMappingClass.GetCustomAttributes(typeof (TableNameAttribute), true).First()).Name; try { db.SetCommand(CommandType.Text, string.Format("SELECT * FROM {0}", tableName)).ExecuteList(dbMappingClass); }catch(Exception e) { throw new Exception(string.Format("Can not read all records from table {0}", tableName), e); } } }); } }
      
      







結論として


説明した経験が誰かに役立つことを願っています。

しかし実際、私のmyな計画はあなたのコメントからさらに利益を得ることでしたので、歓迎します!



All Articles